build ok,

This commit is contained in:
louiscklaw
2025-04-14 09:26:24 +08:00
commit 6c931c1fe8
770 changed files with 63959 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
import * as React from 'react';
interface DialogController<T> {
data?: T;
handleClose: () => void;
handleOpen: (data?: T) => void;
open: boolean;
}
export function useDialog<T = unknown>(): DialogController<T> {
const [state, setState] = React.useState<{ open: boolean; data?: T }>({ open: false, data: undefined });
const handleOpen = React.useCallback((data?: T) => {
setState({ open: true, data });
}, []);
const handleClose = React.useCallback(() => {
setState({ open: false });
}, []);
return { data: state.data, handleClose, handleOpen, open: state.open };
}

View File

@@ -0,0 +1,49 @@
import * as React from 'react';
import { useTheme } from '@mui/material/styles';
type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
type FnName = 'up' | 'down' | 'between' | 'only' | 'not';
export function useMediaQuery(fn: 'up' | 'down', key: Breakpoint | number): boolean;
export function useMediaQuery(fn: 'between', start: Breakpoint | number, end: Breakpoint | number): boolean;
export function useMediaQuery(fn: 'only' | 'not', key: Breakpoint): boolean;
export function useMediaQuery(fn: FnName, start: Breakpoint | number, end?: Breakpoint | number): boolean {
const theme = useTheme();
const [matches, setMatches] = React.useState<boolean>(false);
let mq: string;
if (['up', 'down'].includes(fn) && start) {
mq = theme.breakpoints[fn as 'up' | 'down'](start);
} else if (fn === 'between' && start && end) {
mq = theme.breakpoints[fn as 'between'](start, end);
} else if (['only', 'not'].includes(fn) && start) {
mq = theme.breakpoints[fn as 'only' | 'not'](start as Breakpoint);
} else {
throw new Error('Invalid useMediaQuery params');
}
mq = mq.replace(/^@media(?: ?)/m, '');
React.useEffect((): (() => void) => {
setMatches(window.matchMedia(mq).matches);
function handler(event: MediaQueryListEvent): void {
setMatches(event.matches);
}
const mediaQueryList = window.matchMedia(mq);
mediaQueryList.addEventListener('change', handler);
return (): void => {
mediaQueryList.removeEventListener('change', handler);
};
}, [mq]);
return matches;
}

View File

@@ -0,0 +1,28 @@
import * as React from 'react';
interface PopoverController<T> {
anchorRef: React.MutableRefObject<T | null>;
handleOpen: () => void;
handleClose: () => void;
handleToggle: () => void;
open: boolean;
}
export function usePopover<T = HTMLElement>(): PopoverController<T> {
const anchorRef = React.useRef<T>(null);
const [open, setOpen] = React.useState<boolean>(false);
const handleOpen = React.useCallback(() => {
setOpen(true);
}, []);
const handleClose = React.useCallback(() => {
setOpen(false);
}, []);
const handleToggle = React.useCallback(() => {
setOpen((prevState) => !prevState);
}, []);
return { anchorRef, handleClose, handleOpen, handleToggle, open };
}

View File

@@ -0,0 +1,57 @@
import * as React from 'react';
export interface Selection<T = string> {
deselectAll: () => void;
deselectOne: (key: T) => void;
selectAll: () => void;
selectOne: (key: T) => void;
selected: Set<T>;
selectedAny: boolean;
selectedAll: boolean;
}
// IMPORTANT: To prevent infinite loop, `keys` argument must be memoized with React.useMemo hook.
export function useSelection<T = string>(keys: T[] = []): Selection<T> {
const [selected, setSelected] = React.useState<Set<T>>(new Set());
React.useEffect(() => {
setSelected(new Set());
}, [keys]);
const handleDeselectAll = React.useCallback(() => {
setSelected(new Set());
}, []);
const handleDeselectOne = React.useCallback((key: T) => {
setSelected((prev) => {
const copy = new Set(prev);
copy.delete(key);
return copy;
});
}, []);
const handleSelectAll = React.useCallback(() => {
setSelected(new Set(keys));
}, [keys]);
const handleSelectOne = React.useCallback((key: T) => {
setSelected((prev) => {
const copy = new Set(prev);
copy.add(key);
return copy;
});
}, []);
const selectedAny = selected.size > 0;
const selectedAll = selected.size === keys.length;
return {
deselectAll: handleDeselectAll,
deselectOne: handleDeselectOne,
selectAll: handleSelectAll,
selectOne: handleSelectOne,
selected,
selectedAny,
selectedAll,
};
}

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import { SettingsContext } from '@/contexts/settings';
import type { SettingsContextValue } from '@/contexts/settings';
export function useSettings(): SettingsContextValue {
const context = React.useContext(SettingsContext);
if (!context) {
throw new Error('useSettings must be used within a SettingsProvider');
}
return context;
}

View File

@@ -0,0 +1,14 @@
import * as React from 'react';
import type { UserContextValue } from '@/contexts/auth/types';
import { UserContext } from '@/contexts/auth/user-context';
export function useUser(): UserContextValue {
const context = React.useContext(UserContext);
if (!context) {
throw new Error('useUser must be used within a UserProvider');
}
return context;
}