feat(app): major refactorings and details footer updates

This commit is contained in:
rektdeckard
2023-02-11 13:58:33 -07:00
parent eba876b3ea
commit 345acafb45
33 changed files with 376 additions and 540 deletions

View File

@@ -1,7 +1,10 @@
export { default as useCSSVariables } from "./useCSSVariables";
export { default as useDebounce } from "./useDebounce";
export { default as useEvent } from "./useEvent";
export { default as useIconParameters } from "./useIconParameters";
export { default as useMediaQuery } from "./useMediaQuery";
export { default as usePersistSettings } from "./usePersistSettings";
export { default as useSessionState } from "./useSessionState";
export { default as useThrottle } from "./useThrottle";
export { default as useThrottled } from "./useThrottled";
export { default as useTimeoutFn } from "./useTimeoutFn";

View File

@@ -0,0 +1,40 @@
import { useEffect, useRef } from "react";
type CSSCustomPropertyName = `--${string}`;
type CSSCustomProperties = {
[property: CSSCustomPropertyName]: string | null;
};
function simpleDiff(prev: CSSCustomProperties, next: CSSCustomProperties) {
const merge = { ...prev, ...next };
return Object.entries(merge).reduce<
[property: CSSCustomPropertyName, value: string | null][]
>((acc, [k, val]) => {
let key = k as CSSCustomPropertyName;
if (
!prev[key as CSSCustomPropertyName] ||
prev[key as CSSCustomPropertyName] !== val
) {
acc.push([key, val]);
}
return acc;
}, []);
}
export default function useCSSVariables(properties: CSSCustomProperties) {
const p = useRef<CSSCustomProperties>({});
useEffect(() => {
const diff = simpleDiff(p.current, properties);
if (diff.length > 0) {
diff.forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value);
});
p.current = properties;
}
}, [properties]);
}

View File

@@ -18,7 +18,7 @@ export type UseEventType<E extends UseEventTarget> = keyof UseEventMap<E>;
* provided event name (currently supports {@link Window}, {@link Document},
* and subclasses of {@link HTMLElement} and {@link SVGElement}).
*
* @param type an {@link https://developer.mozilla.org/en-US/docs/Web/Events#event_listing event type}
* @param type an {@link https://developer.mozilla.org/en-US/docs/Web/Events#event_listing event type}
* @param listener a callback to be fired on the event
* @param options {@link AddEventListenerOptions}
* @param el the target element to attack the listener. Defaults to

View File

@@ -1,18 +0,0 @@
import { useWindowSize } from "react-use";
const MOBILE_BREAKPOINT = 536;
const GRID_PADDING = 32; // .grid-container { padding }
const TOOLBAR_WIDTH = 17; // IS THIS BROWSER-SPECIFIC?
const MAX_GRID_WIDTH = 1120; // .grid { max-width }
const ITEM_WIDTH = 168; // .grid-item { width; height; margin }
const ITEM_WIDTH_MOBILE = 108; // .grid-item { width; height; margin }
export default (): number => {
const { width } = useWindowSize();
const itemWidth = width <= MOBILE_BREAKPOINT ? ITEM_WIDTH_MOBILE : ITEM_WIDTH;
return Math.floor(
Math.min(width - GRID_PADDING - TOOLBAR_WIDTH, MAX_GRID_WIDTH) / itemWidth
);
};

View File

@@ -0,0 +1,12 @@
import { useMemo, useReducer, Reducer } from "react";
import useEvent from "./useEvent";
const updater: Reducer<number, void> = (s) => (s + 1) % 1_000_000;
export default function useMediaQuery(query: string) {
const mq = useMemo(() => window.matchMedia(query), [query]);
const [, update] = useReducer(updater, 0);
useEvent("resize", update, { passive: true });
return mq.matches;
}

View File

@@ -1,6 +1,11 @@
import { useRecoilValue } from "recoil";
import {
iconWeightAtom,
iconSizeAtom,
iconColorAtom,
STORAGE_KEY,
} from "@/state";
import useDebounce from "./useDebounce";
import { iconWeightAtom, iconSizeAtom, iconColorAtom } from "../state/atoms";
export default function usePersistSettings() {
const weight = useRecoilValue(iconWeightAtom);
@@ -10,7 +15,7 @@ export default function usePersistSettings() {
useDebounce(
() => {
const serializedState = JSON.stringify({ weight, size, color });
window.localStorage.setItem("__phosphor_settings__", serializedState);
window.localStorage.setItem(STORAGE_KEY, serializedState);
},
2000,
[weight, size, color]

View File

@@ -0,0 +1,35 @@
import { useCallback, useState, Dispatch, SetStateAction } from "react";
import { STORAGE_KEY } from "@/state";
type Initializer<S> = () => S;
type Setter<S> = (prev: S) => S;
type Action<S> = S | Setter<S> | Initializer<S>;
function expand<S extends object>(action: Action<S>, prev?: S) {
if (typeof action === "function") {
return (action as Setter<S>)(prev!);
} else {
return action;
}
}
export default function useSessionState<S extends object>(
key: string,
fallbackState: S | (() => S)
): [S, Dispatch<SetStateAction<S>>] {
const [value, setValue] = useState<S>(() => {
let val = sessionStorage.getItem(STORAGE_KEY + key);
if (val) return JSON.parse(val) as S;
return expand(fallbackState);
});
const set: Dispatch<SetStateAction<S>> = useCallback((val) => {
setValue((prev) => {
const next = expand(val, prev);
sessionStorage.setItem(STORAGE_KEY + key, JSON.stringify(next));
return next;
});
}, []);
return [value, set];
}