mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-17 10:11:31 +10:00
refactor(v4.0.0-alpha): beginning of a new era
This commit is contained in:
25
libs/hooks/src/hooks/use-breakpoint.ts
Normal file
25
libs/hooks/src/hooks/use-breakpoint.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { breakpoints } from "@reactive-resume/utils";
|
||||
import { useMemo } from "react";
|
||||
import { useBreakpoint as _useBreakpoint } from "use-breakpoint";
|
||||
|
||||
export const useBreakpoint = () => {
|
||||
const { breakpoint, minWidth, maxWidth } = _useBreakpoint(breakpoints);
|
||||
|
||||
const { isMobile, isTablet, isDesktop } = useMemo(() => {
|
||||
return {
|
||||
isMobile: breakpoint === "xs" || breakpoint === "sm" || breakpoint === "md",
|
||||
isTablet: breakpoint === "sm" || breakpoint === "md",
|
||||
isDesktop: breakpoint === "lg" || breakpoint === "xl" || breakpoint === "2xl",
|
||||
};
|
||||
}, [breakpoint]);
|
||||
|
||||
return {
|
||||
breakpoint,
|
||||
minWidth,
|
||||
maxWidth,
|
||||
isMobile,
|
||||
isTablet,
|
||||
isDesktop,
|
||||
devicePixelRatio: window.devicePixelRatio,
|
||||
};
|
||||
};
|
||||
37
libs/hooks/src/hooks/use-form-field.ts
Normal file
37
libs/hooks/src/hooks/use-form-field.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { FieldPath, FieldValues } from "react-hook-form";
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
> = { name: TName };
|
||||
|
||||
export const FormFieldContext = createContext<FormFieldContextValue>({} as FormFieldContextValue);
|
||||
|
||||
type FormItemContextValue = { id: string };
|
||||
|
||||
export const FormItemContext = createContext<FormItemContextValue>({} as FormItemContextValue);
|
||||
|
||||
export const useFormField = () => {
|
||||
const fieldContext = useContext(FormFieldContext);
|
||||
const itemContext = useContext(FormItemContext);
|
||||
const { getFieldState, formState } = useFormContext();
|
||||
|
||||
const fieldState = getFieldState(fieldContext.name, formState);
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error("useFormField should be used within <FormField>");
|
||||
}
|
||||
|
||||
const { id } = itemContext;
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
};
|
||||
};
|
||||
39
libs/hooks/src/hooks/use-password-toggle.ts
Normal file
39
libs/hooks/src/hooks/use-password-toggle.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
export const usePasswordToggle = (formRef: React.RefObject<HTMLElement | null>) => {
|
||||
// Show Password on "Control" Key Down
|
||||
useEffect(() => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Control") {
|
||||
formRef.current
|
||||
?.querySelector<HTMLInputElement>('input[name="password"]')
|
||||
?.setAttribute("type", "text");
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", onKeyDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, [formRef]);
|
||||
|
||||
// Hide Password on "Control" Key Up
|
||||
useEffect(() => {
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === "Control") {
|
||||
formRef.current
|
||||
?.querySelector<HTMLInputElement>('input[name="password"]')
|
||||
?.setAttribute("type", "password");
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keyup", onKeyUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [formRef]);
|
||||
|
||||
return;
|
||||
};
|
||||
54
libs/hooks/src/hooks/use-theme.ts
Normal file
54
libs/hooks/src/hooks/use-theme.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { type Dispatch, type SetStateAction, useEffect, useState } from "react";
|
||||
import { useLocalStorage, useMediaQuery, useUpdateEffect } from "usehooks-ts";
|
||||
|
||||
const COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)";
|
||||
|
||||
type Theme = "system" | "dark" | "light";
|
||||
|
||||
interface UseThemeOutput {
|
||||
theme: Theme;
|
||||
isDarkMode: boolean;
|
||||
toggleTheme: () => void;
|
||||
setTheme: Dispatch<SetStateAction<Theme>>;
|
||||
}
|
||||
|
||||
export const useTheme = (): UseThemeOutput => {
|
||||
const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY);
|
||||
const [isDarkMode, setDarkMode] = useState<boolean>(isDarkOS);
|
||||
const [theme, setTheme] = useLocalStorage<Theme>("theme", "system");
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (theme === "system") setDarkMode(isDarkOS);
|
||||
}, [isDarkOS]);
|
||||
|
||||
useEffect(() => {
|
||||
switch (theme) {
|
||||
case "light":
|
||||
setDarkMode(false);
|
||||
break;
|
||||
case "system":
|
||||
setDarkMode(isDarkOS);
|
||||
break;
|
||||
case "dark":
|
||||
setDarkMode(true);
|
||||
break;
|
||||
}
|
||||
}, [theme, isDarkOS]);
|
||||
|
||||
function toggleTheme() {
|
||||
const toggleDict: Record<Theme, Theme> = {
|
||||
light: "system",
|
||||
system: "dark",
|
||||
dark: "light",
|
||||
};
|
||||
|
||||
setTheme((prevMode) => toggleDict[prevMode]);
|
||||
}
|
||||
|
||||
return {
|
||||
theme,
|
||||
setTheme,
|
||||
isDarkMode,
|
||||
toggleTheme,
|
||||
};
|
||||
};
|
||||
4
libs/hooks/src/index.ts
Normal file
4
libs/hooks/src/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./hooks/use-breakpoint";
|
||||
export * from "./hooks/use-form-field";
|
||||
export * from "./hooks/use-password-toggle";
|
||||
export * from "./hooks/use-theme";
|
||||
Reference in New Issue
Block a user