fix: cuesheet header (#600)

* `useLocalStorage` hook uses `useSyncExternalStore`
---------

Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com>
This commit is contained in:
Carlos Valente
2023-11-20 21:11:52 +01:00
committed by GitHub
parent afa62a6e38
commit a79ae7e0c4
4 changed files with 50 additions and 56 deletions
+34 -41
View File
@@ -1,53 +1,46 @@
import { useEffect, useState } from 'react'; import { useSyncExternalStore } from 'react';
/** const STORAGE_EVENT = 'ontime-storage';
* @description utility hook to handle state in local storage
* @param key
* @param initialValue
*/
export const useLocalStorage = <T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void] => {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(`ontime-${key}`);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
useEffect(() => { function getSnapshot(key: string): string | null {
const handleStorageChange = (event: StorageEvent) => { try {
if (event.storageArea === window.localStorage && event.key === key) { return window.localStorage.getItem(`ontime-${key}`);
try { } catch {
const newValue = event.newValue ? JSON.parse(event.newValue) : initialValue; return null;
setStoredValue(newValue); }
} catch (_) { }
/* empty */
}
}
};
window.addEventListener('storage', handleStorageChange); function getParsedJson<T>(localStorageValue: string | null, initialValue: T): T {
try {
return localStorageValue ? JSON.parse(localStorageValue) : initialValue;
} catch {
return initialValue;
}
}
return () => { export const useLocalStorage = <T>(key: string, initialValue: T) => {
window.removeEventListener('storage', handleStorageChange); const localStorageValue = useSyncExternalStore(subscribe, () => getSnapshot(key));
}; const parsedLocalStorageValue = getParsedJson(localStorageValue, initialValue);
}, [initialValue, key]);
/** /**
* @description Set value to local storage * @description Set value to local storage
* @param value * @param value
*/ */
const setValue = (value: T | ((val: T) => T)) => { const setLocalStorageValue = (value: T | ((val: T) => T)) => {
try { // Allow value to be a function so we have same API as useState
// Allow value to be a function so we have same API as useState const valueToStore = value instanceof Function ? value(parsedLocalStorageValue) : value;
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore); localStorage.setItem(`ontime-${key}`, JSON.stringify(valueToStore));
window.localStorage.setItem(`ontime-${key}`, JSON.stringify(valueToStore)); window.dispatchEvent(new StorageEvent(STORAGE_EVENT));
} catch (error) {
console.error(error);
}
}; };
return [storedValue, setValue];
return [parsedLocalStorageValue, setLocalStorageValue] as const;
}; };
function subscribe(callback: () => void) {
window.addEventListener(STORAGE_EVENT, callback);
return () => {
window.removeEventListener(STORAGE_EVENT, callback);
};
}
@@ -24,10 +24,7 @@ interface CuesheetProps {
} }
export default function Cuesheet({ data, columns, handleUpdate, selectedId }: CuesheetProps) { export default function Cuesheet({ data, columns, handleUpdate, selectedId }: CuesheetProps) {
const followSelected = useCuesheetSettings((state) => state.followSelected); const { followSelected, showSettings, showDelayBlock, showPrevious } = useCuesheetSettings();
const showSettings = useCuesheetSettings((state) => state.showSettings);
const showDelayBlock = useCuesheetSettings((state) => state.showDelayBlock);
const showPrevious = useCuesheetSettings((state) => state.showPrevious);
const [columnVisibility, setColumnVisibility] = useLocalStorage('table-hidden', {}); const [columnVisibility, setColumnVisibility] = useLocalStorage('table-hidden', {});
const [columnOrder, saveColumnOrder] = useLocalStorage<string[]>('table-order', initialColumnOrder); const [columnOrder, saveColumnOrder] = useLocalStorage<string[]>('table-order', initialColumnOrder);
@@ -66,7 +63,9 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId }: Cu
setColumnSizing({}); setColumnSizing({});
}; };
const headerGroups = table.getHeaderGroups; const headerGroups = table.getHeaderGroups();
const rowModel = table.getRowModel();
const allLeafColumns = table.getAllLeafColumns();
let eventIndex = 0; let eventIndex = 0;
let isPast = Boolean(selectedId); let isPast = Boolean(selectedId);
@@ -75,7 +74,7 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId }: Cu
<> <>
{showSettings && ( {showSettings && (
<CuesheetTableSettings <CuesheetTableSettings
columns={table.getAllLeafColumns()} columns={allLeafColumns}
handleResetResizing={resetColumnResizing} handleResetResizing={resetColumnResizing}
handleResetReordering={resetColumnOrder} handleResetReordering={resetColumnOrder}
handleClearToggles={setAllVisible} handleClearToggles={setAllVisible}
@@ -85,7 +84,7 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId }: Cu
<table className={style.cuesheet}> <table className={style.cuesheet}>
<CuesheetHeader headerGroups={headerGroups} /> <CuesheetHeader headerGroups={headerGroups} />
<tbody> <tbody>
{table.getRowModel().rows.map((row) => { {rowModel.rows.map((row) => {
const key = row.original.id; const key = row.original.id;
const isSelected = selectedId === key; const isSelected = selectedId === key;
if (isSelected) { if (isSelected) {
@@ -23,7 +23,7 @@ import { SortableCell } from './SortableCell';
import style from '../Cuesheet.module.scss'; import style from '../Cuesheet.module.scss';
interface CuesheetHeaderProps { interface CuesheetHeaderProps {
headerGroups: () => HeaderGroup<OntimeRundownEntry>[]; headerGroups: HeaderGroup<OntimeRundownEntry>[];
} }
function CuesheetHeader(props: CuesheetHeaderProps) { function CuesheetHeader(props: CuesheetHeaderProps) {
@@ -75,7 +75,7 @@ function CuesheetHeader(props: CuesheetHeaderProps) {
return ( return (
<thead className={style.tableHeader}> <thead className={style.tableHeader}>
{headerGroups().map((headerGroup) => { {headerGroups.map((headerGroup) => {
const key = headerGroup.id; const key = headerGroup.id;
return ( return (
@@ -22,12 +22,14 @@ interface CuesheetTableSettingsProps {
function CuesheetTableSettings(props: CuesheetTableSettingsProps) { function CuesheetTableSettings(props: CuesheetTableSettingsProps) {
const { columns, handleResetResizing, handleResetReordering, handleClearToggles } = props; const { columns, handleResetResizing, handleResetReordering, handleClearToggles } = props;
const showPrevious = useCuesheetSettings((state) => state.showPrevious); const {
const togglePreviousVisibility = useCuesheetSettings((state) => state.togglePreviousVisibility); showPrevious,
const showDelayBlock = useCuesheetSettings((state) => state.showDelayBlock); toggleDelayVisibility,
const toggleDelayVisibility = useCuesheetSettings((state) => state.toggleDelayVisibility); showDelayBlock,
const showDelayedTimes = useCuesheetSettings((state) => state.showDelayedTimes); showDelayedTimes,
const toggleDelayedTimes = useCuesheetSettings((state) => state.toggleDelayedTimes); toggleDelayedTimes,
togglePreviousVisibility,
} = useCuesheetSettings();
return ( return (
<div className={style.tableSettings}> <div className={style.tableSettings}>