mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-03 13:29:06 +00:00
fix: column reordering
refactor: migrate local storage hook
This commit is contained in:
committed by
Carlos Valente
parent
4beac19c4a
commit
b5993b91e1
@@ -1,46 +0,0 @@
|
|||||||
import { useSyncExternalStore } from 'react';
|
|
||||||
|
|
||||||
const STORAGE_EVENT = 'ontime-storage';
|
|
||||||
|
|
||||||
function getSnapshot(key: string): string | null {
|
|
||||||
try {
|
|
||||||
return window.localStorage.getItem(`ontime-${key}`);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getParsedJson<T>(localStorageValue: string | null, initialValue: T): T {
|
|
||||||
try {
|
|
||||||
return localStorageValue ? JSON.parse(localStorageValue) : initialValue;
|
|
||||||
} catch {
|
|
||||||
return initialValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useLocalStorage = <T>(key: string, initialValue: T) => {
|
|
||||||
const localStorageValue = useSyncExternalStore(subscribe, () => getSnapshot(key));
|
|
||||||
const parsedLocalStorageValue = getParsedJson(localStorageValue, initialValue);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Set value to local storage
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
const setLocalStorageValue = (value: T | ((val: T) => T)) => {
|
|
||||||
// Allow value to be a function so we have same API as useState
|
|
||||||
const valueToStore = value instanceof Function ? value(parsedLocalStorageValue) : value;
|
|
||||||
|
|
||||||
localStorage.setItem(`ontime-${key}`, JSON.stringify(valueToStore));
|
|
||||||
window.dispatchEvent(new StorageEvent(STORAGE_EVENT));
|
|
||||||
};
|
|
||||||
|
|
||||||
return [parsedLocalStorageValue, setLocalStorageValue] as const;
|
|
||||||
};
|
|
||||||
|
|
||||||
function subscribe(callback: () => void) {
|
|
||||||
window.addEventListener(STORAGE_EVENT, callback);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener(STORAGE_EVENT, callback);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Tooltip } from '@chakra-ui/react';
|
import { Tooltip } from '@chakra-ui/react';
|
||||||
|
import { useLocalStorage } from '@mantine/hooks';
|
||||||
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
|
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
|
||||||
import { IoRemove } from '@react-icons/all-files/io5/IoRemove';
|
import { IoRemove } from '@react-icons/all-files/io5/IoRemove';
|
||||||
import { Playback } from 'ontime-types';
|
import { Playback } from 'ontime-types';
|
||||||
import { MILLIS_PER_HOUR, MILLIS_PER_SECOND, parseUserTime } from 'ontime-utils';
|
import { MILLIS_PER_HOUR, MILLIS_PER_SECOND, parseUserTime } from 'ontime-utils';
|
||||||
|
|
||||||
import TimeInput from '../../../../common/components/input/time-input/TimeInput';
|
import TimeInput from '../../../../common/components/input/time-input/TimeInput';
|
||||||
import { useLocalStorage } from '../../../../common/hooks/useLocalStorage';
|
|
||||||
import { setPlayback } from '../../../../common/hooks/useSocket';
|
import { setPlayback } from '../../../../common/hooks/useSocket';
|
||||||
import { tooltipDelayMid } from '../../../../ontimeConfig';
|
import { tooltipDelayMid } from '../../../../ontimeConfig';
|
||||||
import TapButton from '../tap-button/TapButton';
|
import TapButton from '../tap-button/TapButton';
|
||||||
@@ -18,7 +18,7 @@ interface AddTimeProps {
|
|||||||
|
|
||||||
export default function AddTime(props: AddTimeProps) {
|
export default function AddTime(props: AddTimeProps) {
|
||||||
const { playback } = props;
|
const { playback } = props;
|
||||||
const [time, setTime] = useLocalStorage('add-time', 300_000); // 5 minutes
|
const [time, setTime] = useLocalStorage({ key: 'add-time', defaultValue: 300_000 }); // 5 minutes
|
||||||
|
|
||||||
const handleTimeChange = (_field: string, value: string) => {
|
const handleTimeChange = (_field: string, value: string) => {
|
||||||
const newTime = parseUserTime(value);
|
const newTime = parseUserTime(value);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
import { useLocalStorage } from '@mantine/hooks';
|
||||||
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
|
import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
import { isOntimeBlock, isOntimeDelay, isOntimeEvent, OntimeRundown, OntimeRundownEntry } from 'ontime-types';
|
import { isOntimeBlock, isOntimeDelay, isOntimeEvent, OntimeRundown, OntimeRundownEntry } from 'ontime-types';
|
||||||
|
|
||||||
import useFollowComponent from '../../common/hooks/useFollowComponent';
|
import useFollowComponent from '../../common/hooks/useFollowComponent';
|
||||||
import { useLocalStorage } from '../../common/hooks/useLocalStorage';
|
|
||||||
import { getAccessibleColour } from '../../common/utils/styleUtils';
|
import { getAccessibleColour } from '../../common/utils/styleUtils';
|
||||||
|
|
||||||
import BlockRow from './cuesheet-table-elements/BlockRow';
|
import BlockRow from './cuesheet-table-elements/BlockRow';
|
||||||
@@ -27,14 +27,23 @@ interface CuesheetProps {
|
|||||||
export default function Cuesheet({ data, columns, handleUpdate, selectedId }: CuesheetProps) {
|
export default function Cuesheet({ data, columns, handleUpdate, selectedId }: CuesheetProps) {
|
||||||
const { followSelected, showSettings, showDelayBlock, showPrevious } = useCuesheetSettings();
|
const { followSelected, showSettings, showDelayBlock, showPrevious } = useCuesheetSettings();
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] = useLocalStorage('table-hidden', {});
|
const [columnVisibility, setColumnVisibility] = useLocalStorage({ key: 'table-hidden', defaultValue: {} });
|
||||||
const [columnOrder, saveColumnOrder] = useLocalStorage<string[]>('table-order', initialColumnOrder);
|
const [columnOrder, saveColumnOrder] = useLocalStorage<string[]>({
|
||||||
const [columnSizing, setColumnSizing] = useLocalStorage('table-sizes', {});
|
key: 'table-order',
|
||||||
|
defaultValue: initialColumnOrder,
|
||||||
|
});
|
||||||
|
const [columnSizing, setColumnSizing] = useLocalStorage({ key: 'table-sizes', defaultValue: {} });
|
||||||
|
|
||||||
const selectedRef = useRef<HTMLTableRowElement | null>(null);
|
const selectedRef = useRef<HTMLTableRowElement | null>(null);
|
||||||
const tableContainerRef = useRef<HTMLDivElement | null>(null);
|
const tableContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
useFollowComponent({ followRef: selectedRef, scrollRef: tableContainerRef, doFollow: followSelected });
|
useFollowComponent({ followRef: selectedRef, scrollRef: tableContainerRef, doFollow: followSelected });
|
||||||
|
|
||||||
|
// keep column order in sync with columns
|
||||||
|
useEffect(() => {
|
||||||
|
const order = columns.map((col) => col.id as string);
|
||||||
|
saveColumnOrder(order);
|
||||||
|
}, [columns, saveColumnOrder]);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
@@ -64,6 +73,26 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId }: Cu
|
|||||||
setColumnSizing({});
|
setColumnSizing({});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const reorder = useCallback(
|
||||||
|
(fromId: string, toId: string) => {
|
||||||
|
// get index of from
|
||||||
|
const fromIndex = columnOrder.indexOf(fromId);
|
||||||
|
|
||||||
|
// get index of to
|
||||||
|
const toIndex = columnOrder.indexOf(toId);
|
||||||
|
|
||||||
|
if (toIndex === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reorderedCols = [...columnOrder];
|
||||||
|
const reorderedItem = reorderedCols.splice(fromIndex, 1);
|
||||||
|
reorderedCols.splice(toIndex, 0, reorderedItem[0]);
|
||||||
|
saveColumnOrder(reorderedCols);
|
||||||
|
},
|
||||||
|
[columnOrder, saveColumnOrder],
|
||||||
|
);
|
||||||
|
|
||||||
const headerGroups = table.getHeaderGroups();
|
const headerGroups = table.getHeaderGroups();
|
||||||
const rowModel = table.getRowModel();
|
const rowModel = table.getRowModel();
|
||||||
const allLeafColumns = table.getAllLeafColumns();
|
const allLeafColumns = table.getAllLeafColumns();
|
||||||
@@ -83,7 +112,7 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId }: Cu
|
|||||||
)}
|
)}
|
||||||
<div ref={tableContainerRef} className={style.cuesheetContainer}>
|
<div ref={tableContainerRef} className={style.cuesheetContainer}>
|
||||||
<table className={style.cuesheet}>
|
<table className={style.cuesheet}>
|
||||||
<CuesheetHeader headerGroups={headerGroups} />
|
<CuesheetHeader headerGroups={headerGroups} saveColumnOrder={reorder} />
|
||||||
<tbody>
|
<tbody>
|
||||||
{rowModel.rows.map((row) => {
|
{rowModel.rows.map((row) => {
|
||||||
const key = row.original.id;
|
const key = row.original.id;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { memo, useEffect } from 'react';
|
|
||||||
import { Tooltip } from '@chakra-ui/react';
|
import { Tooltip } from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
closestCenter,
|
closestCenter,
|
||||||
@@ -14,10 +13,8 @@ import { horizontalListSortingStrategy, SortableContext, sortableKeyboardCoordin
|
|||||||
import { flexRender, HeaderGroup } from '@tanstack/react-table';
|
import { flexRender, HeaderGroup } from '@tanstack/react-table';
|
||||||
import { OntimeRundownEntry } from 'ontime-types';
|
import { OntimeRundownEntry } from 'ontime-types';
|
||||||
|
|
||||||
import { useLocalStorage } from '../../../common/hooks/useLocalStorage';
|
|
||||||
import { getAccessibleColour } from '../../../common/utils/styleUtils';
|
import { getAccessibleColour } from '../../../common/utils/styleUtils';
|
||||||
import { tooltipDelayFast } from '../../../ontimeConfig';
|
import { tooltipDelayFast } from '../../../ontimeConfig';
|
||||||
import { initialColumnOrder } from '../cuesheetCols';
|
|
||||||
|
|
||||||
import { SortableCell } from './SortableCell';
|
import { SortableCell } from './SortableCell';
|
||||||
|
|
||||||
@@ -25,17 +22,11 @@ import style from '../Cuesheet.module.scss';
|
|||||||
|
|
||||||
interface CuesheetHeaderProps {
|
interface CuesheetHeaderProps {
|
||||||
headerGroups: HeaderGroup<OntimeRundownEntry>[];
|
headerGroups: HeaderGroup<OntimeRundownEntry>[];
|
||||||
|
saveColumnOrder: (fromId: string, toId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CuesheetHeader(props: CuesheetHeaderProps) {
|
export default function CuesheetHeader(props: CuesheetHeaderProps) {
|
||||||
const { headerGroups } = props;
|
const { headerGroups, saveColumnOrder } = props;
|
||||||
const [columnOrder, saveColumnOrder] = useLocalStorage<string[]>('table-order', initialColumnOrder);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!localStorage.getItem('table-order')) {
|
|
||||||
saveColumnOrder(initialColumnOrder);
|
|
||||||
}
|
|
||||||
}, [saveColumnOrder]);
|
|
||||||
|
|
||||||
const handleOnDragEnd = (event: DragEndEvent) => {
|
const handleOnDragEnd = (event: DragEndEvent) => {
|
||||||
const { delta, active, over } = event;
|
const { delta, active, over } = event;
|
||||||
@@ -45,21 +36,7 @@ function CuesheetHeader(props: CuesheetHeaderProps) {
|
|||||||
// cancel if we do not have an over id
|
// cancel if we do not have an over id
|
||||||
if (over?.id == null) return;
|
if (over?.id == null) return;
|
||||||
|
|
||||||
// get index of from
|
saveColumnOrder(active.id as string, over.id as string);
|
||||||
const fromIndex = columnOrder.indexOf(active.id as string);
|
|
||||||
|
|
||||||
// get index of to
|
|
||||||
const toIndex = columnOrder.indexOf(over.id as string);
|
|
||||||
|
|
||||||
if (toIndex === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reorderedCols = [...columnOrder];
|
|
||||||
const reorderedItem = reorderedCols.splice(fromIndex, 1);
|
|
||||||
reorderedCols.splice(toIndex, 0, reorderedItem[0]);
|
|
||||||
|
|
||||||
saveColumnOrder(reorderedCols);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
@@ -119,5 +96,3 @@ function CuesheetHeader(props: CuesheetHeaderProps) {
|
|||||||
</thead>
|
</thead>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(CuesheetHeader);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user