diff --git a/vuu-ui/packages/vuu-data/src/connection-manager.ts b/vuu-ui/packages/vuu-data/src/connection-manager.ts index dc81e4dbc..ce9d3b15b 100644 --- a/vuu-ui/packages/vuu-data/src/connection-manager.ts +++ b/vuu-ui/packages/vuu-data/src/connection-manager.ts @@ -75,6 +75,8 @@ const pendingRequests = new Map(); type WorkerOptions = { protocol: WebSocketProtocol; + retryLimitDisconnect?: number; + retryLimitStartup?: number; url: string; token?: string; username: string | undefined; @@ -89,6 +91,8 @@ type WorkerOptions = { const getWorker = async ({ handleConnectionStatusChange, protocol, + retryLimitDisconnect, + retryLimitStartup, token = "", username, url, @@ -120,6 +124,8 @@ const getWorker = async ({ window.clearTimeout(timer); worker.postMessage({ protocol, + retryLimitDisconnect, + retryLimitStartup, token, type: "connect", url, @@ -274,6 +280,10 @@ export type ConnectOptions = { authToken?: string; username?: string; protocol?: WebSocketProtocol; + /** Max number of reconnect attempts in the event of unsuccessful websocket connection at startup */ + retryLimitStartup?: number; + /** Max number of reconnect attempts in the event of a disconnected websocket connection */ + retryLimitDisconnect?: number; }; class _ConnectionManager extends EventEmitter { @@ -284,6 +294,8 @@ class _ConnectionManager extends EventEmitter { authToken, username, protocol, + retryLimitDisconnect, + retryLimitStartup, }: ConnectOptions): Promise { // By passing handleMessageFromWorker here, we can get connection status //messages while we wait for worker to resolve. @@ -292,6 +304,8 @@ class _ConnectionManager extends EventEmitter { url, token: authToken, username, + retryLimitDisconnect, + retryLimitStartup, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore handleConnectionStatusChange: handleMessageFromWorker, @@ -321,6 +335,8 @@ export const connectToServer = async ({ protocol = undefined, authToken, username, + retryLimitDisconnect, + retryLimitStartup, }: ConnectOptions) => { try { const serverAPI = await ConnectionManager.connect({ @@ -328,6 +344,8 @@ export const connectToServer = async ({ url, authToken, username, + retryLimitDisconnect, + retryLimitStartup, }); resolveServer(serverAPI); } catch (err: unknown) { diff --git a/vuu-ui/packages/vuu-data/src/vuuUIMessageTypes.ts b/vuu-ui/packages/vuu-data/src/vuuUIMessageTypes.ts index 32f6198a2..c416bc5b6 100644 --- a/vuu-ui/packages/vuu-data/src/vuuUIMessageTypes.ts +++ b/vuu-ui/packages/vuu-data/src/vuuUIMessageTypes.ts @@ -167,6 +167,8 @@ export interface VuuUIMessageOutConnect { token: string; url: string; username?: string; + retryLimitDisconnect?: number; + retryLimitStartup?: number; } export interface VuuUIMessageOutSubscribe extends ServerProxySubscribeMessage { diff --git a/vuu-ui/packages/vuu-data/src/websocket-connection.ts b/vuu-ui/packages/vuu-data/src/websocket-connection.ts index 2526a178c..e0a8a5976 100644 --- a/vuu-ui/packages/vuu-data/src/websocket-connection.ts +++ b/vuu-ui/packages/vuu-data/src/websocket-connection.ts @@ -27,9 +27,21 @@ const WS = "ws"; // to stop semGrep complaining const isWebsocketUrl = (url: string) => url.startsWith(WS + "://") || url.startsWith(WS + "s://"); -const connectionAttempts: { - [key: string]: { attemptsRemaining: number; status: ConnectionStatus }; -} = {}; +type ConnectionTracking = { + [key: string]: { + connect: { + allowed: number; + remaining: number; + }; + reconnect: { + allowed: number; + remaining: number; + }; + status: ConnectionStatus; + }; +}; + +const connectionAttemptStatus: ConnectionTracking = {}; const setWebsocket = Symbol("setWebsocket"); const connectionCallback = Symbol("connectionCallback"); @@ -37,8 +49,21 @@ const connectionCallback = Symbol("connectionCallback"); export async function connect( connectionString: string, protocol: WebSocketProtocol, - callback: ConnectionCallback + callback: ConnectionCallback, + retryLimitDisconnect = 10, + retryLimitStartup = 5 ): Promise { + connectionAttemptStatus[connectionString] = { + status: "connecting", + connect: { + allowed: retryLimitStartup, + remaining: retryLimitStartup, + }, + reconnect: { + allowed: retryLimitDisconnect, + remaining: retryLimitDisconnect, + }, + }; return makeConnection(connectionString, protocol, callback); } @@ -58,12 +83,14 @@ async function makeConnection( callback: ConnectionCallback, connection?: WebsocketConnection ): Promise { - const connectionStatus = - connectionAttempts[url] || - (connectionAttempts[url] = { - attemptsRemaining: 5, - status: "disconnected", - }); + const { + status: currentStatus, + connect: connectStatus, + reconnect: reconnectStatus, + } = connectionAttemptStatus[url]; + + const trackedStatus = + currentStatus === "connecting" ? connectStatus : reconnectStatus; try { callback({ type: "connection-status", status: "connecting" }); @@ -89,10 +116,12 @@ async function makeConnection( callback({ type: "connection-status", status }); websocketConnection.status = status; + // reset the retry attempts for subsequent disconnections + trackedStatus.remaining = trackedStatus.allowed; + return websocketConnection as Connection; - } catch (evt) { - console.log({ evt }); - const retry = --connectionStatus.attemptsRemaining > 0; + } catch (err) { + const retry = --trackedStatus.remaining > 0; callback({ type: "connection-status", status: "disconnected", @@ -100,7 +129,7 @@ async function makeConnection( retry, }); if (retry) { - return makeConnectionIn(url, protocol, callback, connection, 10000); + return makeConnectionIn(url, protocol, callback, connection, 2000); } else { throw Error("Failed to establish connection"); } @@ -204,7 +233,7 @@ export class WebsocketConnection implements Connection { messagesLength: this.messagesCount, }); this.messagesCount = 0; - }, 1000); + }, 2000); ws.onerror = () => { error(`⚡ connection error`); diff --git a/vuu-ui/packages/vuu-data/src/worker.ts b/vuu-ui/packages/vuu-data/src/worker.ts index 3481d0d8a..35952e442 100644 --- a/vuu-ui/packages/vuu-data/src/worker.ts +++ b/vuu-ui/packages/vuu-data/src/worker.ts @@ -25,7 +25,9 @@ async function connectToServer( protocol: WebSocketProtocol, token: string, username: string | undefined, - onConnectionStatusChange: (msg: ConnectionStatusMessage) => void + onConnectionStatusChange: (msg: ConnectionStatusMessage) => void, + retryLimitDisconnect?: number, + retryLimitStartup?: number ) { const connection = await connectWebsocket( url, @@ -44,7 +46,9 @@ async function connectToServer( } else { server.handleMessageFromServer(msg); } - } + }, + retryLimitDisconnect, + retryLimitStartup ); server = new ServerProxy(connection, (msg) => sendMessageToClient(msg)); @@ -72,7 +76,9 @@ const handleMessageFromClient = async ({ message.protocol, message.token, message.username, - postMessage + postMessage, + message.retryLimitDisconnect, + message.retryLimitStartup ); postMessage({ type: "connected" }); break; diff --git a/vuu-ui/packages/vuu-data/test/websocket-connection.test.ts b/vuu-ui/packages/vuu-data/test/websocket-connection.test.ts new file mode 100644 index 000000000..eae0185f7 --- /dev/null +++ b/vuu-ui/packages/vuu-data/test/websocket-connection.test.ts @@ -0,0 +1,109 @@ +import "./global-mocks"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { + connect as connectWebsocket, + ConnectionMessage, +} from "../src/websocket-connection"; + +describe("websocket-connection", () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + afterEach(() => { + vi.useRealTimers(); + }); + + it("tries to connect by default a maximum of 5 times before throwing Exception", async () => { + const statusMessages: ConnectionMessage[] = []; + const callback = async (message: ConnectionMessage) => { + statusMessages.push(message); + await vi.advanceTimersByTimeAsync(2000); + }; + + try { + await connectWebsocket("tst/url", "", callback); + } catch (e) { + expect(e.message).toEqual("Failed to establish connection"); + } + + expect(statusMessages.length).toEqual(10); + expect(statusMessages).toEqual([ + { type: "connection-status", status: "connecting" }, + { + type: "connection-status", + status: "disconnected", + reason: "failed to connect", + retry: true, + }, + { type: "connection-status", status: "connecting" }, + { + type: "connection-status", + status: "disconnected", + reason: "failed to connect", + retry: true, + }, + { type: "connection-status", status: "connecting" }, + { + type: "connection-status", + status: "disconnected", + reason: "failed to connect", + retry: true, + }, + { type: "connection-status", status: "connecting" }, + { + type: "connection-status", + status: "disconnected", + reason: "failed to connect", + retry: true, + }, + { type: "connection-status", status: "connecting" }, + { + type: "connection-status", + status: "disconnected", + reason: "failed to connect", + retry: false, + }, + ]); + }); + + it("fires connection-status messages when connecting/connected", async () => { + class MockWebSocket { + private openHandler: any; + private errorHandler: any; + constructor() { + setTimeout(() => { + this?.openHandler(); + }, 0); + } + set onopen(callback) { + this.openHandler = callback; + } + set onerror(callback) { + this.errorHandler = callback; + } + } + vi.stubGlobal("WebSocket", MockWebSocket); + + const statusMessages: ConnectionMessage[] = []; + const callback = async (message: ConnectionMessage) => { + statusMessages.push(message); + await vi.advanceTimersByTimeAsync(10); + }; + + try { + await connectWebsocket("tst/url", "", callback); + } catch (e) { + expect(e.message).toEqual("Failed to establish connection"); + } + + expect(statusMessages.length).toEqual(2); + expect(statusMessages).toEqual([ + { type: "connection-status", status: "connecting" }, + { + type: "connection-status", + status: "connection-open-awaiting-session", + }, + ]); + }); +}); diff --git a/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx b/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx index 0fd161802..ac9b1cc26 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx +++ b/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx @@ -193,7 +193,7 @@ export const ComboBox = forwardRef(function Combobox< listHandlers={listHandlers} onSelectionChange={onSelectionChange} ref={listRef} - selected={collectionItemsToItem(selected)} + selected={collectionItemsToItem(selected as any)} selectionStrategy={selectionStrategy} /> diff --git a/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts b/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts index f9b95910a..5b425f6ea 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts @@ -376,7 +376,11 @@ export const useCombobox = < if (selectedCollectionItem) { if (Array.isArray(selectedCollectionItem)) { // TODO multi select + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore } else if (selectedCollectionItem !== selected) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore setSelectedRef.current?.(selectedCollectionItem); onSelectionChange?.( evt, diff --git a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/useCollectionItems.ts b/vuu-ui/packages/vuu-ui-controls/src/common-hooks/useCollectionItems.ts index c261734a1..b272d435b 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/useCollectionItems.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/common-hooks/useCollectionItems.ts @@ -29,7 +29,6 @@ const defaultCollectionOptions = {}; export const useCollectionItems = ({ children, id: idRoot, - label = "", options = defaultCollectionOptions, // revealSelected = false, source, @@ -49,17 +48,14 @@ export const useCollectionItems = ({ itemToString = defaultItemToString, } = options; - const isExpanded = useCallback( - (path: string) => { - // We can't do this here because itemToId won't work until we complete this phase - // if (Array.isArray(revealSelected)) { - // const selectedIds = revealSelected.map(itemToId); - // return selectedIds.some((id) => isParentPath(path, id)); - // } - return options.defaultExpanded || false; - }, - [options.defaultExpanded] - ); + const isExpanded = useCallback(() => { + // We can't do this here because itemToId won't work until we complete this phase + // if (Array.isArray(revealSelected)) { + // const selectedIds = revealSelected.map(itemToId); + // return selectedIds.some((id) => isParentPath(path, id)); + // } + return options.defaultExpanded || false; + }, [options.defaultExpanded]); const addMetadataToItems = useCallback( ( @@ -87,7 +83,7 @@ export const useCollectionItems = ({ const expanded = nonCollapsible ? undefined - : item.expanded ?? isExpanded(id); + : item.expanded ?? isExpanded(); //TODO dev time check - if id is provided by user, make sure // hierarchical pattern is consistent const normalisedItem: CollectionItem = { @@ -254,6 +250,7 @@ export const useCollectionItems = ({ // TODO what about Tree structures, we need to search flattened source const collectionItem = flattenedDataRef.current.find((i) => // const collectionItem = collectionItemsRef.current.find((i) => + // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore isValidElement(i.value) ? i.label === item : i.value === item ); @@ -413,6 +410,8 @@ export const useCollectionItems = ({ [collectVisibleItems] ); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore return ( inheritedCollectionHook || { collapseGroupItem, diff --git a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/DragDropState.ts b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/DragDropState.ts index bd605ec06..2f5351e7f 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/DragDropState.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/DragDropState.ts @@ -16,7 +16,7 @@ export class DragDropState { } /** Used to capture a ref to the Draggable JSX.Element */ - setDraggable = (el: HTMLElement) => { + setDraggable = (el: HTMLElement | null) => { this.draggableElement = el; }; diff --git a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropIndicator.tsx b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropIndicator.tsx index 478fe90d6..f86509b8c 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropIndicator.tsx +++ b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropIndicator.tsx @@ -304,7 +304,7 @@ export const useDragDropIndicator = ({ if (overflowMenuShowingRef.current) { onDrop(fromIndex, -1, { fromIndex, - roIndex: -1, + toIndex: -1, }); } else { if (fromIndex < originalDropTargetIndex) { diff --git a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNaturalMovementNext.tsx b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNaturalMovementNext.tsx index 5c921c713..8383d1200 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNaturalMovementNext.tsx +++ b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNaturalMovementNext.tsx @@ -156,6 +156,8 @@ export const useDragDropNaturalMovement = ({ console.log(`nextDropTarget ${dropTarget.element.textContent}`); // need to compute the correct position of this + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore const draggedItem = (draggedItemRef.current = { end, mid, @@ -167,6 +169,8 @@ export const useDragDropNaturalMovement = ({ const indexOfDropTarget = dropTargets.indexOf(dropTarget); console.log({ indexOfDropTarget }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + //@ts-ignore dropTargets.splice(indexOfDropTarget, 0, draggedItem); for (let i = index + 1; i < dropTargets.length; i++) { const target = dropTargets[i]; diff --git a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNext.tsx b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNext.tsx index 9ec50928f..b91267d04 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNext.tsx +++ b/vuu-ui/packages/vuu-ui-controls/src/drag-drop/useDragDropNext.tsx @@ -267,7 +267,9 @@ export const useDragDropNext: DragDropHook = ({ onDrop?.(fromIndex, toIndex, options); } dropIndexRef.current = toIndex; - onEndOfDragOperation?.(id); + if (id) { + onEndOfDragOperation?.(id); + } dragDropStateRef.current = null; }, [id, onDrop, onEndOfDragOperation] @@ -307,7 +309,7 @@ export const useDragDropNext: DragDropHook = ({ if (onDragOut?.(id as string, dragDropStateRef.current)) { // TODO create a cleanup function removeDragHandlers(); - releaseDrag(); + releaseDrag?.(); dragDropStateRef.current = null; } // remove the drag boundaries @@ -315,7 +317,7 @@ export const useDragDropNext: DragDropHook = ({ return true; } }, - [id, isDragSource, onDragOut, orientation, removeDragHandlers] + [id, isDragSource, onDragOut, orientation, releaseDrag, removeDragHandlers] ); const dragMouseMoveHandler = useCallback( diff --git a/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdownBase.ts b/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdownBase.ts index df8ae5916..dcf9b0a80 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdownBase.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/dropdown/useDropdownBase.ts @@ -130,7 +130,7 @@ export const useDropdownBase = ({ width: popup.width, }; - const popupComponentRef = useForkRef(popperCallbackRef, popupComponent.ref); + const popupComponentRef = useForkRef(popperCallbackRef, popperRef); return { componentProps: dropdownComponentProps, diff --git a/vuu-ui/packages/vuu-ui-controls/src/list/VirtualizedList.tsx b/vuu-ui/packages/vuu-ui-controls/src/list/VirtualizedList.tsx index f8cdcb137..d7bbd7f50 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/list/VirtualizedList.tsx +++ b/vuu-ui/packages/vuu-ui-controls/src/list/VirtualizedList.tsx @@ -2,12 +2,11 @@ import { makePrefixer, useForkRef, useIdMemo } from "@salt-ds/core"; import { clsx } from "clsx"; import { ForwardedRef, forwardRef, memo, ReactElement, useRef } from "react"; import { - CollectionIndexer, isSelected, useCollectionItems, useImperativeScrollingAPI, } from "./common-hooks"; -import { SelectionStrategy } from "../common-hooks"; +import { CollectionIndexer, SelectionStrategy } from "../common-hooks"; import { useListHeight } from "./useListHeight"; import { ListItem as DefaultListItem, ListItemProxy } from "./ListItem"; @@ -53,6 +52,8 @@ export const VirtualizedList = forwardRef(function List< maxWidth, minHeight, minWidth, + onDragStart, + onDrop, onSelect, onSelectionChange, onViewportScroll, @@ -89,7 +90,11 @@ export const VirtualizedList = forwardRef(function List< }, }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const { contentHeight, listItemHeight, listHeight } = useListHeight({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore borderless, displayedItemCount, height, @@ -112,6 +117,8 @@ export const VirtualizedList = forwardRef(function List< collectionHook, containerRef: rootRef, defaultHighlightedIndex: defaultHighlightedIdx, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore defaultSelected: collectionHook.itemToCollectionItem< Selection, typeof defaultSelected @@ -124,6 +131,8 @@ export const VirtualizedList = forwardRef(function List< onSelectionChange, onHighlight, restoreLastFocus, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore selected: collectionHook.itemToCollectionItem< Selection, typeof defaultSelected diff --git a/vuu-ui/packages/vuu-ui-controls/src/list/common-hooks/useKeyboardNavigation.ts b/vuu-ui/packages/vuu-ui-controls/src/list/common-hooks/useKeyboardNavigation.ts index 10d50ae14..1b40a73b5 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/list/common-hooks/useKeyboardNavigation.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/list/common-hooks/useKeyboardNavigation.ts @@ -205,6 +205,8 @@ export const useKeyboardNavigation = < } else { const indexOfSelectedItem = getIndexOfSelectedItem( indexPositions, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore selected ); // The start index is generally the highlightedIdx (passed in as idx). @@ -260,6 +262,8 @@ export const useKeyboardNavigation = < } else { const selectedItemIdx = getIndexOfSelectedItem( indexPositions, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore selected ); if (selectedItemIdx !== -1) { @@ -271,6 +275,8 @@ export const useKeyboardNavigation = < } else if (hasSelection(selected)) { const selectedItemIdx = getIndexOfSelectedItem( indexPositions, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore selected ); setHighlightedIndex(selectedItemIdx); diff --git a/vuu-ui/packages/vuu-ui-controls/src/list/listTypes.ts b/vuu-ui/packages/vuu-ui-controls/src/list/listTypes.ts index 989802717..bc0e94e8f 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/list/listTypes.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/list/listTypes.ts @@ -71,7 +71,10 @@ export interface ListProps< Item = string, Selection extends SelectionStrategy = "default" > extends SelectionProps, - Omit, "onSelect" | "defaultValue"> { + Omit< + HTMLAttributes, + "onDragStart" | "onDrop" | "onSelect" | "defaultValue" + > { /** * The component used to render a ListItem instead of the default. This must itself render a ListItem, * must implement props that extend ListItemProps and must forward ListItem props to the ListItem. diff --git a/vuu-ui/packages/vuu-ui-controls/src/list/useList.ts b/vuu-ui/packages/vuu-ui-controls/src/list/useList.ts index 9c02f82fa..c712869bf 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/list/useList.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/list/useList.ts @@ -192,8 +192,12 @@ export const useList = ({ const reorderSelectedIndices = useCallback( (selected: string | string[], fromIndex: number, toIndex: number) => { if (Array.isArray(selected)) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore return selected.map((item) => adjustIndex(item, fromIndex, toIndex)); } else { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore return adjustIndex(selected, fromIndex, toIndex); } }, diff --git a/vuu-ui/packages/vuu-ui-controls/src/list/useVirtualization.ts b/vuu-ui/packages/vuu-ui-controls/src/list/useVirtualization.ts index 2d66cfcc1..28ca229f0 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/list/useVirtualization.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/list/useVirtualization.ts @@ -1,6 +1,6 @@ import { useMemo } from "react"; +import { CollectionItem } from "../common-hooks"; import { KeySet } from "./keyset"; -import { CollectionItem } from "./common-hooks"; import { ViewportRange } from "./useScrollPosition"; /**