mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-20 23:13:23 +10:00
refactor(v4.0.0-alpha): beginning of a new era
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@reactive-resume/hooks",
|
||||
"version": "0.0.1",
|
||||
"private": false,
|
||||
"main": "./index.js",
|
||||
"types": "./index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.mjs",
|
||||
"require": "./index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "hooks",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/hooks/src",
|
||||
"projectType": "library",
|
||||
"tags": ["frontend"],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/hooks/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"executor": "@nx/vite:build",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"outputPath": "dist/libs/hooks"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"mode": "development"
|
||||
},
|
||||
"production": {
|
||||
"mode": "production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/vite:test",
|
||||
"outputs": ["{options.reportsDirectory}"],
|
||||
"options": {
|
||||
"passWithNoTests": true,
|
||||
"reportsDirectory": "../../coverage/libs/hooks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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";
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"types": ["vite/client", "vitest"]
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx"
|
||||
],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx", "src/index.ts.bak"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["vitest/globals", "vitest/importMeta", "vite/client", "node", "vitest"]
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/// <reference types='vitest' />
|
||||
|
||||
import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import * as path from "path";
|
||||
import { defineConfig, splitVendorChunkPlugin } from "vite";
|
||||
import dts from "vite-plugin-dts";
|
||||
|
||||
export default defineConfig({
|
||||
cacheDir: "../../node_modules/.vite/hooks",
|
||||
|
||||
plugins: [
|
||||
react(),
|
||||
nxViteTsPaths(),
|
||||
splitVendorChunkPlugin(),
|
||||
dts({
|
||||
entryRoot: "src",
|
||||
tsconfigPath: path.join(__dirname, "tsconfig.lib.json"),
|
||||
}),
|
||||
],
|
||||
|
||||
build: {
|
||||
lib: {
|
||||
entry: "src/index.ts",
|
||||
name: "hooks",
|
||||
fileName: "index",
|
||||
formats: ["es", "cjs"],
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [/^react.*/, "react-hook-form", "use-breakpoint", "usehooks-ts"],
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: "../../node_modules/.vitest",
|
||||
},
|
||||
environment: "jsdom",
|
||||
include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user