mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-21 15:09:10 +00:00
fix: cuesheet header (#600)
* `useLocalStorage` hook uses `useSyncExternalStore` --------- Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com>
This commit is contained in:
@@ -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}>
|
||||||
|
|||||||
Reference in New Issue
Block a user