diff --git a/apps/client/package.json b/apps/client/package.json index 1a1e0e697..fb2e79cc2 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -15,6 +15,7 @@ "@mantine/hooks": "^7.13.3", "@react-icons/all-files": "^4.1.0", "@sentry/react": "^8.43.0", + "@table-nav/react": "^0.0.7", "@tanstack/react-query": "^5.62.7", "@tanstack/react-query-devtools": "^5.62.7", "@tanstack/react-table": "^8.20.5", diff --git a/apps/client/src/common/components/input/text-input/useReactiveTextInput.tsx b/apps/client/src/common/components/input/text-input/useReactiveTextInput.tsx index edec6366a..91dd879a4 100644 --- a/apps/client/src/common/components/input/text-input/useReactiveTextInput.tsx +++ b/apps/client/src/common/components/input/text-input/useReactiveTextInput.tsx @@ -17,6 +17,7 @@ export default function useReactiveTextInput( submitOnCtrlEnter?: boolean; onCancelUpdate?: () => void; allowSubmitSameValue?: boolean; + allowKeyboardNavigation?: boolean; }, ): UseReactiveTextInputReturn { const [text, setText] = useState(initialText); @@ -58,7 +59,14 @@ export default function useReactiveTextInput( setText(cleanVal); } } - setTimeout(() => ref.current?.blur()); // Immediate timeout to ensure text is set before bluring + + setTimeout(() => { + if (options?.allowKeyboardNavigation) { + ref.current?.parentElement?.focus(); // Focus on parent element to continue keyboard navigation + } else { + ref.current?.blur(); + } + }); // Immediate timeout to ensure text is set before blurring }, [initialText, options, ref, submitCallback], ); @@ -70,12 +78,25 @@ export default function useReactiveTextInput( const handleEscape = useCallback(() => { // No need to update if it hasn't changed setText(initialText); + // force the text to be the initial value + if (ref.current) { + ref.current.value = initialText; + } options?.onCancelUpdate?.(); - setTimeout(() => ref.current?.blur()); // Immediate timeout to ensure text is set before bluring + setTimeout(() => ref.current?.blur()); // Immediate timeout to ensure text is set before blurring }, [initialText, options, ref]); const keyHandler = useMemo(() => { - const hotKeys: HotkeyItem[] = [['Escape', handleEscape, { preventDefault: true }]]; + const hotKeys: HotkeyItem[] = [ + [ + 'Escape', + (event) => { + event.preventDefault(); + handleEscape(); + }, + { preventDefault: true }, + ], + ]; if (options?.submitOnEnter) { hotKeys.push(['Enter', () => handleSubmit(text)]); @@ -84,7 +105,22 @@ export default function useReactiveTextInput( if (options?.submitOnCtrlEnter) { hotKeys.push(['mod + Enter', () => handleSubmit(text)]); } - return getHotkeyHandler(hotKeys); + + const hotKeyHandler = getHotkeyHandler(hotKeys); + + return (event: KeyboardEvent) => { + // allow moving in input field with arrow keys + if ( + event.key === 'ArrowLeft' || + event.key === 'ArrowRight' || + event.key === 'ArrowUp' || + event.key === 'ArrowDown' + ) { + event.stopPropagation(); + } + + hotKeyHandler(event); + }; }, [handleEscape, handleSubmit, options?.submitOnCtrlEnter, options?.submitOnEnter, text]); return { diff --git a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss index 620bfbb1f..3bfa34a80 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss +++ b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.module.scss @@ -28,6 +28,11 @@ $table-header-font-size: calc(1rem - 2px); position: relative; @include ellipsis-overflow; } + + th:focus, + td:focus { + outline: 2px solid $ui-white; + } } .tableHeader, diff --git a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx index e2690ed9f..6a4c096f8 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx @@ -1,27 +1,14 @@ import { useCallback, useRef } from 'react'; +import { useTableNav } from '@table-nav/react'; import { ColumnDef, getCoreRowModel, useReactTable } from '@tanstack/react-table'; -import Color from 'color'; -import { - isOntimeBlock, - isOntimeDelay, - isOntimeEvent, - MaybeString, - OntimeEvent, - OntimeRundown, - OntimeRundownEntry, - TimeField, -} from 'ontime-types'; +import { isOntimeEvent, MaybeString, OntimeEvent, OntimeRundown, OntimeRundownEntry, TimeField } from 'ontime-types'; import { useEventAction } from '../../../common/hooks/useEventAction'; import useFollowComponent from '../../../common/hooks/useFollowComponent'; -import { useSelectedEventId } from '../../../common/hooks/useSocket'; -import { getAccessibleColour } from '../../../common/utils/styleUtils'; import { useCuesheetOptions } from '../cuesheet.options'; -import BlockRow from './cuesheet-table-elements/BlockRow'; +import CuesheetBody from './cuesheet-table-elements/CuesheetBody'; import CuesheetHeader from './cuesheet-table-elements/CuesheetHeader'; -import DelayRow from './cuesheet-table-elements/DelayRow'; -import EventRow from './cuesheet-table-elements/EventRow'; import CuesheetTableMenu from './cuesheet-table-menu/CuesheetTableMenu'; import CuesheetTableSettings from './cuesheet-table-settings/CuesheetTableSettings'; import useColumnManager from './useColumnManager'; @@ -38,8 +25,7 @@ export default function CuesheetTable(props: CuesheetTableProps) { const { data, columns, showModal } = props; const { updateEvent, updateTimer } = useEventAction(); - const { selectedEventId } = useSelectedEventId(); - const { followSelected, hideDelays, hidePast, showDelayedTimes, hideTableSeconds } = useCuesheetOptions(); + const { followSelected, showDelayedTimes, hideTableSeconds } = useCuesheetOptions(); const { columnVisibility, columnOrder, columnSizing, resetColumnOrder, setColumnVisibility, setColumnSizing } = useColumnManager(columns); @@ -47,6 +33,8 @@ export default function CuesheetTable(props: CuesheetTableProps) { const tableContainerRef = useRef(null); useFollowComponent({ followRef: selectedRef, scrollRef: tableContainerRef, doFollow: followSelected }); + const { listeners } = useTableNav(); + const table = useReactTable({ data, columns, @@ -63,6 +51,7 @@ export default function CuesheetTable(props: CuesheetTableProps) { handleUpdate: (rowIndex: number, accessor: string, payload: string, isCustom = false) => { // check if value is the same const event = data[rowIndex]; + if (!event || !isOntimeEvent(event)) { return; } @@ -104,10 +93,6 @@ export default function CuesheetTable(props: CuesheetTableProps) { const rowModel = table.getRowModel(); const allLeafColumns = table.getAllLeafColumns(); - let eventIndex = 0; - // for the first event, it will be past if there is something selected - let isPast = Boolean(selectedEventId); - return ( <>
- +
- - {rowModel.rows.map((row, index) => { - const key = row.original.id; - const isSelected = selectedEventId === key; - const entry = row.original; - if (isSelected) { - isPast = false; - } - - if (isOntimeBlock(entry)) { - return ; - } - if (isOntimeDelay(entry)) { - if (isPast && hidePast) { - return null; - } - const delayVal = entry.duration; - if (hideDelays || delayVal === 0) { - return null; - } - - return ; - } - if (isOntimeEvent(entry)) { - eventIndex++; - const isSelected = key === selectedEventId; - - if (isPast && hidePast) { - return null; - } - - let rowBgColour: string | undefined; - if (isSelected) { - rowBgColour = '#D20300'; // $red-700 - } else if (entry.colour) { - try { - // the colour is user defined and might be invalid - const accessibleBackgroundColor = Color(getAccessibleColour(entry.colour).backgroundColor); - rowBgColour = accessibleBackgroundColor.fade(0.75).hexa(); - } catch (_error) { - /* we do not handle errors here */ - } - } - - return ( - - ); - } - - // currently there is no scenario where entryType is not handled above, either way... - return null; - })} - +
diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/BlockRow.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/BlockRow.tsx index 1ef44ff37..9fac1f1b6 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/BlockRow.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/BlockRow.tsx @@ -19,7 +19,9 @@ function BlockRow(props: BlockRowProps) { return ( - {title} + + {title} + ); } diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/CuesheetBody.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/CuesheetBody.tsx new file mode 100644 index 000000000..d5ed7a3f8 --- /dev/null +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/CuesheetBody.tsx @@ -0,0 +1,96 @@ +import { MutableRefObject } from 'react'; +import { RowModel, Table } from '@tanstack/react-table'; +import Color from 'color'; +import { isOntimeBlock, isOntimeDelay, isOntimeEvent, OntimeRundownEntry } from 'ontime-types'; + +import { useSelectedEventId } from '../../../../common/hooks/useSocket'; +import { getAccessibleColour } from '../../../../common/utils/styleUtils'; +import { useCuesheetOptions } from '../../../cuesheet/cuesheet.options'; + +import BlockRow from './BlockRow'; +import DelayRow from './DelayRow'; +import EventRow from './EventRow'; + +interface CuesheetBodyProps { + rowModel: RowModel; + selectedRef: MutableRefObject; + table: Table; + columnSizing: Record; +} + +export default function CuesheetBody(props: CuesheetBodyProps) { + const { rowModel, selectedRef, table, columnSizing } = props; + + const { selectedEventId } = useSelectedEventId(); + const { hideDelays, hidePast } = useCuesheetOptions(); + + let eventIndex = 0; + // for the first event, it will be past if there is something selected + let isPast = Boolean(selectedEventId); + return ( + + {rowModel.rows.map((row, index) => { + const key = row.original.id; + const isSelected = selectedEventId === key; + const entry = row.original; + if (isSelected) { + isPast = false; + } + + if (isOntimeBlock(entry)) { + return ; + } + if (isOntimeDelay(entry)) { + if (isPast && hidePast) { + return null; + } + const delayVal = entry.duration; + if (hideDelays || delayVal === 0) { + return null; + } + + return ; + } + if (isOntimeEvent(entry)) { + eventIndex++; + const isSelected = key === selectedEventId; + + if (isPast && hidePast) { + return null; + } + + let rowBgColour: string | undefined; + if (isSelected) { + rowBgColour = '#D20300'; // $red-700 + } else if (entry.colour) { + try { + // the colour is user defined and might be invalid + const accessibleBackgroundColor = Color(getAccessibleColour(entry.colour).backgroundColor); + rowBgColour = accessibleBackgroundColor.fade(0.75).hexa(); + } catch (_error) { + /* we do not handle errors here */ + } + } + + return ( + + ); + } + + // currently there is no scenario where entryType is not handled above, either way... + return null; + })} + + ); +} diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/CuesheetHeader.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/CuesheetHeader.tsx index 25a497c2b..794705ca9 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/CuesheetHeader.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/CuesheetHeader.tsx @@ -24,8 +24,12 @@ export default function CuesheetHeader(props: CuesheetHeaderProps) { return ( - {showActionMenu && } - {!hideIndexColumn && #} + {showActionMenu && } + {!hideIndexColumn && ( + + # + + )} {headerGroup.headers.map((header) => { const width = header.getSize(); diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DelayRow.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DelayRow.tsx index d8aaa57cf..b7e842b29 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DelayRow.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DelayRow.tsx @@ -14,7 +14,9 @@ function DelayRow(props: DelayRowProps) { return ( - {delayTime} + + {delayTime} + ); } diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/EventRow.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/EventRow.tsx index 5d58e9132..96b45cbd9 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/EventRow.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/EventRow.tsx @@ -2,7 +2,7 @@ import { memo, MutableRefObject, useLayoutEffect, useRef, useState } from 'react import { IoEllipsisHorizontal } from '@react-icons/all-files/io5/IoEllipsisHorizontal'; import { flexRender, Table } from '@tanstack/react-table'; import Color from 'color'; -import { OntimeRundownEntry } from 'ontime-types'; +import { OntimeEvent, OntimeRundownEntry } from 'ontime-types'; import IconButton from '../../../../common/components/buttons/IconButton'; import { cx, getAccessibleColour } from '../../../../common/utils/styleUtils'; @@ -13,7 +13,7 @@ import style from '../CuesheetTable.module.scss'; interface EventRowProps { rowId: string; - eventId: string; + event: OntimeEvent; eventIndex: number; rowIndex: number; isPast?: boolean; @@ -26,10 +26,22 @@ interface EventRowProps { columnSizing: Record; } -export default memo(EventRow); +export default memo(EventRow, (prevProps, nextProps) => { + return ( + prevProps.rowId === nextProps.rowId && + prevProps.event.revision === nextProps.event.revision && + prevProps.eventIndex === nextProps.eventIndex && + prevProps.rowIndex === nextProps.rowIndex && + prevProps.isPast === nextProps.isPast && + prevProps.selectedRef === nextProps.selectedRef && + prevProps.rowBgColour === nextProps.rowBgColour && + prevProps.table === nextProps.table && + prevProps.columnSizing === nextProps.columnSizing + ); +}); function EventRow(props: EventRowProps) { - const { rowId, eventId, eventIndex, rowIndex, isPast, selectedRef, skip, colour, rowBgColour, table } = props; + const { rowId, event, eventIndex, rowIndex, isPast, selectedRef, rowBgColour, table } = props; const { hideIndexColumn, showActionMenu } = useCuesheetOptions(); const ownRef = useRef(null); const [isVisible, setIsVisible] = useState(false); @@ -63,23 +75,23 @@ function EventRow(props: EventRowProps) { }; }, [ownRef, selectedRef]); - const { color, backgroundColor } = getAccessibleColour(colour); + const { color, backgroundColor } = getAccessibleColour(event.colour); const mutedText = Color(color).fade(0.4).hexa(); return ( {showActionMenu && ( - + { - const rect = event.currentTarget.getBoundingClientRect(); + onClick={(e) => { + const rect = e.currentTarget.getBoundingClientRect(); const yPos = 8 + rect.y + rect.height / 2; - openMenu({ x: rect.x, y: yPos }, eventId, rowIndex); + openMenu({ x: rect.x, y: yPos }, event.id, rowIndex); }} > @@ -87,7 +99,7 @@ function EventRow(props: EventRowProps) { )} {!hideIndexColumn && ( - + {eventIndex} )} @@ -97,7 +109,12 @@ function EventRow(props: EventRowProps) { ?.getVisibleCells() .map((cell) => { return ( - + {flexRender(cell.column.columnDef.cell, cell.getContext())} ); diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/MultiLineCell.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/MultiLineCell.tsx index d8fcfbcbc..9a9c5ec67 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/MultiLineCell.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/MultiLineCell.tsx @@ -17,6 +17,7 @@ function MultiLineCell(props: MultiLineCellProps) { const { value, onChange, onBlur, onKeyDown } = useReactiveTextInput(initialValue, submitCallback, ref, { submitOnCtrlEnter: true, + allowKeyboardNavigation: true, }); return ( diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SingleLineCell.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SingleLineCell.tsx index efd7381cf..6eb546291 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SingleLineCell.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SingleLineCell.tsx @@ -17,6 +17,7 @@ const SingleLineCell = forwardRef((props: SingleLineCellProps, inputRef) => { const { value, onChange, onBlur, onKeyDown } = useReactiveTextInput(initialValue, submitCallback, ref, { allowSubmitSameValue, + allowKeyboardNavigation: true, submitOnEnter: true, // single line should submit on enter submitOnCtrlEnter: true, onCancelUpdate: handleCancelUpdate, @@ -31,6 +32,9 @@ const SingleLineCell = forwardRef((props: SingleLineCellProps, inputRef) => { select() { ref.current?.select(); }, + focusParentElement() { + ref.current?.parentElement?.focus(); + }, }; }, [ref]); diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SortableCell.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SortableCell.tsx index 30912ea44..05bd08954 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SortableCell.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/SortableCell.tsx @@ -28,7 +28,7 @@ export function SortableCell({ header, style, children }: SortableCellProps) { }; return ( - +
{children}
diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx index fbe5d1894..9c23fad7a 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx @@ -1,22 +1,34 @@ -import { HTMLAttributes, memo, PropsWithChildren } from 'react'; +import { forwardRef, HTMLAttributes, memo, PropsWithChildren, useImperativeHandle, useRef } from 'react'; import { cx } from '../../../../common/utils/styleUtils'; import style from './TextLikeInput.module.scss'; -export default memo(TextLikeInput); - interface TextLikeInputProps extends HTMLAttributes { delayed?: boolean; muted?: boolean; } -function TextLikeInput(props: PropsWithChildren) { +const TextLikeInput = forwardRef((props: PropsWithChildren, textRef) => { const { delayed, muted, children, className, ...elementProps } = props; + const ref = useRef(null); const classes = cx([style.textInput, delayed && style.delayed, muted && style.muted, className]); + + useImperativeHandle(textRef, () => { + return { + focusParentElement() { + ref.current?.parentElement?.focus(); + } + } + }) + return ( -
+
{children}
); -} +}); + +TextLikeInput.displayName = 'TextLikeInput'; + +export default memo(TextLikeInput); diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx index 873460add..cdda23367 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx @@ -11,6 +11,10 @@ interface TimeInputDurationProps { onSubmit: (value: string) => void; } +interface ParentFocusableInput extends HTMLInputElement { + focusParentElement: () => void; +} + export default memo(TimeInputDuration); function TimeInputDuration(props: PropsWithChildren) { @@ -18,7 +22,8 @@ function TimeInputDuration(props: PropsWithChildren) { const [isEditing, setIsEditing] = useState(false); const [value, setValue] = useState(initialValue); - const inputRef = useRef(null); + const inputRef = useRef(null); + const textRef = useRef(null); // when we go into edit mode, set focus to the input useEffect(() => { @@ -36,7 +41,10 @@ function TimeInputDuration(props: PropsWithChildren) { }, [initialValue, isEditing]); const handleFakeFocus = () => setIsEditing(true); - const handleFakeBlur = () => setIsEditing(false); + const handleFakeBlur = () => { + setIsEditing(false); + setTimeout(() => textRef.current?.focusParentElement()); // Immediate timeout to ensure state change takes place first + }; const handleUpdate = useCallback( (newValue: string) => { @@ -45,28 +53,33 @@ function TimeInputDuration(props: PropsWithChildren) { // if the user sends an empty string, we want to clear the value if (newValue === '') { onSubmit(newValue); + inputRef.current?.focusParentElement(); return; } // we dont know the values in the rundown, escalate to handler if (newValue.startsWith('p') || newValue.startsWith('+')) { onSubmit(newValue); + inputRef.current?.focusParentElement(); return; } const valueInMillis = parseUserTime(newValue); if (valueInMillis < 0 || isNaN(valueInMillis)) { setValue(initialValue); + setTimeout(() => textRef.current?.focusParentElement()); // Immediate timeout to ensure state change takes place first return; } // if the value is the same, we may still want to push the lock change if (valueInMillis === initialValue && lockedValue) { + inputRef.current?.focusParentElement(); return; } onSubmit(newValue); setValue(Number(newValue)); + setTimeout(() => textRef.current?.focusParentElement()); // Immediate timeout to ensure state change takes place first }, [initialValue, lockedValue, onSubmit], ); @@ -82,7 +95,13 @@ function TimeInputDuration(props: PropsWithChildren) { handleCancelUpdate={handleFakeBlur} /> ) : ( - + {children} ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 633e7b53c..4f1397536 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,6 +119,9 @@ importers: '@sentry/react': specifier: ^8.43.0 version: 8.45.0(react@18.3.1) + '@table-nav/react': + specifier: ^0.0.7 + version: 0.0.7(@table-nav/core@0.0.7) '@tanstack/react-query': specifier: ^5.62.7 version: 5.62.7(react@18.3.1) @@ -1949,6 +1952,14 @@ packages: resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} engines: {node: '>=14.16'} + '@table-nav/core@0.0.7': + resolution: {integrity: sha512-pCh18jHDRe3tw9sJZXfKi4cSD6VjHbn40CYdqhp5X91SIX7rakDEQAsTx6F7Fv9TUv265l+5rUDcYNaJ0N0cqQ==} + + '@table-nav/react@0.0.7': + resolution: {integrity: sha512-S+DsD/qDqp50Z4dqt5tZFMWA3sRu0OOT/grMQuq/z/52jPEKJB+b9t+YSH8Ms55vCJOJ0DxuYldJpYrJLMG5ew==} + peerDependencies: + '@table-nav/core': ^0.0.7 + '@tanstack/eslint-plugin-query@5.8.4': resolution: {integrity: sha512-KVgcMc+Bn1qbwkxYVWQoiVSNEIN4IAiLj3cUH/SAHT8m8E59Y97o8ON1syp0Rcw094ItG8pEVZFyQuOaH6PDgQ==} peerDependencies: @@ -6698,6 +6709,12 @@ snapshots: dependencies: defer-to-connect: 2.0.1 + '@table-nav/core@0.0.7': {} + + '@table-nav/react@0.0.7(@table-nav/core@0.0.7)': + dependencies: + '@table-nav/core': 0.0.7 + '@tanstack/eslint-plugin-query@5.8.4(eslint@8.56.0)(typescript@5.5.3)': dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@5.5.3)