From f877071c52797ccf67d836330eb4ba9baa572ee3 Mon Sep 17 00:00:00 2001 From: Vahor Date: Sat, 20 Apr 2024 13:10:56 +0200 Subject: [PATCH] refactor: replace prettier/eslint with biome a update biome --- .github/workflows/code-check.yml | 4 +- biome.json | 32 + docs/package.json | 8 +- package.json | 9 +- packages/common/.eslintignore | 2 - packages/common/.eslintrc.cjs | 7 - packages/common/.prettierignore | 4 - packages/common/package.json | 85 +- packages/common/src/cache/cache.test.ts | 168 +- packages/common/src/cache/cache.ts | 64 +- packages/common/src/cache/index.ts | 2 +- packages/common/src/cache/memory-cache.ts | 66 +- packages/common/src/cache/shared.ts | 10 +- packages/common/src/cache/types.ts | 20 +- packages/common/src/hooks/index.ts | 14 +- packages/common/src/hooks/useBeforeUnload.ts | 51 +- packages/common/src/hooks/useEventListener.ts | 100 +- .../src/hooks/useIsomorphicLayoutEffect.ts | 4 +- packages/common/src/hooks/useMediaQuery.ts | 78 +- .../common/src/hooks/useOnClickOutside.ts | 26 +- packages/common/src/hooks/useScrollLock.ts | 18 +- packages/common/src/hooks/util.ts | 34 +- packages/common/src/utils/circle-gradient.ts | 18 +- packages/common/src/utils/colors.test.ts | 74 +- packages/common/src/utils/colors.ts | 24 +- .../common/src/utils/generatePagination.ts | 44 +- packages/common/src/utils/hash.test.ts | 181 +- packages/common/src/utils/hash.ts | 73 +- packages/common/src/utils/is-network-error.ts | 44 +- packages/common/src/utils/math.test.ts | 34 +- packages/common/src/utils/math.ts | 16 +- packages/common/src/utils/navigation.ts | 10 +- packages/common/src/utils/random.test.ts | 38 +- packages/common/src/utils/random.ts | 8 +- packages/common/src/utils/wait.ts | 51 +- .../common/src/utils/wrap-with-loading.ts | 135 +- packages/common/tsconfig.json | 68 +- packages/common/tsup.config.ts | 59 +- packages/common/vitest.config.js | 12 +- packages/design/.eslintrc.cjs | 10 +- packages/design/components.json | 28 +- packages/design/package.json | 168 +- .../design/scripts/add-directive-dist.cjs | 81 +- packages/design/scripts/react-import.js | 2 +- packages/design/src/css.d.ts | 18 +- .../design/src/tailwind/tailwind.config.ts | 749 +++--- packages/design/src/ui/accordion.tsx | 90 +- packages/design/src/ui/aspect-ratio.tsx | 4 +- packages/design/src/ui/avatar.tsx | 59 +- packages/design/src/ui/badge.tsx | 38 +- packages/design/src/ui/burger/index.tsx | 35 +- packages/design/src/ui/button.tsx | 195 +- packages/design/src/ui/calendar.tsx | 204 +- .../design/src/ui/callout/InfoCallout.tsx | 28 +- .../design/src/ui/callout/SuccessCallout.tsx | 28 +- packages/design/src/ui/callout/index.tsx | 75 +- packages/design/src/ui/card.tsx | 111 +- packages/design/src/ui/checkbox-card.tsx | 87 +- packages/design/src/ui/checkbox.tsx | 40 +- packages/design/src/ui/collapsible.tsx | 40 +- packages/design/src/ui/color-pill.tsx | 25 +- packages/design/src/ui/daypicker.tsx | 93 +- packages/design/src/ui/dialog.tsx | 151 +- packages/design/src/ui/dropdown-menu.tsx | 316 +-- packages/design/src/ui/dropzone.tsx | 124 +- packages/design/src/ui/form.tsx | 283 +-- .../design/src/ui/icons/IconAccountPin.tsx | 15 +- .../design/src/ui/icons/IconArrowDown.tsx | 36 +- .../src/ui/icons/IconArrowDownRight.tsx | 34 +- .../design/src/ui/icons/IconArrowRight.tsx | 30 +- .../design/src/ui/icons/IconArrowRight2.tsx | 34 +- packages/design/src/ui/icons/IconArrowUp.tsx | 34 +- .../design/src/ui/icons/IconBookMarked.tsx | 34 +- packages/design/src/ui/icons/IconBookText.tsx | 36 +- packages/design/src/ui/icons/IconBookUser.tsx | 36 +- packages/design/src/ui/icons/IconCalendar.tsx | 38 +- .../design/src/ui/icons/IconCalendarX.tsx | 38 +- .../design/src/ui/icons/IconCaretSort.tsx | 28 +- packages/design/src/ui/icons/IconCheck.tsx | 30 +- .../design/src/ui/icons/IconChevronDown.tsx | 24 +- .../design/src/ui/icons/IconChevronLeft.tsx | 32 +- .../design/src/ui/icons/IconChevronRight.tsx | 24 +- .../design/src/ui/icons/IconChevronsLeft.tsx | 34 +- .../design/src/ui/icons/IconChevronsRight.tsx | 34 +- packages/design/src/ui/icons/IconCircle.tsx | 34 +- packages/design/src/ui/icons/IconColumns2.tsx | 34 +- .../src/ui/icons/IconCursorOnSquareDashed.tsx | 50 +- packages/design/src/ui/icons/IconDotsH.tsx | 16 +- packages/design/src/ui/icons/IconDownload.tsx | 36 +- packages/design/src/ui/icons/IconEyeNone.tsx | 34 +- packages/design/src/ui/icons/IconFileDown.tsx | 38 +- packages/design/src/ui/icons/IconFlagFR.tsx | 14 +- packages/design/src/ui/icons/IconFlagGB.tsx | 30 +- packages/design/src/ui/icons/IconGoogle.tsx | 16 +- .../design/src/ui/icons/IconGripVertical.tsx | 42 +- .../design/src/ui/icons/IconGripVertical2.tsx | 10 +- packages/design/src/ui/icons/IconHome.tsx | 16 +- .../design/src/ui/icons/IconInfoCircle.tsx | 38 +- .../src/ui/icons/IconInfoCircleFill.tsx | 16 +- packages/design/src/ui/icons/IconLink.tsx | 16 +- .../design/src/ui/icons/IconListOrdered.tsx | 30 +- packages/design/src/ui/icons/IconLock.tsx | 36 +- packages/design/src/ui/icons/IconLogout.tsx | 36 +- packages/design/src/ui/icons/IconMail.tsx | 34 +- .../design/src/ui/icons/IconMailSearch.tsx | 44 +- .../design/src/ui/icons/IconMicrosoft.tsx | 16 +- .../src/ui/icons/IconMoreHorizontal.tsx | 36 +- .../design/src/ui/icons/IconOpenBookText.tsx | 42 +- .../design/src/ui/icons/IconPaintBrush.tsx | 36 +- .../src/ui/icons/IconPanelLeftClose.tsx | 36 +- .../design/src/ui/icons/IconPanelLeftOpen.tsx | 36 +- packages/design/src/ui/icons/IconPedaki.tsx | 143 +- packages/design/src/ui/icons/IconPencil.tsx | 34 +- packages/design/src/ui/icons/IconPlus.tsx | 36 +- .../src/ui/icons/IconQuestionCircleFill.tsx | 16 +- packages/design/src/ui/icons/IconSchool.tsx | 44 +- packages/design/src/ui/icons/IconSearch.tsx | 30 +- packages/design/src/ui/icons/IconSettings.tsx | 34 +- .../design/src/ui/icons/IconSettings2.tsx | 38 +- .../design/src/ui/icons/IconShoppingBag.tsx | 36 +- packages/design/src/ui/icons/IconSpinner.tsx | 28 +- packages/design/src/ui/icons/IconTada.tsx | 52 +- .../design/src/ui/icons/IconTadaColor.tsx | 75 +- .../design/src/ui/icons/IconTranslation.tsx | 38 +- packages/design/src/ui/icons/IconTrash.tsx | 40 +- packages/design/src/ui/icons/IconUpload.tsx | 36 +- packages/design/src/ui/icons/IconUser.tsx | 16 +- .../design/src/ui/icons/IconUserGroups.tsx | 36 +- packages/design/src/ui/icons/IconUserPlus.tsx | 38 +- packages/design/src/ui/icons/IconWand2.tsx | 46 +- packages/design/src/ui/icons/IconX.tsx | 24 +- packages/design/src/ui/icons/index.ts | 130 +- packages/design/src/ui/icons/type.ts | 2 +- packages/design/src/ui/input.tsx | 50 +- packages/design/src/ui/label.tsx | 25 +- packages/design/src/ui/navigation-menu.tsx | 172 +- packages/design/src/ui/pagination.tsx | 230 +- packages/design/src/ui/popover.tsx | 34 +- packages/design/src/ui/progress.tsx | 45 +- packages/design/src/ui/resizable.tsx | 57 +- packages/design/src/ui/scroll-area.tsx | 78 +- packages/design/src/ui/select.tsx | 223 +- packages/design/src/ui/separator.tsx | 80 +- packages/design/src/ui/sheet.tsx | 183 +- packages/design/src/ui/skeleton.tsx | 21 +- packages/design/src/ui/slider.tsx | 35 +- packages/design/src/ui/styled-link.tsx | 111 +- packages/design/src/ui/switch.tsx | 40 +- packages/design/src/ui/table.tsx | 147 +- packages/design/src/ui/tooltip.tsx | 49 +- packages/design/src/utils/cn.ts | 78 +- packages/design/src/utils/index.ts | 2 +- packages/design/tsconfig.json | 68 +- packages/design/tsup.config.ts | 73 +- packages/eslint-config/.npmignore | 2 - packages/eslint-config/.prettierignore | 5 - packages/eslint-config/README.md | 1 - packages/eslint-config/index.js | 49 - packages/eslint-config/package.json | 36 - packages/mailer/.eslintignore | 2 - packages/mailer/.eslintrc.cjs | 6 - packages/mailer/.prettierignore | 5 - packages/mailer/package.json | 99 +- packages/mailer/src/index.ts | 4 +- packages/mailer/src/send-email.tsx | 80 +- packages/mailer/src/type.ts | 6 +- packages/mailer/tsconfig.json | 75 +- packages/mailer/tsup.config.ts | 59 +- packages/prettier-config/.npmignore | 2 - packages/prettier-config/.prettierignore | 4 - packages/prettier-config/README.md | 1 - packages/prettier-config/index.js | 15 - packages/prettier-config/package.json | 29 - pnpm-lock.yaml | 2023 +---------------- 174 files changed, 4932 insertions(+), 6514 deletions(-) create mode 100644 biome.json delete mode 100644 packages/common/.eslintignore delete mode 100644 packages/common/.eslintrc.cjs delete mode 100644 packages/common/.prettierignore delete mode 100644 packages/eslint-config/.npmignore delete mode 100644 packages/eslint-config/.prettierignore delete mode 100644 packages/eslint-config/README.md delete mode 100644 packages/eslint-config/index.js delete mode 100644 packages/eslint-config/package.json delete mode 100644 packages/mailer/.eslintignore delete mode 100644 packages/mailer/.eslintrc.cjs delete mode 100644 packages/mailer/.prettierignore delete mode 100644 packages/prettier-config/.npmignore delete mode 100644 packages/prettier-config/.prettierignore delete mode 100644 packages/prettier-config/README.md delete mode 100644 packages/prettier-config/index.js delete mode 100644 packages/prettier-config/package.json diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml index 0649d7e..99466f2 100644 --- a/.github/workflows/code-check.yml +++ b/.github/workflows/code-check.yml @@ -36,10 +36,10 @@ jobs: - name: Install deps run: pnpm install --frozen-lockfile --prefer-offline - - name: Run eslint + - name: Run lint run: pnpm lint - - name: Run prettier + - name: Run format check run: pnpm format:check - name: Run typescript diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..20946a8 --- /dev/null +++ b/biome.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.7.0/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "style": { + "noNonNullAssertion": "off", + "useSelfClosingElements": "off" + }, + "a11y": { + "noSvgWithoutTitle": "off" + } + } + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignore": [ + "./dist/**", + "./node_modules/**", + "./.next/**", + "./.react-email/**" + ] + } +} diff --git a/docs/package.json b/docs/package.json index 8daca4a..cdbf1f0 100644 --- a/docs/package.json +++ b/docs/package.json @@ -22,9 +22,5 @@ }, "dependencies": { "mintlify": "latest" - }, - "devDependencies": { - "@pedaki/prettier-config": "0.5.3" - }, - "prettier": "@pedaki/prettier-config" -} \ No newline at end of file + } +} diff --git a/package.json b/package.json index b6cc1c6..2d7df73 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "publish-release": "node --no-warnings=ExperimentalWarning scripts/publish-release.mjs" }, "devDependencies": { + "@biomejs/biome": "^1.7.0", "@manypkg/get-packages": "^2.2.1", "@types/fs-extra": "^11.0.4", "@types/node": "^20.12.7", @@ -52,12 +53,6 @@ "typescript": "5.4.5", "vitest": "^1.5.0" }, - "lint-staged": { - "*.{js,jsx,ts,tsx}": [ - "prettier --write", - "eslint --fix" - ] - }, "engines": { "node": "^18 || ^20", "pnpm": ">=8.0.0", @@ -65,4 +60,4 @@ "npm": "use-pnpm" }, "packageManager": "pnpm@9.0.4" -} \ No newline at end of file +} diff --git a/packages/common/.eslintignore b/packages/common/.eslintignore deleted file mode 100644 index 534c988..0000000 --- a/packages/common/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -.eslintrc.cjs -tsup.config.ts diff --git a/packages/common/.eslintrc.cjs b/packages/common/.eslintrc.cjs deleted file mode 100644 index 171501c..0000000 --- a/packages/common/.eslintrc.cjs +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - extends: ['@pedaki/eslint-config'], - parserOptions: { - project: true, - }, - ignorePatterns: ['vitest.config.js'], -}; diff --git a/packages/common/.prettierignore b/packages/common/.prettierignore deleted file mode 100644 index 4845023..0000000 --- a/packages/common/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -*.md -package.json -coverage diff --git a/packages/common/package.json b/packages/common/package.json index 6b86df2..cb13c36 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,45 +1,42 @@ { - "name": "@pedaki/common", - "version": "0.5.3", - "author": "Nathan David ", - "repository": { - "type": "git", - "url": "https://github.com/PedakiHQ/pedaki", - "directory": "packages/common" - }, - "homepage": "https://www.pedaki.fr", - "license": "CC-BY-NC-SA-4.0", - "private": false, - "type": "module", - "publishConfig": { - "directory": "dist" - }, - "scripts": { - "build": "tsup", - "lint": "eslint .", - "typecheck": "tsc --noEmit", - "format": "prettier --write .", - "format:check": "prettier --check ." - }, - "devDependencies": { - "@pedaki/eslint-config": "0.5.3", - "@pedaki/prettier-config": "0.5.3", - "@types/bcryptjs": "^2.4.6", - "@types/memory-cache": "latest", - "@types/react": "^18.2.79", - "react": "^18.2.0" - }, - "peerDependencies": { - "bcryptjs": "^2.4.3", - "memory-cache": "^0.2.0", - "nanoid": "^5.0.0", - "sonner": "^1.0.0" - }, - "engines": { - "node": "^18 || ^20", - "pnpm": ">=9.0.0", - "yarn": "use-pnpm", - "npm": "use-pnpm" - }, - "prettier": "@pedaki/prettier-config" -} \ No newline at end of file + "name": "@pedaki/common", + "version": "0.5.3", + "author": "Nathan David ", + "repository": { + "type": "git", + "url": "https://github.com/PedakiHQ/pedaki", + "directory": "packages/common" + }, + "homepage": "https://www.pedaki.fr", + "license": "CC-BY-NC-SA-4.0", + "private": false, + "type": "module", + "publishConfig": { + "directory": "dist" + }, + "scripts": { + "build": "tsup", + "format": "pnpx @biomejs/biome format ./ --write", + "lint": "pnpx @biomejs/biome lint ./ --apply", + "format:check": "pnpx @biomejs/biome format ./", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.6", + "@types/memory-cache": "latest", + "@types/react": "^18.2.79", + "react": "^18.2.0" + }, + "peerDependencies": { + "bcryptjs": "^2.4.3", + "memory-cache": "^0.2.0", + "nanoid": "^5.0.0", + "sonner": "^1.0.0" + }, + "engines": { + "node": "^18 || ^20", + "pnpm": ">=9.0.0", + "yarn": "use-pnpm", + "npm": "use-pnpm" + } +} diff --git a/packages/common/src/cache/cache.test.ts b/packages/common/src/cache/cache.test.ts index fedb5a2..35f4048 100644 --- a/packages/common/src/cache/cache.test.ts +++ b/packages/common/src/cache/cache.test.ts @@ -1,114 +1,120 @@ -import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; -import { cache, getCache, isStale, revalidate, revalidateAll } from './cache.ts'; +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { + cache, + getCache, + isStale, + revalidate, + revalidateAll, +} from "./cache.ts"; -describe('cache - memory', () => { - const value = 'test'; - const asyncFn = () => Promise.resolve(value); - const syncFn = () => value; - const key = 'key'; - const mock = vi.fn().mockImplementation(asyncFn); +describe("cache - memory", () => { + const value = "test"; + const asyncFn = () => Promise.resolve(value); + const syncFn = () => value; + const key = "key"; + const mock = vi.fn().mockImplementation(asyncFn); - beforeEach(() => { - revalidateAll(); - }); + beforeEach(() => { + revalidateAll(); + }); - afterEach(() => { - vi.restoreAllMocks(); - }); + afterEach(() => { + vi.restoreAllMocks(); + }); - test('cache a value (async)', async () => { - const result = await cache(asyncFn, key); - expect(result).toBe(value); - }); + test("cache a value (async)", async () => { + const result = await cache(asyncFn, key); + expect(result).toBe(value); + }); - test('cache a value (sync)', async () => { - const result = await cache(syncFn, key); - expect(result).toBe(value); - }); + test("cache a value (sync)", async () => { + const result = await cache(syncFn, key); + expect(result).toBe(value); + }); - describe('with ttl', () => { - test('without keepStale', async () => { - await cache(value, key, { ttl: 1000, keepStale: false }); + describe("with ttl", () => { + test("without keepStale", async () => { + await cache(value, key, { ttl: 1000, keepStale: false }); - let cached = await getCache(key); - expect(isStale(cached!, 1000)).toBe(false); + let cached = await getCache(key); + expect(isStale(cached!, 1000)).toBe(false); - vi.useFakeTimers(); - vi.advanceTimersByTime(10000); + vi.useFakeTimers(); + vi.advanceTimersByTime(10000); - cached = await getCache(key); - expect(cached).toBeNull(); - }); + cached = await getCache(key); + expect(cached).toBeNull(); + }); - test('with keepStale', async () => { - await cache(value, key, { ttl: 1000, keepStale: true }); + test("with keepStale", async () => { + await cache(value, key, { ttl: 1000, keepStale: true }); - let cached = await getCache(key); - expect(isStale(cached!, 1000)).toBe(false); + let cached = await getCache(key); + expect(isStale(cached!, 1000)).toBe(false); - vi.useFakeTimers(); - vi.advanceTimersByTime(10000); + vi.useFakeTimers(); + vi.advanceTimersByTime(10000); - cached = await getCache(key); - expect(cached).not.toBeNull(); + cached = await getCache(key); + expect(cached).not.toBeNull(); - expect(isStale(cached!, 1000)).toBe(true); - }); - }); + expect(isStale(cached!, 1000)).toBe(true); + }); + }); - describe('ignoreCache', () => { - test('without ignoreCache', async () => { - await cache(mock, key, { ignoreCache: false }); - await cache(mock, key, { ignoreCache: false }); + describe("ignoreCache", () => { + test("without ignoreCache", async () => { + await cache(mock, key, { ignoreCache: false }); + await cache(mock, key, { ignoreCache: false }); - expect(mock).toHaveBeenCalledTimes(1); - }); + expect(mock).toHaveBeenCalledTimes(1); + }); - test('with ignoreCache', async () => { - await cache(mock, key, { ignoreCache: true }); - await cache(mock, key, { ignoreCache: true }); + test("with ignoreCache", async () => { + await cache(mock, key, { ignoreCache: true }); + await cache(mock, key, { ignoreCache: true }); - expect(mock).toHaveBeenCalledTimes(2); - }); - }); + expect(mock).toHaveBeenCalledTimes(2); + }); + }); - describe('revalidate', () => { - test('revalidate a key', async () => { - await cache(mock, key); - await cache(mock, key); + describe("revalidate", () => { + test("revalidate a key", async () => { + await cache(mock, key); + await cache(mock, key); - expect(mock).toHaveBeenCalledTimes(1); + expect(mock).toHaveBeenCalledTimes(1); - const before = await getCache(key); - expect(before).not.toBeNull(); + const before = await getCache(key); + expect(before).not.toBeNull(); - revalidate(key); + revalidate(key); - const after = await getCache(key); - expect(after).toBeNull(); + const after = await getCache(key); + expect(after).toBeNull(); - await cache(mock, key); + await cache(mock, key); - expect(mock).toHaveBeenCalledTimes(2); - }); + expect(mock).toHaveBeenCalledTimes(2); + }); - test('revalidate all keys', async () => { - await cache(mock, key); - await cache(mock, key); + test("revalidate all keys", async () => { + await cache(mock, key); + await cache(mock, key); - expect(mock).toHaveBeenCalledTimes(1); + expect(mock).toHaveBeenCalledTimes(1); - const before = await getCache(key); - expect(before).not.toBeNull(); + const before = await getCache(key); + expect(before).not.toBeNull(); - revalidateAll(); + revalidateAll(); - const after = await getCache(key); - expect(after).toBeNull(); + const after = await getCache(key); + expect(after).toBeNull(); - await cache(mock, key); + await cache(mock, key); - expect(mock).toHaveBeenCalledTimes(2); - }); - }); + expect(mock).toHaveBeenCalledTimes(2); + }); + }); }); diff --git a/packages/common/src/cache/cache.ts b/packages/common/src/cache/cache.ts index 02cc4c6..c28e7af 100644 --- a/packages/common/src/cache/cache.ts +++ b/packages/common/src/cache/cache.ts @@ -1,51 +1,53 @@ import { - cache as memoryCache, - getCache as memoryGetCache, - revalidate as memoryRevalidate, - revalidateAll as memoryRevalidateAll, -} from './memory-cache.ts'; -import type { CachedData, CacheOptions } from './types.ts'; + cache as memoryCache, + getCache as memoryGetCache, + revalidate as memoryRevalidate, + revalidateAll as memoryRevalidateAll, +} from "./memory-cache.ts"; +import type { CachedData, CacheOptions } from "./types.ts"; export const cache = async ( - fn: (() => Promise) | T, - key: string, - options?: CacheOptions, + fn: (() => Promise) | T, + key: string, + options?: CacheOptions, ): Promise => { - if (!options?.type || options.type === 'memory') { - return memoryCache(fn, key, options); - } + if (!options?.type || options.type === "memory") { + return memoryCache(fn, key, options); + } - throw new Error('Invalid cache type'); + throw new Error("Invalid cache type"); }; -export const revalidate = (key: string, options?: Pick) => { - if (!options?.type || options.type === 'memory') { - return memoryRevalidate(key); - } +export const revalidate = ( + key: string, + options?: Pick, +) => { + if (!options?.type || options.type === "memory") { + return memoryRevalidate(key); + } - throw new Error('Invalid cache type'); + throw new Error("Invalid cache type"); }; -export const revalidateAll = (options?: Pick) => { - if (!options?.type || options.type === 'memory') { - return memoryRevalidateAll(); - } +export const revalidateAll = (options?: Pick) => { + if (!options?.type || options.type === "memory") { + return memoryRevalidateAll(); + } - throw new Error('Invalid cache type'); + throw new Error("Invalid cache type"); }; export const getCache = async ( - key: string, - options?: Pick, - // eslint-disable-next-line @typescript-eslint/require-await + key: string, + options?: Pick, ): Promise | null> => { - if (!options?.type || options.type === 'memory') { - return memoryGetCache(key); - } + if (!options?.type || options.type === "memory") { + return memoryGetCache(key); + } - throw new Error('Invalid cache type'); + throw new Error("Invalid cache type"); }; export const isStale = (data: CachedData, ttl: number): boolean => { - return Date.now() - data.createdAt > ttl; + return Date.now() - data.createdAt > ttl; }; diff --git a/packages/common/src/cache/index.ts b/packages/common/src/cache/index.ts index ba0d584..2885e54 100644 --- a/packages/common/src/cache/index.ts +++ b/packages/common/src/cache/index.ts @@ -1 +1 @@ -export * from './cache.ts'; +export * from "./cache.ts"; diff --git a/packages/common/src/cache/memory-cache.ts b/packages/common/src/cache/memory-cache.ts index fc10c4c..79b5c44 100644 --- a/packages/common/src/cache/memory-cache.ts +++ b/packages/common/src/cache/memory-cache.ts @@ -1,44 +1,48 @@ -import cacheData from 'memory-cache'; -import { formatData } from './shared.ts'; -import type { CachedData, MemoryCacheOptions } from './types.ts'; +import cacheData from "memory-cache"; +import { formatData } from "./shared.ts"; +import type { CachedData, MemoryCacheOptions } from "./types.ts"; export const cache = async ( - fn: (() => Promise) | T, - key: string, - options?: MemoryCacheOptions, + fn: (() => Promise) | T, + key: string, + options?: MemoryCacheOptions, ): Promise => { - const value = getCache(key); - const hit = value !== null && value !== undefined; - - if (!options?.ignoreCache && hit) { - // Check if data is still valid - if (!options?.ttl || Date.now() - value.createdAt <= options.ttl) { - return value.data; - } - } - - try { - const newValue = fn instanceof Function ? await fn() : fn; - - cacheData.put(key, formatData(newValue), options?.keepStale ? undefined : options?.ttl); - return newValue; - } catch (e) { - if (hit && options?.keepStale) { - return value.data; - } - - throw e; - } + const value = getCache(key); + const hit = value !== null && value !== undefined; + + if (!options?.ignoreCache && hit) { + // Check if data is still valid + if (!options?.ttl || Date.now() - value.createdAt <= options.ttl) { + return value.data; + } + } + + try { + const newValue = fn instanceof Function ? await fn() : fn; + + cacheData.put( + key, + formatData(newValue), + options?.keepStale ? undefined : options?.ttl, + ); + return newValue; + } catch (e) { + if (hit && options?.keepStale) { + return value.data; + } + + throw e; + } }; export const revalidate = (key: string) => { - cacheData.del(key); + cacheData.del(key); }; export const revalidateAll = () => { - cacheData.clear(); + cacheData.clear(); }; export const getCache = (key: string): CachedData | null => { - return cacheData.get(key) as CachedData | null; + return cacheData.get(key) as CachedData | null; }; diff --git a/packages/common/src/cache/shared.ts b/packages/common/src/cache/shared.ts index 570d278..07bdec5 100644 --- a/packages/common/src/cache/shared.ts +++ b/packages/common/src/cache/shared.ts @@ -1,8 +1,8 @@ -import type { CachedData } from './types.ts'; +import type { CachedData } from "./types.ts"; export const formatData = (data: T) => { - return { - data, - createdAt: Date.now(), - } as CachedData; + return { + data, + createdAt: Date.now(), + } as CachedData; }; diff --git a/packages/common/src/cache/types.ts b/packages/common/src/cache/types.ts index aca2e0c..d669df0 100644 --- a/packages/common/src/cache/types.ts +++ b/packages/common/src/cache/types.ts @@ -1,19 +1,19 @@ /* c8 ignore start */ -export type CacheType = 'memory'; +export type CacheType = "memory"; export interface CacheOptions { - ignoreCache?: boolean; - ttl?: number; - keepStale?: boolean; - // Default: memory - type?: CacheType; + ignoreCache?: boolean; + ttl?: number; + keepStale?: boolean; + // Default: memory + type?: CacheType; } -export interface MemoryCacheOptions extends Omit { - type?: 'memory'; +export interface MemoryCacheOptions extends Omit { + type?: "memory"; } export interface CachedData { - data: T; - createdAt: number; + data: T; + createdAt: number; } diff --git a/packages/common/src/hooks/index.ts b/packages/common/src/hooks/index.ts index 0c4f1ce..f90c2a6 100644 --- a/packages/common/src/hooks/index.ts +++ b/packages/common/src/hooks/index.ts @@ -1,7 +1,7 @@ -export * from './useEventListener.ts'; -export * from './useIsomorphicLayoutEffect.ts'; -export * from './useMediaQuery.ts'; -export * from './useOnClickOutside.ts'; -export * from './useScrollLock.ts'; -export * from './useBeforeUnload.ts'; -export * from './util.ts'; +export * from "./useEventListener.ts"; +export * from "./useIsomorphicLayoutEffect.ts"; +export * from "./useMediaQuery.ts"; +export * from "./useOnClickOutside.ts"; +export * from "./useScrollLock.ts"; +export * from "./useBeforeUnload.ts"; +export * from "./util.ts"; diff --git a/packages/common/src/hooks/useBeforeUnload.ts b/packages/common/src/hooks/useBeforeUnload.ts index 109823d..0fcfc76 100644 --- a/packages/common/src/hooks/useBeforeUnload.ts +++ b/packages/common/src/hooks/useBeforeUnload.ts @@ -1,37 +1,40 @@ // https://github.com/streamich/react-use/blob/e27c1930da8b94a2c570954786aa68a3dc5624be/src/useBeforeUnload.ts -import { useCallback, useEffect } from 'react'; -import { off, on } from './util.ts'; +import { useCallback, useEffect } from "react"; +import { off, on } from "./util.ts"; -const useBeforeUnload = (enabled: boolean | (() => boolean) = true, message?: string) => { - const handler = useCallback( - (event: BeforeUnloadEvent) => { - const finalEnabled = typeof enabled === 'function' ? enabled() : true; +const useBeforeUnload = ( + enabled: boolean | (() => boolean) = true, + message?: string, +) => { + const handler = useCallback( + (event: BeforeUnloadEvent) => { + const finalEnabled = typeof enabled === "function" ? enabled() : true; - if (!finalEnabled) { - return; - } + if (!finalEnabled) { + return; + } - event.preventDefault(); + event.preventDefault(); - if (message) { - event.returnValue = message; - } + if (message) { + event.returnValue = message; + } - return message; - }, - [enabled, message], - ); + return message; + }, + [enabled, message], + ); - useEffect(() => { - if (!enabled) { - return; - } + useEffect(() => { + if (!enabled) { + return; + } - on(window, 'beforeunload', handler); + on(window, "beforeunload", handler); - return () => off(window, 'beforeunload', handler); - }, [enabled, handler]); + return () => off(window, "beforeunload", handler); + }, [enabled, handler]); }; export default useBeforeUnload; diff --git a/packages/common/src/hooks/useEventListener.ts b/packages/common/src/hooks/useEventListener.ts index 21d38d1..dede578 100644 --- a/packages/common/src/hooks/useEventListener.ts +++ b/packages/common/src/hooks/useEventListener.ts @@ -1,80 +1,84 @@ // https://github.com/juliencrn/usehooks-ts/tree/master -import { useEffect, useRef } from 'react'; -import type { RefObject } from 'react'; -import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect.ts'; +import { useEffect, useRef } from "react"; +import type { RefObject } from "react"; +import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.ts"; // MediaQueryList Event based useEventListener interface function useEventListener( - eventName: K, - handler: (event: MediaQueryListEventMap[K]) => void, - element: RefObject, - options?: boolean | AddEventListenerOptions, + eventName: K, + handler: (event: MediaQueryListEventMap[K]) => void, + element: RefObject, + options?: boolean | AddEventListenerOptions, ): void; // Window Event based useEventListener interface function useEventListener( - eventName: K, - handler: (event: WindowEventMap[K]) => void, - element?: undefined, - options?: boolean | AddEventListenerOptions, + eventName: K, + handler: (event: WindowEventMap[K]) => void, + element?: undefined, + options?: boolean | AddEventListenerOptions, ): void; // Element Event based useEventListener interface function useEventListener< - K extends keyof HTMLElementEventMap, - T extends HTMLElement = HTMLDivElement, + K extends keyof HTMLElementEventMap, + T extends HTMLElement = HTMLDivElement, >( - eventName: K, - handler: (event: HTMLElementEventMap[K]) => void, - element: RefObject, - options?: boolean | AddEventListenerOptions, + eventName: K, + handler: (event: HTMLElementEventMap[K]) => void, + element: RefObject, + options?: boolean | AddEventListenerOptions, ): void; // Document Event based useEventListener interface function useEventListener( - eventName: K, - handler: (event: DocumentEventMap[K]) => void, - element: RefObject, - options?: boolean | AddEventListenerOptions, + eventName: K, + handler: (event: DocumentEventMap[K]) => void, + element: RefObject, + options?: boolean | AddEventListenerOptions, ): void; function useEventListener< - KW extends keyof WindowEventMap, - KH extends keyof HTMLElementEventMap, - KM extends keyof MediaQueryListEventMap, - T extends HTMLElement | MediaQueryList | void = void, + KW extends keyof WindowEventMap, + KH extends keyof HTMLElementEventMap, + KM extends keyof MediaQueryListEventMap, + T extends HTMLElement | MediaQueryList, >( - eventName: KW | KH | KM, - handler: ( - event: WindowEventMap[KW] | HTMLElementEventMap[KH] | MediaQueryListEventMap[KM] | Event, - ) => void, - element?: RefObject, - options?: boolean | AddEventListenerOptions, + eventName: KW | KH | KM, + handler: ( + event: + | WindowEventMap[KW] + | HTMLElementEventMap[KH] + | MediaQueryListEventMap[KM] + | Event, + ) => void, + element?: RefObject, + options?: boolean | AddEventListenerOptions, ) { - // Create a ref that stores handler - const savedHandler = useRef(handler); + // Create a ref that stores handler + const savedHandler = useRef(handler); - useIsomorphicLayoutEffect(() => { - savedHandler.current = handler; - }, [handler]); + useIsomorphicLayoutEffect(() => { + savedHandler.current = handler; + }, [handler]); - useEffect(() => { - // Define the listening target - const targetElement: T | Window = element?.current ?? window; + useEffect(() => { + // Define the listening target + const targetElement: T | Window = element?.current ?? window; - if (!(targetElement && targetElement.addEventListener)) return; + if (!targetElement?.addEventListener) return; - // Create event listener that calls handler function stored in ref - const listener: typeof handler = event => savedHandler.current(event); + // Create event listener that calls handler function stored in ref + const listener: typeof handler = (event) => savedHandler.current(event); - targetElement.addEventListener(eventName, listener, options); + targetElement.addEventListener(eventName, listener, options); - // Remove event listener on cleanup - return () => { - targetElement.removeEventListener(eventName, listener, options); - }; - }, [eventName, element, options]); + // Remove event listener on cleanup + return () => { + targetElement.removeEventListener(eventName, listener, options); + }; + }, [eventName, element, options]); } export { useEventListener }; diff --git a/packages/common/src/hooks/useIsomorphicLayoutEffect.ts b/packages/common/src/hooks/useIsomorphicLayoutEffect.ts index ebee7f2..bf0bda0 100644 --- a/packages/common/src/hooks/useIsomorphicLayoutEffect.ts +++ b/packages/common/src/hooks/useIsomorphicLayoutEffect.ts @@ -1,4 +1,4 @@ -import { useEffect, useLayoutEffect } from 'react'; +import { useEffect, useLayoutEffect } from "react"; export const useIsomorphicLayoutEffect = - typeof window !== 'undefined' ? useLayoutEffect : useEffect; + typeof window !== "undefined" ? useLayoutEffect : useEffect; diff --git a/packages/common/src/hooks/useMediaQuery.ts b/packages/common/src/hooks/useMediaQuery.ts index 638f852..1c84964 100644 --- a/packages/common/src/hooks/useMediaQuery.ts +++ b/packages/common/src/hooks/useMediaQuery.ts @@ -1,44 +1,44 @@ // https://github.com/juliencrn/usehooks-ts/tree/master -import { useEffect, useState } from 'react'; +import { useEffect, useState } from "react"; export function useMediaQuery(query: string): boolean { - const getMatches = (query: string): boolean => { - // Prevents SSR issues - if (typeof window !== 'undefined') { - return window.matchMedia(query).matches; - } - return false; - }; - - const [matches, setMatches] = useState(getMatches(query)); - - function handleChange() { - setMatches(getMatches(query)); - } - - useEffect(() => { - const matchMedia = window.matchMedia(query); - - // Triggered at the first client-side load and if query changes - handleChange(); - - // Listen matchMedia - if (matchMedia.addListener) { - matchMedia.addListener(handleChange); - } else { - matchMedia.addEventListener('change', handleChange); - } - - return () => { - if (matchMedia.removeListener) { - matchMedia.removeListener(handleChange); - } else { - matchMedia.removeEventListener('change', handleChange); - } - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [query]); - - return matches; + const getMatches = (query: string): boolean => { + // Prevents SSR issues + if (typeof window !== "undefined") { + return window.matchMedia(query).matches; + } + return false; + }; + + const [matches, setMatches] = useState(getMatches(query)); + + function handleChange() { + setMatches(getMatches(query)); + } + + // biome-ignore lint/correctness/useExhaustiveDependencies: + useEffect(() => { + const matchMedia = window.matchMedia(query); + + // Triggered at the first client-side load and if query changes + handleChange(); + + // Listen matchMedia + if (matchMedia.addListener) { + matchMedia.addListener(handleChange); + } else { + matchMedia.addEventListener("change", handleChange); + } + + return () => { + if (matchMedia.removeListener) { + matchMedia.removeListener(handleChange); + } else { + matchMedia.removeEventListener("change", handleChange); + } + }; + }, [query]); + + return matches; } diff --git a/packages/common/src/hooks/useOnClickOutside.ts b/packages/common/src/hooks/useOnClickOutside.ts index 1b097b2..efb596b 100644 --- a/packages/common/src/hooks/useOnClickOutside.ts +++ b/packages/common/src/hooks/useOnClickOutside.ts @@ -1,23 +1,23 @@ // https://github.com/juliencrn/usehooks-ts/tree/master -import type { RefObject } from 'react'; -import { useEventListener } from './useEventListener.ts'; +import type { RefObject } from "react"; +import { useEventListener } from "./useEventListener.ts"; type Handler = (event: MouseEvent) => void; export function useOnClickOutside( - ref: RefObject, - handler: Handler, - mouseEvent: 'mousedown' | 'mouseup' = 'mousedown', + ref: RefObject, + handler: Handler, + mouseEvent: "mousedown" | "mouseup" = "mousedown", ): void { - useEventListener(mouseEvent, event => { - const el = ref?.current; + useEventListener(mouseEvent, (event) => { + const el = ref?.current; - // Do nothing if clicking ref's element or descendent elements - if (!el || el.contains(event.target as Node)) { - return; - } + // Do nothing if clicking ref's element or descendent elements + if (!el || el.contains(event.target as Node)) { + return; + } - handler(event); - }); + handler(event); + }); } diff --git a/packages/common/src/hooks/useScrollLock.ts b/packages/common/src/hooks/useScrollLock.ts index a4f353d..b789c23 100644 --- a/packages/common/src/hooks/useScrollLock.ts +++ b/packages/common/src/hooks/useScrollLock.ts @@ -1,15 +1,15 @@ -import { useEffect } from 'react'; +import { useEffect } from "react"; const useScrollLock = (isLocked: boolean) => { - useEffect(() => { - if (isLocked) { - document.body.classList.add('lock-scroll'); - } else { - document.body.classList.remove('lock-scroll'); - } - }, [isLocked]); + useEffect(() => { + if (isLocked) { + document.body.classList.add("lock-scroll"); + } else { + document.body.classList.remove("lock-scroll"); + } + }, [isLocked]); - return null; + return null; }; export { useScrollLock }; diff --git a/packages/common/src/hooks/util.ts b/packages/common/src/hooks/util.ts index 34fa42c..64ac108 100644 --- a/packages/common/src/hooks/util.ts +++ b/packages/common/src/hooks/util.ts @@ -1,23 +1,33 @@ export const noop = () => {}; export function on( - obj: T | null, - ...args: Parameters | [string, Function | null, ...any] + obj: T | null, + // biome-ignore lint/suspicious/noExplicitAny: + // biome-ignore lint/complexity/noBannedTypes: + ...args: Parameters | [string, Function | null, ...any] ): void { - if (obj && obj.addEventListener) { - obj.addEventListener(...(args as Parameters)); - } + if (obj?.addEventListener) { + obj.addEventListener( + ...(args as Parameters), + ); + } } export function off( - obj: T | null, - ...args: Parameters | [string, Function | null, ...any] + obj: T | null, + ...args: + | Parameters + // biome-ignore lint/suspicious/noExplicitAny: + // biome-ignore lint/complexity/noBannedTypes: + | [string, Function | null, ...any] ): void { - if (obj && obj.removeEventListener) { - obj.removeEventListener(...(args as Parameters)); - } + if (obj?.removeEventListener) { + obj.removeEventListener( + ...(args as Parameters), + ); + } } -export const isBrowser = typeof window !== 'undefined'; +export const isBrowser = typeof window !== "undefined"; -export const isNavigator = typeof navigator !== 'undefined'; +export const isNavigator = typeof navigator !== "undefined"; diff --git a/packages/common/src/utils/circle-gradient.ts b/packages/common/src/utils/circle-gradient.ts index a836f48..320060f 100644 --- a/packages/common/src/utils/circle-gradient.ts +++ b/packages/common/src/utils/circle-gradient.ts @@ -2,22 +2,22 @@ // Based on https://github.com/coffee-cup/gradient-avatars/blob/main/src/gradients.ts -import { hsl } from './colors.ts'; +import { hsl } from "./colors.ts"; const randomHsl = (): string => { - const [hue, saturation, lightness] = hsl(); - return `hsl(${hue}, ${saturation}%, ${lightness}%)`; + const [hue, saturation, lightness] = hsl(); + return `hsl(${hue}, ${saturation}%, ${lightness}%)`; }; const generateColours = (): [string, string] => { - return [randomHsl(), randomHsl()]; + return [randomHsl(), randomHsl()]; }; export const generateSVG = (size = 256): string => { - const [c1, c2] = generateColours(); - const rotate = Math.floor(Math.random() * 360); + const [c1, c2] = generateColours(); + const rotate = Math.floor(Math.random() * 360); - return ` + return ` @@ -31,6 +31,6 @@ export const generateSVG = (size = 256): string => { }; export const generateDataURL = (size = 256): string => { - const svg = generateSVG(size); - return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`; + const svg = generateSVG(size); + return `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`; }; diff --git a/packages/common/src/utils/colors.test.ts b/packages/common/src/utils/colors.test.ts index 791a0fb..279d963 100644 --- a/packages/common/src/utils/colors.test.ts +++ b/packages/common/src/utils/colors.test.ts @@ -1,47 +1,47 @@ -import { describe, expect, test } from 'vitest'; -import { hexToRgb, hsl, isDark } from './colors.ts'; +import { describe, expect, test } from "vitest"; +import { hexToRgb, hsl, isDark } from "./colors.ts"; -describe('hsl', () => { - test('generate a random hsl color', () => { - const [hue, saturation, lightness] = hsl(); +describe("hsl", () => { + test("generate a random hsl color", () => { + const [hue, saturation, lightness] = hsl(); - expect(hue).toBeGreaterThanOrEqual(0); - expect(hue).toBeLessThanOrEqual(360); + expect(hue).toBeGreaterThanOrEqual(0); + expect(hue).toBeLessThanOrEqual(360); - expect(saturation).toBeGreaterThanOrEqual(35); - expect(saturation).toBeLessThanOrEqual(100); + expect(saturation).toBeGreaterThanOrEqual(35); + expect(saturation).toBeLessThanOrEqual(100); - expect(lightness).toBeGreaterThanOrEqual(35); - expect(lightness).toBeLessThanOrEqual(100); - }); + expect(lightness).toBeGreaterThanOrEqual(35); + expect(lightness).toBeLessThanOrEqual(100); + }); }); -describe('hexToRgb', () => { - test('convert a hex color to rgb', () => { - expect(hexToRgb('#000000')).toEqual([0, 0, 0]); - expect(hexToRgb('#FFFFFF')).toEqual([255, 255, 255]); - expect(hexToRgb('#FF0000')).toEqual([255, 0, 0]); - expect(hexToRgb('#00FF00')).toEqual([0, 255, 0]); - expect(hexToRgb('#0000FF')).toEqual([0, 0, 255]); - expect(hexToRgb('#FF00FF')).toEqual([255, 0, 255]); - expect(hexToRgb('#00FFFF')).toEqual([0, 255, 255]); - expect(hexToRgb('#FFFF00')).toEqual([255, 255, 0]); - }); +describe("hexToRgb", () => { + test("convert a hex color to rgb", () => { + expect(hexToRgb("#000000")).toEqual([0, 0, 0]); + expect(hexToRgb("#FFFFFF")).toEqual([255, 255, 255]); + expect(hexToRgb("#FF0000")).toEqual([255, 0, 0]); + expect(hexToRgb("#00FF00")).toEqual([0, 255, 0]); + expect(hexToRgb("#0000FF")).toEqual([0, 0, 255]); + expect(hexToRgb("#FF00FF")).toEqual([255, 0, 255]); + expect(hexToRgb("#00FFFF")).toEqual([0, 255, 255]); + expect(hexToRgb("#FFFF00")).toEqual([255, 255, 0]); + }); - test('return null if the hex color is invalid', () => { - expect(hexToRgb('#000')).toBeNull(); - }); + test("return null if the hex color is invalid", () => { + expect(hexToRgb("#000")).toBeNull(); + }); }); -describe('isDark', () => { - test('determine if a color is dark', () => { - expect(isDark([0, 0, 0])).toBe(true); - expect(isDark([255, 255, 255])).toBe(false); - expect(isDark([255, 0, 0])).toBe(true); - expect(isDark([0, 255, 0])).toBe(false); - expect(isDark([0, 0, 255])).toBe(true); - expect(isDark([255, 0, 255])).toBe(true); - expect(isDark([0, 255, 255])).toBe(false); - expect(isDark([255, 255, 0])).toBe(false); - }); +describe("isDark", () => { + test("determine if a color is dark", () => { + expect(isDark([0, 0, 0])).toBe(true); + expect(isDark([255, 255, 255])).toBe(false); + expect(isDark([255, 0, 0])).toBe(true); + expect(isDark([0, 255, 0])).toBe(false); + expect(isDark([0, 0, 255])).toBe(true); + expect(isDark([255, 0, 255])).toBe(true); + expect(isDark([0, 255, 255])).toBe(false); + expect(isDark([255, 255, 0])).toBe(false); + }); }); diff --git a/packages/common/src/utils/colors.ts b/packages/common/src/utils/colors.ts index bfcd613..e7e75fb 100644 --- a/packages/common/src/utils/colors.ts +++ b/packages/common/src/utils/colors.ts @@ -1,21 +1,25 @@ export const hsl = (): [number, number, number] => { - const hue = Math.floor(Math.random() * 360); - const saturation = 35 + Math.floor(Math.random() * 65); - const lightness = 35 + Math.floor(Math.random() * 65); + const hue = Math.floor(Math.random() * 360); + const saturation = 35 + Math.floor(Math.random() * 65); + const lightness = 35 + Math.floor(Math.random() * 65); - return [hue, saturation, lightness]; + return [hue, saturation, lightness]; }; export const hexToRgb = (hex: string): [number, number, number] | null => { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result && result.length === 4 - ? [parseInt(result[1]!, 16), parseInt(result[2]!, 16), parseInt(result[3]!, 16)] - : null; + return result && result.length === 4 + ? [ + Number.parseInt(result[1]!, 16), + Number.parseInt(result[2]!, 16), + Number.parseInt(result[3]!, 16), + ] + : null; }; export const isDark = (rgb: [number, number, number]): boolean => { - const [r, g, b] = rgb; + const [r, g, b] = rgb; - return (r * 299 + g * 587 + b * 114) / 1000 < 128; + return (r * 299 + g * 587 + b * 114) / 1000 < 128; }; diff --git a/packages/common/src/utils/generatePagination.ts b/packages/common/src/utils/generatePagination.ts index 57da188..969a653 100644 --- a/packages/common/src/utils/generatePagination.ts +++ b/packages/common/src/utils/generatePagination.ts @@ -1,29 +1,29 @@ -type PaginationElement = number | 'ellipsis_l' | 'ellipsis_r'; +type PaginationElement = number | "ellipsis_l" | "ellipsis_r"; export const generatePagination = ( - currentPage: number, - totalPages: number, + currentPage: number, + totalPages: number, ): PaginationElement[] => { - if (currentPage === 1 && currentPage === totalPages) return [1]; - const center: PaginationElement[] = [ - currentPage - 2, - currentPage - 1, - currentPage, - currentPage + 1, - currentPage + 2, - ], - // @ts-ignore - filteredCenter = center.filter(p => p > 1 && p < totalPages), - includeThreeLeft = currentPage === 5, - includeThreeRight = currentPage === totalPages - 4, - includeLeftDots = currentPage > 5, - includeRightDots = currentPage < totalPages - 4; + if (currentPage === 1 && currentPage === totalPages) return [1]; + const center: PaginationElement[] = [ + currentPage - 2, + currentPage - 1, + currentPage, + currentPage + 1, + currentPage + 2, + ]; + // @ts-ignore + const filteredCenter = center.filter((p) => p > 1 && p < totalPages); + const includeThreeLeft = currentPage === 5; + const includeThreeRight = currentPage === totalPages - 4; + const includeLeftDots = currentPage > 5; + const includeRightDots = currentPage < totalPages - 4; - if (includeThreeLeft) filteredCenter.unshift(2); - if (includeThreeRight) filteredCenter.push(totalPages - 1); + if (includeThreeLeft) filteredCenter.unshift(2); + if (includeThreeRight) filteredCenter.push(totalPages - 1); - if (includeLeftDots) filteredCenter.unshift('ellipsis_l'); - if (includeRightDots) filteredCenter.push('ellipsis_r'); + if (includeLeftDots) filteredCenter.unshift("ellipsis_l"); + if (includeRightDots) filteredCenter.push("ellipsis_r"); - return [1, ...filteredCenter, totalPages]; + return [1, ...filteredCenter, totalPages]; }; diff --git a/packages/common/src/utils/hash.test.ts b/packages/common/src/utils/hash.test.ts index e865eb0..f14cd1d 100644 --- a/packages/common/src/utils/hash.test.ts +++ b/packages/common/src/utils/hash.test.ts @@ -1,95 +1,110 @@ -import * as crypto from 'crypto'; -import { describe, expect, test } from 'vitest'; -import { decrypt, encrypt, hash256, hashPassword, hmac, matchPassword } from './hash.ts'; +import * as crypto from "node:crypto"; +import { describe, expect, test } from "vitest"; +import { + decrypt, + encrypt, + hash256, + hashPassword, + hmac, + matchPassword, +} from "./hash.ts"; -describe('hash256', () => { - describe("hash 'hello' with sha256", () => { - const input = 'hello'; - const hash = hash256(input); - test('the output should not be the same as the input', () => { - expect(hash).not.toBe(input); - }); - test('with the same input, the hash should be the same', () => { - expect(hash256(input)).toBe(hash); - }); - test('the hash should be the same as the one generated by sha256 online', () => { - expect(hash).toBe('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'); - }); - }); +describe("hash256", () => { + describe("hash 'hello' with sha256", () => { + const input = "hello"; + const hash = hash256(input); + test("the output should not be the same as the input", () => { + expect(hash).not.toBe(input); + }); + test("with the same input, the hash should be the same", () => { + expect(hash256(input)).toBe(hash); + }); + test("the hash should be the same as the one generated by sha256 online", () => { + expect(hash).toBe( + "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", + ); + }); + }); - describe('hash an empty string with sha256', () => { - const input = ''; - const hash = hash256(input); - test('the output should not be the same as the input', () => { - expect(hash).not.toBe(input); - }); - test('with the same input, the hash should be the same', () => { - expect(hash256(input)).toBe(hash); - }); - test('the hash should be the same as the one generated by sha256 online', () => { - expect(hash).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); - }); - }); + describe("hash an empty string with sha256", () => { + const input = ""; + const hash = hash256(input); + test("the output should not be the same as the input", () => { + expect(hash).not.toBe(input); + }); + test("with the same input, the hash should be the same", () => { + expect(hash256(input)).toBe(hash); + }); + test("the hash should be the same as the one generated by sha256 online", () => { + expect(hash).toBe( + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + ); + }); + }); }); -describe('hmac', () => { - describe('hash a string with secret', () => { - const input = 'hello'; - const secret = 'secret'; - const hash = hmac(input, secret); - test('the output should not be the same as the input', () => { - expect(hash).not.toBe(input); - }); - test('with the same input and secret, the hash should be the same', () => { - expect(hmac(input, secret)).toBe(hash); - }); - }); +describe("hmac", () => { + describe("hash a string with secret", () => { + const input = "hello"; + const secret = "secret"; + const hash = hmac(input, secret); + test("the output should not be the same as the input", () => { + expect(hash).not.toBe(input); + }); + test("with the same input and secret, the hash should be the same", () => { + expect(hmac(input, secret)).toBe(hash); + }); + }); }); -describe('hashPassword/matchPassword', () => { - describe('hash a password with pepper', () => { - const password = 'password'; - const pepper = 'pepper'; - const hash = hashPassword(password, pepper); +describe("hashPassword/matchPassword", () => { + describe("hash a password with pepper", () => { + const password = "password"; + const pepper = "pepper"; + const hash = hashPassword(password, pepper); - test('the output should not be the same as the input', () => { - expect(hash).not.toBe(password); - }); + test("the output should not be the same as the input", () => { + expect(hash).not.toBe(password); + }); - test('with the same password and pepper, the hash should not be the same', () => { - expect(hashPassword(password, pepper)).not.toBe(hash); - }); + test("with the same password and pepper, the hash should not be the same", () => { + expect(hashPassword(password, pepper)).not.toBe(hash); + }); - test('with the same password and pepper, the hash should match', () => { - expect(matchPassword(password, hash, pepper)).toBe(true); - }); - }); + test("with the same password and pepper, the hash should match", () => { + expect(matchPassword(password, hash, pepper)).toBe(true); + }); + }); }); -describe('encrypt/decrypt', () => { - const key = 'supersecretkeysupersecretkeysupe'; - const text = crypto.randomBytes(32).toString('utf-8'); - const encrypted = encrypt(text, key); - describe('encrypt', () => { - test('the output should not be the same as the input', () => { - expect(encrypted).not.toBe(text); - }); - test('with the same input and key, the encrypted data should be different', () => { - expect(encrypt(text, key)).not.toBe(encrypted); - }); - test('the encrypted text should be different with a different key', () => { - expect(encrypt(text, 'notthesamekeynotthesamekeynotthe')).not.toBe(encrypted); - }); - test('the encrypted text should be different with a different input', () => { - expect(encrypt('hello world', key)).not.toBe(encrypted); - }); - }); - describe('decrypt', () => { - test('with the same encrypted text and key, the decrypted text should be the same', () => { - expect(decrypt(encrypted, key)).toBe(text); - }); - test('with a different key, this should throw an error', () => { - expect(() => decrypt(encrypted, crypto.randomBytes(32).toString('utf-8'))).toThrow(); - }); - }); +describe("encrypt/decrypt", () => { + const key = "supersecretkeysupersecretkeysupe"; + const text = crypto.randomBytes(32).toString("utf-8"); + const encrypted = encrypt(text, key); + describe("encrypt", () => { + test("the output should not be the same as the input", () => { + expect(encrypted).not.toBe(text); + }); + test("with the same input and key, the encrypted data should be different", () => { + expect(encrypt(text, key)).not.toBe(encrypted); + }); + test("the encrypted text should be different with a different key", () => { + expect(encrypt(text, "notthesamekeynotthesamekeynotthe")).not.toBe( + encrypted, + ); + }); + test("the encrypted text should be different with a different input", () => { + expect(encrypt("hello world", key)).not.toBe(encrypted); + }); + }); + describe("decrypt", () => { + test("with the same encrypted text and key, the decrypted text should be the same", () => { + expect(decrypt(encrypted, key)).toBe(text); + }); + test("with a different key, this should throw an error", () => { + expect(() => + decrypt(encrypted, crypto.randomBytes(32).toString("utf-8")), + ).toThrow(); + }); + }); }); diff --git a/packages/common/src/utils/hash.ts b/packages/common/src/utils/hash.ts index 6869323..9db25ee 100644 --- a/packages/common/src/utils/hash.ts +++ b/packages/common/src/utils/hash.ts @@ -1,52 +1,63 @@ -import * as crypto from 'crypto'; -import bcrypt from 'bcryptjs'; +import * as crypto from "node:crypto"; +import bcrypt from "bcryptjs"; export const hash256 = (input: string): string => { - const sha256 = crypto.createHash('sha256'); - return sha256.update(input).digest('hex'); + const sha256 = crypto.createHash("sha256"); + return sha256.update(input).digest("hex"); }; -export const hmac = (input: string, secret = ''): string => { - const hmac = crypto.createHmac('sha256', secret); - return hmac.update(input).digest('hex'); +export const hmac = (input: string, secret = ""): string => { + const hmac = crypto.createHmac("sha256", secret); + return hmac.update(input).digest("hex"); }; export const hashPassword = (password: string, pepper: string): string => { - const salt = bcrypt.genSaltSync(12); - return bcrypt.hashSync(hmac(password, pepper), salt); + const salt = bcrypt.genSaltSync(12); + return bcrypt.hashSync(hmac(password, pepper), salt); }; export const matchPassword = ( - password: string, - hashedPassword: string, - pepper: string, + password: string, + hashedPassword: string, + pepper: string, ): boolean => { - return bcrypt.compareSync(hmac(password, pepper), hashedPassword); + return bcrypt.compareSync(hmac(password, pepper), hashedPassword); }; -export const ENCRYPTION_ALGORITHM = 'aes-256-gcm'; +export const ENCRYPTION_ALGORITHM = "aes-256-gcm"; export const encrypt = (text: string, key: string): string => { - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, Buffer.from(key, 'utf-8'), iv); - const encrypted = cipher.update(text, 'utf8', 'base64') + cipher.final('base64'); - const tag = cipher.getAuthTag(); - return Buffer.concat([iv, tag, Buffer.from(encrypted)]).toString('hex'); + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv( + ENCRYPTION_ALGORITHM, + Buffer.from(key, "utf-8"), + iv, + ); + const encrypted = + cipher.update(text, "utf8", "base64") + cipher.final("base64"); + const tag = cipher.getAuthTag(); + return Buffer.concat([iv, tag, Buffer.from(encrypted)]).toString("hex"); }; export const decrypt = (encrypted: string, key: string): string => { - const content = Buffer.from(encrypted, 'hex'); - const iv = content.subarray(0, 16); - const tag = content.subarray(16, 32); - const text = content.subarray(32); - const decipher = crypto.createDecipheriv(ENCRYPTION_ALGORITHM, Buffer.from(key, 'utf-8'), iv); - decipher.setAuthTag(tag); - return decipher.update(text.toString(), 'base64', 'utf8') + decipher.final('utf8'); + const content = Buffer.from(encrypted, "hex"); + const iv = content.subarray(0, 16); + const tag = content.subarray(16, 32); + const text = content.subarray(32); + const decipher = crypto.createDecipheriv( + ENCRYPTION_ALGORITHM, + Buffer.from(key, "utf-8"), + iv, + ); + decipher.setAuthTag(tag); + return ( + decipher.update(text.toString(), "base64", "utf8") + decipher.final("utf8") + ); }; export const hashCode = (s: string): number => { - let h = 0; - for (let i = 0; i < s.length; i++) { - h = (Math.imul(31, h) + s.charCodeAt(i)) | 0; - } - return h; + let h = 0; + for (let i = 0; i < s.length; i++) { + h = (Math.imul(31, h) + s.charCodeAt(i)) | 0; + } + return h; }; diff --git a/packages/common/src/utils/is-network-error.ts b/packages/common/src/utils/is-network-error.ts index e99dcc1..c1284e7 100644 --- a/packages/common/src/utils/is-network-error.ts +++ b/packages/common/src/utils/is-network-error.ts @@ -1,34 +1,34 @@ // Based on https://github.com/sindresorhus/is-network-error/blob/main/index.js -// eslint-disable-next-line @typescript-eslint/unbound-method const objectToString = Object.prototype.toString; -const isError = (value: Object): value is Error => objectToString.call(value) === '[object Error]'; +const isError = (value: unknown): value is Error => + objectToString.call(value) === "[object Error]"; const errorMessages = new Set([ - 'Failed to fetch', // Chrome - 'NetworkError when attempting to fetch resource.', // Firefox - 'The Internet connection appears to be offline.', // Safari 16 - 'Load failed', // Safari 17+ - 'Network request failed', // `cross-fetch` - 'fetch failed', // Undici (Node.js) + "Failed to fetch", // Chrome + "NetworkError when attempting to fetch resource.", // Firefox + "The Internet connection appears to be offline.", // Safari 16 + "Load failed", // Safari 17+ + "Network request failed", // `cross-fetch` + "fetch failed", // Undici (Node.js) ]); export default function isNetworkError(error: unknown) { - const isValid = - error && - isError(error) && - // && error.name === 'TypeError' // NOTE: This is the only line that is different from the original - typeof error.message === 'string'; + const isValid = + error && + isError(error) && + // && error.name === 'TypeError' // NOTE: This is the only line that is different from the original + typeof error.message === "string"; - if (!isValid) { - return false; - } + if (!isValid) { + return false; + } - // We do an extra check for Safari 17+ as it has a very generic error message. - // Network errors in Safari have no stack. - if (error.message === 'Load failed') { - return error.stack === undefined; - } + // We do an extra check for Safari 17+ as it has a very generic error message. + // Network errors in Safari have no stack. + if (error.message === "Load failed") { + return error.stack === undefined; + } - return errorMessages.has(error.message); + return errorMessages.has(error.message); } diff --git a/packages/common/src/utils/math.test.ts b/packages/common/src/utils/math.test.ts index a343dc8..5c7363d 100644 --- a/packages/common/src/utils/math.test.ts +++ b/packages/common/src/utils/math.test.ts @@ -1,21 +1,21 @@ -import { describe, expect, test } from 'vitest'; -import { clamp, parseIntOr } from './math.ts'; +import { describe, expect, test } from "vitest"; +import { clamp, parseIntOr } from "./math.ts"; -describe('clamp', () => { - test('clamp a value between min and max', () => { - expect(clamp(0, 1, 10)).toBe(1); - expect(clamp(5, 1, 10)).toBe(5); - expect(clamp(15, 1, 10)).toBe(10); - }); +describe("clamp", () => { + test("clamp a value between min and max", () => { + expect(clamp(0, 1, 10)).toBe(1); + expect(clamp(5, 1, 10)).toBe(5); + expect(clamp(15, 1, 10)).toBe(10); + }); }); -describe('parseIntOr', () => { - test('parse a string to int or return default value', () => { - expect(parseIntOr('1', 2)).toBe(1); - expect(parseIntOr('a', 2)).toBe(2); - expect(parseIntOr(null, 2)).toBe(2); - expect(parseIntOr(undefined, 2)).toBe(2); - expect(parseIntOr(NaN, 2)).toBe(2); - expect(parseIntOr(Number.MIN_VALUE, 2)).toBe(Number.MIN_VALUE); - }); +describe("parseIntOr", () => { + test("parse a string to int or return default value", () => { + expect(parseIntOr("1", 2)).toBe(1); + expect(parseIntOr("a", 2)).toBe(2); + expect(parseIntOr(null, 2)).toBe(2); + expect(parseIntOr(undefined, 2)).toBe(2); + expect(parseIntOr(Number.NaN, 2)).toBe(2); + expect(parseIntOr(Number.MIN_VALUE, 2)).toBe(Number.MIN_VALUE); + }); }); diff --git a/packages/common/src/utils/math.ts b/packages/common/src/utils/math.ts index 8cb98d2..a00534c 100644 --- a/packages/common/src/utils/math.ts +++ b/packages/common/src/utils/math.ts @@ -1,13 +1,13 @@ export const clamp = (value: number, min: number, max: number) => { - return Math.max(Math.min(value, max), min); + return Math.max(Math.min(value, max), min); }; export const parseIntOr = (value: unknown, defaultValue: number) => { - if (typeof value === 'number' && !isNaN(value)) return value; - if (value === null || typeof value != 'string') return defaultValue; - const parsed = parseInt(value, 10); - if (isNaN(parsed)) { - return defaultValue; - } - return parsed; + if (typeof value === "number" && !Number.isNaN(value)) return value; + if (value === null || typeof value !== "string") return defaultValue; + const parsed = Number.parseInt(value, 10); + if (Number.isNaN(parsed)) { + return defaultValue; + } + return parsed; }; diff --git a/packages/common/src/utils/navigation.ts b/packages/common/src/utils/navigation.ts index 50d3339..b0c97fb 100644 --- a/packages/common/src/utils/navigation.ts +++ b/packages/common/src/utils/navigation.ts @@ -1,9 +1,9 @@ /* c8 ignore start */ export const safeHistoryReplaceState = (url: string) => { - try { - history.replaceState(null, '', url); - } catch (e) { - console.error(e); - } + try { + history.replaceState(null, "", url); + } catch (e) { + console.error(e); + } }; diff --git a/packages/common/src/utils/random.test.ts b/packages/common/src/utils/random.test.ts index 3e05938..399b5bb 100644 --- a/packages/common/src/utils/random.test.ts +++ b/packages/common/src/utils/random.test.ts @@ -1,24 +1,26 @@ -import { describe, expect, test } from 'vitest'; -import { generateToken, randomId } from './random.ts'; +import { describe, expect, test } from "vitest"; +import { generateToken, randomId } from "./random.ts"; -test('generate a randomId of length 9', () => { - const id = randomId(); - expect(id.length).toBe(9); - expect(id).toMatch(/v-[a-z0-9]{7}/); +test("generate a randomId of length 9", () => { + const id = randomId(); + expect(id.length).toBe(9); + expect(id).toMatch(/v-[a-z0-9]{7}/); }); -describe('generate a token with a fixed length', () => { - const length = Math.floor(Math.random() * 100) + 1; +describe("generate a token with a fixed length", () => { + const length = Math.floor(Math.random() * 100) + 1; - test(`generate a token of length ${length}`, () => { - const token = generateToken(length); - expect(token.length).toBe(length); - }); + test(`generate a token of length ${length}`, () => { + const token = generateToken(length); + expect(token.length).toBe(length); + }); - test(`generate a token of length ${length} with prefix`, () => { - const prefix = 'prefix-'; - const token = generateToken(length, prefix); - expect(token.length).toBe(Math.max(length, prefix.length)); - expect(token).toMatch(new RegExp(`^${prefix}.{${Math.max(0, length - prefix.length)}}`)); - }); + test(`generate a token of length ${length} with prefix`, () => { + const prefix = "prefix-"; + const token = generateToken(length, prefix); + expect(token.length).toBe(Math.max(length, prefix.length)); + expect(token).toMatch( + new RegExp(`^${prefix}.{${Math.max(0, length - prefix.length)}}`), + ); + }); }); diff --git a/packages/common/src/utils/random.ts b/packages/common/src/utils/random.ts index 65b9987..31411a9 100644 --- a/packages/common/src/utils/random.ts +++ b/packages/common/src/utils/random.ts @@ -1,8 +1,8 @@ -import { nanoid } from 'nanoid'; +import { nanoid } from "nanoid"; export function randomId(): string { - return `v-${Math.random().toString(36).substring(2, 9)}`; + return `v-${Math.random().toString(36).substring(2, 9)}`; } -export const generateToken = (numberOfCharacters = 32, prefix = ''): string => - prefix + nanoid(numberOfCharacters - prefix.length); +export const generateToken = (numberOfCharacters = 32, prefix = ""): string => + prefix + nanoid(numberOfCharacters - prefix.length); diff --git a/packages/common/src/utils/wait.ts b/packages/common/src/utils/wait.ts index 29f2589..b8236d4 100644 --- a/packages/common/src/utils/wait.ts +++ b/packages/common/src/utils/wait.ts @@ -1,39 +1,40 @@ -type ArrayElement = ArrayType extends (infer ElementType)[] ? ElementType : never; +type ArrayElement = ArrayType extends (infer ElementType)[] + ? ElementType + : never; type ResolveType = T extends Promise ? R : T; type Wait = ( - promises: T extends number - ? T - : T extends ArrayElement[] - ? (Promise> | ArrayElement)[] - : T | Promise, - minimumWaitTime?: T extends number ? never : number, + promises: T extends number + ? T + : T extends ArrayElement[] + ? (Promise> | ArrayElement)[] + : T | Promise, + minimumWaitTime?: T extends number ? never : number, ) => Promise< - T extends number - ? void - : T extends ArrayElement[] - ? ResolveType>[] - : ResolveType + T extends number + ? void + : T extends ArrayElement[] + ? ResolveType>[] + : ResolveType >; const wait: Wait = async (promises, minimumWaitTime) => { - if (typeof promises === 'number') { - return new Promise(res => setTimeout(res, promises)); - } + if (typeof promises === "number") { + return new Promise((res) => setTimeout(res, promises)); + } - const multiplePromisesProvided = Array.isArray(promises); + const multiplePromisesProvided = Array.isArray(promises); - const arrayOfPromises = multiplePromisesProvided ? promises : [promises]; + const arrayOfPromises = multiplePromisesProvided ? promises : [promises]; - const resolved = ( - await Promise.all([ - ...arrayOfPromises, - new Promise(resolve => setTimeout(resolve, minimumWaitTime)), - ]) - ).slice(0, arrayOfPromises.length); + const resolved = ( + await Promise.all([ + ...arrayOfPromises, + new Promise((resolve) => setTimeout(resolve, minimumWaitTime)), + ]) + ).slice(0, arrayOfPromises.length); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return multiplePromisesProvided ? resolved : resolved[0]; + return multiplePromisesProvided ? resolved : resolved[0]; }; export default wait; diff --git a/packages/common/src/utils/wrap-with-loading.ts b/packages/common/src/utils/wrap-with-loading.ts index a67a141..d2e03c4 100644 --- a/packages/common/src/utils/wrap-with-loading.ts +++ b/packages/common/src/utils/wrap-with-loading.ts @@ -1,88 +1,85 @@ /* c8 ignore start */ -'use client'; +"use client"; -import { toast } from 'sonner'; -import type { ToastT } from 'sonner'; -import { randomId } from './random.ts'; +import { toast } from "sonner"; +import type { ToastT } from "sonner"; +import { randomId } from "./random.ts"; -type NotificationBase = Omit; +type NotificationBase = Omit; interface WrapWithLoadingProps { - sharedProps?: NotificationBase; - loadingProps?: NotificationBase; - successProps?: ((data: T) => NotificationBase) | NotificationBase; - errorProps?: ((error: Error) => NotificationBase) | NotificationBase; - throwOnError?: B; + sharedProps?: NotificationBase; + loadingProps?: NotificationBase; + successProps?: ((data: T) => NotificationBase) | NotificationBase; + errorProps?: ((error: Error) => NotificationBase) | NotificationBase; + throwOnError?: B; } export const wrapWithLoading = async ( - promise: () => Promise | T, - { - sharedProps = {}, - loadingProps, - successProps, - errorProps, - throwOnError, - }: WrapWithLoadingProps, + promise: () => Promise | T, + { + sharedProps = {}, + loadingProps, + successProps, + errorProps, + throwOnError, + }: WrapWithLoadingProps, ): Promise => { - const id = randomId(); + const id = randomId(); - if (loadingProps) { - toast.loading(loadingProps.title, { - id, - ...sharedProps, - ...loadingProps, - }); - } + if (loadingProps) { + toast.loading(loadingProps.title, { + id, + ...sharedProps, + ...loadingProps, + }); + } - try { - const result = await promise(); + try { + const result = await promise(); - if (successProps) { - let notificationProps: NotificationBase; - if (typeof successProps === 'function') { - notificationProps = successProps(result); - } else { - notificationProps = successProps; - } + if (successProps) { + let notificationProps: NotificationBase; + if (typeof successProps === "function") { + notificationProps = successProps(result); + } else { + notificationProps = successProps; + } - toast.message(notificationProps.title, { - id, - type: 'success', - duration: 4000, - ...sharedProps, - ...notificationProps, - }); - } + toast.message(notificationProps.title, { + id, + type: "success", + duration: 4000, + ...sharedProps, + ...notificationProps, + }); + } - return result; - } catch (error) { - if (errorProps) { - let notificationProps: NotificationBase; - if (typeof errorProps === 'function') { - notificationProps = errorProps(error as Error); - } else { - notificationProps = errorProps; - } + return result; + } catch (error) { + if (errorProps) { + let notificationProps: NotificationBase; + if (typeof errorProps === "function") { + notificationProps = errorProps(error as Error); + } else { + notificationProps = errorProps; + } - toast.message(notificationProps.title, { - id, - type: 'error', - duration: 4000, - ...sharedProps, - ...notificationProps, - }); - } + toast.message(notificationProps.title, { + id, + type: "error", + duration: 4000, + ...sharedProps, + ...notificationProps, + }); + } - if (throwOnError) { - throw error; - } + if (throwOnError) { + throw error; + } - // @ts-expect-error: happy compiler - return null; - } - - // @ts-expect-error: happy compiler - return null; + // @ts-expect-error: happy compiler + return null; + } }; diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json index fe5f00c..825efaa 100644 --- a/packages/common/tsconfig.json +++ b/packages/common/tsconfig.json @@ -1,36 +1,36 @@ { - "compilerOptions": { - "rootDir": "src", - "baseUrl": ".", - "outDir": "dist", - "target": "es2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "checkJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "esModuleInterop": true, - "module": "esnext", - "allowImportingTsExtensions": true, - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "noUncheckedIndexedAccess": true, - "composite": false, - "declaration": true, - "declarationMap": true, - "inlineSources": false, - "noUnusedLocals": false, - "noUnusedParameters": false, - "preserveWatchOutput": true, - "verbatimModuleSyntax": true, - "experimentalDecorators": true, - "paths": { - "~/*": ["src/*"] - } - }, - "include": ["src"], - "exclude": ["node_modules", ".idea", ".github", "dist"] + "compilerOptions": { + "rootDir": "src", + "baseUrl": ".", + "outDir": "dist", + "target": "es2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "checkJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "module": "esnext", + "allowImportingTsExtensions": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "noUncheckedIndexedAccess": true, + "composite": false, + "declaration": true, + "declarationMap": true, + "inlineSources": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "verbatimModuleSyntax": true, + "experimentalDecorators": true, + "paths": { + "~/*": ["src/*"] + } + }, + "include": ["src"], + "exclude": ["node_modules", ".idea", ".github", "dist"] } diff --git a/packages/common/tsup.config.ts b/packages/common/tsup.config.ts index 0bd7586..f747750 100644 --- a/packages/common/tsup.config.ts +++ b/packages/common/tsup.config.ts @@ -1,31 +1,34 @@ -import cpy from 'cpy'; -import { execaCommand } from 'execa'; -import type { Options } from 'tsup'; -import { defineConfig } from 'tsup'; +import cpy from "cpy"; +import { execaCommand } from "execa"; +import type { Options } from "tsup"; +import { defineConfig } from "tsup"; export default defineConfig((options: Options) => ({ - treeshake: true, - splitting: true, - entry: ['src/**/*.(tsx|ts|cjs)', '!src/**/*.(config|test).(tsx|ts|cjs)'], - format: ['esm'], - dts: true, - sourcemap: true, - minify: true, - minifyWhitespace: true, - keepNames: true, - clean: true, - bundle: false, - external: ['react'], - onSuccess: async () => { - await cpy(['package.json', 'src/**/*.{css,scss,svg,config.ts}', 'README.md'], 'dist'); - await execaCommand('pnpm exec tsconfig-replace-paths', { - stdout: process.stdout, - stderr: process.stderr, - }); - await execaCommand('node ../../scripts/fix-ts-paths.js', { - stdout: process.stdout, - stderr: process.stderr, - }); - }, - ...options, + treeshake: true, + splitting: true, + entry: ["src/**/*.(tsx|ts|cjs)", "!src/**/*.(config|test).(tsx|ts|cjs)"], + format: ["esm"], + dts: true, + sourcemap: true, + minify: true, + minifyWhitespace: true, + keepNames: true, + clean: true, + bundle: false, + external: ["react"], + onSuccess: async () => { + await cpy( + ["package.json", "src/**/*.{css,scss,svg,config.ts}", "README.md"], + "dist", + ); + await execaCommand("pnpm exec tsconfig-replace-paths", { + stdout: process.stdout, + stderr: process.stderr, + }); + await execaCommand("node ../../scripts/fix-ts-paths.js", { + stdout: process.stdout, + stderr: process.stderr, + }); + }, + ...options, })); diff --git a/packages/common/vitest.config.js b/packages/common/vitest.config.js index 57d8e44..dedbb63 100644 --- a/packages/common/vitest.config.js +++ b/packages/common/vitest.config.js @@ -1,11 +1,9 @@ -import { mergeConfig } from 'vitest/config'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment +import { mergeConfig } from "vitest/config"; // @ts-ignore -// eslint-disable-next-line node/no-unpublished-import -import configShared from '../../vitest.config.js'; +import configShared from "../../vitest.config.js"; export default mergeConfig(configShared, { - test: { - name: 'common', - }, + test: { + name: "common", + }, }); diff --git a/packages/design/.eslintrc.cjs b/packages/design/.eslintrc.cjs index ca65403..26d06dd 100644 --- a/packages/design/.eslintrc.cjs +++ b/packages/design/.eslintrc.cjs @@ -1,7 +1,7 @@ module.exports = { - extends: ['@pedaki/eslint-config'], - parserOptions: { - project: true, - }, - ignorePatterns: ['**/dist/**/*', '**/node_modules/**/*', '**/scripts/**/*'], + extends: ["@pedaki/eslint-config"], + parserOptions: { + project: true, + }, + ignorePatterns: ["**/dist/**/*", "**/node_modules/**/*", "**/scripts/**/*"], }; diff --git a/packages/design/components.json b/packages/design/components.json index 316b81d..092f561 100644 --- a/packages/design/components.json +++ b/packages/design/components.json @@ -1,16 +1,16 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "src/tailwind/tailwind.config.ts", - "css": "src/tailwind/index.css", - "baseColor": "slate", - "cssVariables": true - }, - "aliases": { - "components": "src/ui", - "utils": "src/utils" - } + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "src/tailwind/tailwind.config.ts", + "css": "src/tailwind/index.css", + "baseColor": "slate", + "cssVariables": true + }, + "aliases": { + "components": "src/ui", + "utils": "src/utils" + } } diff --git a/packages/design/package.json b/packages/design/package.json index 2435832..9e40448 100644 --- a/packages/design/package.json +++ b/packages/design/package.json @@ -1,88 +1,82 @@ { - "name": "@pedaki/design", - "version": "0.5.3", - "author": "Nathan David ", - "repository": { - "type": "git", - "url": "https://github.com/PedakiHQ/pedaki", - "directory": "packages/design" - }, - "homepage": "https://www.pedaki.fr", - "description": "React components and theme for Pedaki", - "license": "CC-BY-NC-SA-4.0", - "private": false, - "type": "module", - "sideEffects": [ - "**/*.css", - "**/*.scss" - ], - "publishConfig": { - "directory": "dist" - }, - "scripts": { - "build": "tsup", - "build:watch": "tsup --watch", - "lint": "eslint .", - "typecheck": "tsc --noEmit", - "format": "prettier --write .", - "format:check": "prettier --check ." - }, - "devDependencies": { - "@pedaki/eslint-config": "0.5.3", - "@pedaki/prettier-config": "0.5.3", - "@types/react": "^18.2.79", - "esbuild-css-modules-plugin": "^3.1.1", - "esbuild-plugin-alias-path": "^2.0.2", - "esbuild-plugin-file-path-extensions": "^2.0.0", - "esbuild-sass-plugin": "^3.2.0", - "next": "latest", - "react": "^18.2.0", - "react-hook-form": "^7.51.3" - }, - "peerDependencies": { - "@radix-ui/react-accordion": "^1.1.2", - "@radix-ui/react-aspect-ratio": "^1.0.3", - "@radix-ui/react-avatar": "^1.0.3", - "@radix-ui/react-checkbox": "^1.0.4", - "@radix-ui/react-collapsible": "^1.0.3", - "@radix-ui/react-dialog": "^1.0.4", - "@radix-ui/react-dropdown-menu": "^2.0.5", - "@radix-ui/react-label": "^2.0.2", - "@radix-ui/react-navigation-menu": "^1.1.3", - "@radix-ui/react-popover": "^1.0.7", - "@radix-ui/react-progress": "^1.0.3", - "@radix-ui/react-radio-group": "^1.1.3", - "@radix-ui/react-scroll-area": "^1.0.4", - "@radix-ui/react-select": "^2.0.0", - "@radix-ui/react-separator": "^1.0.3", - "@radix-ui/react-slider": "^1.1.2", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-switch": "^1.0.3", - "@radix-ui/react-tooltip": "^1.0.6", - "@tailwindcss/container-queries": "^0.1.1", - "class-variance-authority": "^0.7.0", - "clsx": "^2.0.0", - "date-fns": "^3.3.1", - "dayjs": "^1.11.10", - "mini-svg-data-uri": "^1.4.4", - "react-day-picker": "^8.10.0", - "react-hook-form": "^7.46.1", - "react-resizable-panels": "^2.0.0", - "sass": "^1.66.1", - "tailwind-merge": "^2.0.0", - "tailwindcss": "^3.3.3", - "tailwindcss-animate": "^1.0.7" - }, - "peerDependenciesMeta": { - "react-hook-form": { - "optional": true - } - }, - "engines": { - "node": "^18 || ^20", - "pnpm": ">=9.0.0", - "yarn": "use-pnpm", - "npm": "use-pnpm" - }, - "prettier": "@pedaki/prettier-config" -} \ No newline at end of file + "name": "@pedaki/design", + "version": "0.5.3", + "author": "Nathan David ", + "repository": { + "type": "git", + "url": "https://github.com/PedakiHQ/pedaki", + "directory": "packages/design" + }, + "homepage": "https://www.pedaki.fr", + "description": "React components and theme for Pedaki", + "license": "CC-BY-NC-SA-4.0", + "private": false, + "type": "module", + "sideEffects": ["**/*.css", "**/*.scss"], + "publishConfig": { + "directory": "dist" + }, + "scripts": { + "build": "tsup", + "build:watch": "tsup --watch", + "format": "pnpx @biomejs/biome format ./ --write", + "lint": "pnpx @biomejs/biome lint ./ --apply", + "format:check": "pnpx @biomejs/biome format ./", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@types/react": "^18.2.79", + "esbuild-css-modules-plugin": "^3.1.1", + "esbuild-plugin-alias-path": "^2.0.2", + "esbuild-plugin-file-path-extensions": "^2.0.0", + "esbuild-sass-plugin": "^3.2.0", + "next": "latest", + "react": "^18.2.0", + "react-hook-form": "^7.51.3" + }, + "peerDependencies": { + "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-aspect-ratio": "^1.0.3", + "@radix-ui/react-avatar": "^1.0.3", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-dialog": "^1.0.4", + "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-navigation-menu": "^1.1.3", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-scroll-area": "^1.0.4", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tooltip": "^1.0.6", + "@tailwindcss/container-queries": "^0.1.1", + "class-variance-authority": "^0.7.0", + "clsx": "^2.0.0", + "date-fns": "^3.3.1", + "dayjs": "^1.11.10", + "mini-svg-data-uri": "^1.4.4", + "react-day-picker": "^8.10.0", + "react-hook-form": "^7.46.1", + "react-resizable-panels": "^2.0.0", + "sass": "^1.66.1", + "tailwind-merge": "^2.0.0", + "tailwindcss": "^3.3.3", + "tailwindcss-animate": "^1.0.7" + }, + "peerDependenciesMeta": { + "react-hook-form": { + "optional": true + } + }, + "engines": { + "node": "^18 || ^20", + "pnpm": ">=9.0.0", + "yarn": "use-pnpm", + "npm": "use-pnpm" + } +} diff --git a/packages/design/scripts/add-directive-dist.cjs b/packages/design/scripts/add-directive-dist.cjs index 9c56469..85839ad 100644 --- a/packages/design/scripts/add-directive-dist.cjs +++ b/packages/design/scripts/add-directive-dist.cjs @@ -1,47 +1,48 @@ -const fs = require('fs-extra'); -const path = require('path'); +const fs = require("fs-extra"); +const path = require("node:path"); -const distFolder = 'dist'; -const srcFolder = 'src'; +const distFolder = "dist"; +const srcFolder = "src"; -const listFiles = dir => { - const files = fs.readdirSync(dir); - return files.flatMap(file => { - const filePath = path.join(dir, file); - const stat = fs.statSync(filePath); - if (stat.isDirectory()) { - return listFiles(filePath); - } else { - // keep only js files - if (!filePath.endsWith('.js')) { - return []; - } - return [filePath]; - } - }); +const listFiles = (dir) => { + const files = fs.readdirSync(dir); + return files.flatMap((file) => { + const filePath = path.join(dir, file); + const stat = fs.statSync(filePath); + if (stat.isDirectory()) { + return listFiles(filePath); + } + + // keep only js files + if (!filePath.endsWith(".js")) { + return []; + } + return [filePath]; + }); }; const files = listFiles(distFolder); -files.forEach(file => { - let found = false; - ['.tsx', '.ts'].forEach(ext => { - if (found) return; +for (const file of files) { + let found = false; + const extensions = [".ts", ".tsx"]; + for (const ext of extensions) { + if (found) break; - const srcFile = file.replace(distFolder, srcFolder).replace('.js', ext); - if (fs.existsSync(srcFile)) { - const srcContent = fs.readFileSync(srcFile, 'utf8'); - const distContent = fs.readFileSync(file, 'utf8'); - const directive = srcContent.split('\n')[0]; - const matchPattern = ['use client', 'use server']; - if (matchPattern.some(pattern => directive.includes(pattern))) { - console.log(`Adding ${directive} in ${file}`); - fs.writeFileSync(file, `${directive}\n${distContent}`); - } - found = true; - } - }); + const srcFile = file.replace(distFolder, srcFolder).replace(".js", ext); + if (fs.existsSync(srcFile)) { + const srcContent = fs.readFileSync(srcFile, "utf8"); + const distContent = fs.readFileSync(file, "utf8"); + const directive = srcContent.split("\n")[0]; + const matchPattern = ["use client", "use server"]; + if (matchPattern.some((pattern) => directive.includes(pattern))) { + console.log(`Adding ${directive} in ${file}`); + fs.writeFileSync(file, `${directive}\n${distContent}`); + } + found = true; + } + } - if (!found) { - console.log(`No source file found for ${file}`); - } -}); + if (!found) { + console.log(`No source file found for ${file}`); + } +} diff --git a/packages/design/scripts/react-import.js b/packages/design/scripts/react-import.js index 0094abe..0a1f94c 100644 --- a/packages/design/scripts/react-import.js +++ b/packages/design/scripts/react-import.js @@ -1,3 +1,3 @@ -import React from 'react'; +import React from "react"; export { React }; diff --git a/packages/design/src/css.d.ts b/packages/design/src/css.d.ts index 559c43b..01c056e 100644 --- a/packages/design/src/css.d.ts +++ b/packages/design/src/css.d.ts @@ -1,14 +1,14 @@ -declare module '*.module.css' { - const classes: Readonly>; - export default classes; +declare module "*.module.css" { + const classes: Readonly>; + export default classes; } -declare module '*.module.sass' { - const classes: Readonly>; - export default classes; +declare module "*.module.sass" { + const classes: Readonly>; + export default classes; } -declare module '*.module.scss' { - const classes: Readonly>; - export default classes; +declare module "*.module.scss" { + const classes: Readonly>; + export default classes; } diff --git a/packages/design/src/tailwind/tailwind.config.ts b/packages/design/src/tailwind/tailwind.config.ts index e2435bb..007c79b 100644 --- a/packages/design/src/tailwind/tailwind.config.ts +++ b/packages/design/src/tailwind/tailwind.config.ts @@ -1,401 +1,400 @@ -import svgToDataUri from 'mini-svg-data-uri'; -import type { Config } from 'tailwindcss'; -import { fontFamily } from 'tailwindcss/defaultTheme.js'; +import svgToDataUri from "mini-svg-data-uri"; +import type { Config } from "tailwindcss"; +import { fontFamily } from "tailwindcss/defaultTheme.js"; // @ts-expect-error - no types -import flattenColorPalette from 'tailwindcss/lib/util/flattenColorPalette.js'; -import plugin from 'tailwindcss/plugin.js'; +import flattenColorPalette from "tailwindcss/lib/util/flattenColorPalette.js"; +import plugin from "tailwindcss/plugin.js"; const gridBackground = plugin(({ matchUtilities, theme }) => { - matchUtilities( - { - 'bgi-grid': (value: string) => ({ - backgroundImage: `url("${svgToDataUri( - ``, - )}")`, - }), - 'bgi-grid-dashed': (value: string) => ({ - backgroundImage: `url("${svgToDataUri( - ``, - )}")`, - }), - }, - // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment - { values: flattenColorPalette(theme('backgroundColor')), type: 'color' }, - ); + matchUtilities( + { + "bgi-grid": (value: string) => ({ + backgroundImage: `url("${svgToDataUri( + ``, + )}")`, + }), + "bgi-grid-dashed": (value: string) => ({ + backgroundImage: `url("${svgToDataUri( + ``, + )}")`, + }), + }, + { values: flattenColorPalette(theme("backgroundColor")), type: "color" }, + ); }); export default { - darkMode: ['class'], - theme: { - container: { - center: true, - padding: { - DEFAULT: '1rem', - sm: '2rem', - lg: '4rem', - xl: '7.5rem', - }, - screens: { - sm: '640px', - md: '768px', - lg: '1024px', - xl: '1280px', - '2xl': '1400px', + darkMode: ["class"], + theme: { + container: { + center: true, + padding: { + DEFAULT: "1rem", + sm: "2rem", + lg: "4rem", + xl: "7.5rem", + }, + screens: { + sm: "640px", + md: "768px", + lg: "1024px", + xl: "1280px", + "2xl": "1400px", - tall: { raw: '(min-height: 800px)' }, - }, - }, - colors: { - transparent: 'transparent', + tall: { raw: "(min-height: 800px)" }, + }, + }, + colors: { + transparent: "transparent", - white: 'rgb(255 255 255 / )', - 'neutral-0': 'rgb(255 255 255 / )', - 'neutral-100': 'rgb(249 249 249 / )', - 'neutral-200': 'rgb(226 228 233 / )', - 'neutral-300': 'rgb(205 208 213 / )', - 'neutral-400': 'rgb(134 140 152 / )', - 'neutral-500': 'rgb(82 88 102 / )', - 'neutral-600': 'rgb(49 53 63 / )', - 'neutral-700': 'rgb(32 35 45 / )', - 'neutral-800': 'rgb(22 25 34 / )', - 'neutral-900': 'rgb(10 13 20 / )', - black: 'rgb(10 13 20 / )', + white: "rgb(255 255 255 / )", + "neutral-0": "rgb(255 255 255 / )", + "neutral-100": "rgb(249 249 249 / )", + "neutral-200": "rgb(226 228 233 / )", + "neutral-300": "rgb(205 208 213 / )", + "neutral-400": "rgb(134 140 152 / )", + "neutral-500": "rgb(82 88 102 / )", + "neutral-600": "rgb(49 53 63 / )", + "neutral-700": "rgb(32 35 45 / )", + "neutral-800": "rgb(22 25 34 / )", + "neutral-900": "rgb(10 13 20 / )", + black: "rgb(10 13 20 / )", - 'blue-base': 'rgb(55 93 251 / )', - 'blue-dark': 'rgb(37 62 167 / )', - 'blue-darker': 'rgb(22 38 100 / )', - 'blue-light': 'rgb(194 214 255 / )', - 'blue-lighter': 'rgb(235 241 255 / )', + "blue-base": "rgb(55 93 251 / )", + "blue-dark": "rgb(37 62 167 / )", + "blue-darker": "rgb(22 38 100 / )", + "blue-light": "rgb(194 214 255 / )", + "blue-lighter": "rgb(235 241 255 / )", - 'green-base': 'rgb(56 199 147 / )', - 'green-dark': 'rgb(45 159 117 / )', - 'green-darker': 'rgb(23 100 72 / )', - 'green-light': 'rgb(203 245 229 / )', - 'green-lighter': 'rgb(239 250 246 / )', + "green-base": "rgb(56 199 147 / )", + "green-dark": "rgb(45 159 117 / )", + "green-darker": "rgb(23 100 72 / )", + "green-light": "rgb(203 245 229 / )", + "green-lighter": "rgb(239 250 246 / )", - 'orange-base': 'rgb(241 123 44 / )', - 'orange-dark': 'rgb(194 84 10 / )', - 'orange-darker': 'rgb(110 51 12 / )', - 'orange-light': 'rgb(255 218 194 / )', - 'orange-lighter': 'rgb(254 243 235 / )', + "orange-base": "rgb(241 123 44 / )", + "orange-dark": "rgb(194 84 10 / )", + "orange-darker": "rgb(110 51 12 / )", + "orange-light": "rgb(255 218 194 / )", + "orange-lighter": "rgb(254 243 235 / )", - 'pink-base': 'rgb(226 85 242 / )', - 'pink-dark': 'rgb(156 35 169 / )', - 'pink-darker': 'rgb(98 15 108 / )', - 'pink-light': 'rgb(249 194 255 / )', - 'pink-lighter': 'rgb(253 235 255 / )', + "pink-base": "rgb(226 85 242 / )", + "pink-dark": "rgb(156 35 169 / )", + "pink-darker": "rgb(98 15 108 / )", + "pink-light": "rgb(249 194 255 / )", + "pink-lighter": "rgb(253 235 255 / )", - 'purple-base': 'rgb(110 63 243 / )', - 'purple-dark': 'rgb(90 54 191 / )', - 'purple-darker': 'rgb(43 22 100 / )', - 'purple-light': 'rgb(202 194 255 / )', - 'purple-lighter': 'rgb(238 235 255 / )', + "purple-base": "rgb(110 63 243 / )", + "purple-dark": "rgb(90 54 191 / )", + "purple-darker": "rgb(43 22 100 / )", + "purple-light": "rgb(202 194 255 / )", + "purple-lighter": "rgb(238 235 255 / )", - 'red-base': 'rgb(223 28 65 / )', - 'red-dark': 'rgb(175 29 56 / )', - 'red-darker': 'rgb(113 14 33 / )', - 'red-light': 'rgb(248 201 210 / )', - 'red-lighter': 'rgb(253 237 240 / )', + "red-base": "rgb(223 28 65 / )", + "red-dark": "rgb(175 29 56 / )", + "red-darker": "rgb(113 14 33 / )", + "red-light": "rgb(248 201 210 / )", + "red-lighter": "rgb(253 237 240 / )", - 'teal-base': 'rgb(53 185 233 / )', - 'teal-dark': 'rgb(31 135 173 / )', - 'teal-darker': 'rgb(22 69 100 / )', - 'teal-light': 'rgb(194 239 255 / )', - 'teal-lighter': 'rgb(235 250 255 / )', + "teal-base": "rgb(53 185 233 / )", + "teal-dark": "rgb(31 135 173 / )", + "teal-darker": "rgb(22 69 100 / )", + "teal-light": "rgb(194 239 255 / )", + "teal-lighter": "rgb(235 250 255 / )", - 'yellow-base': 'rgb(242 174 64 / )', - 'yellow-dark': 'rgb(180 120 24 / )', - 'yellow-darker': 'rgb(105 61 17 / )', - 'yellow-light': 'rgb(251 223 177 / )', - 'yellow-lighter': 'rgb(254 247 236 / )', + "yellow-base": "rgb(242 174 64 / )", + "yellow-dark": "rgb(180 120 24 / )", + "yellow-darker": "rgb(105 61 17 / )", + "yellow-light": "rgb(251 223 177 / )", + "yellow-lighter": "rgb(254 247 236 / )", - 'state-away': 'var(--tw-yellow-base)', - 'state-error': 'var(--tw-red-base)', - 'state-feature': 'var(--tw-purple-base)', - 'state-information': 'var(--tw-blue-base)', - 'state-neutral': 'var(--tw-neutral-400)', - 'state-success': 'var(--tw-green-base)', - 'state-verified': 'var(--tw-teal-base)', - 'state-warning': 'var(--tw-orange-base)', + "state-away": "var(--tw-yellow-base)", + "state-error": "var(--tw-red-base)", + "state-feature": "var(--tw-purple-base)", + "state-information": "var(--tw-blue-base)", + "state-neutral": "var(--tw-neutral-400)", + "state-success": "var(--tw-green-base)", + "state-verified": "var(--tw-teal-base)", + "state-warning": "var(--tw-orange-base)", - 'stroke-disabled': 'var(--tw-neutral-100)', - 'stroke-soft': 'var(--tw-neutral-200)', - 'stroke-strong': 'var(--tw-neutral-900)', - 'stroke-sub': 'var(--tw-neutral-300)', - 'stroke-white': 'var(--tw-neutral-0)', + "stroke-disabled": "var(--tw-neutral-100)", + "stroke-soft": "var(--tw-neutral-200)", + "stroke-strong": "var(--tw-neutral-900)", + "stroke-sub": "var(--tw-neutral-300)", + "stroke-white": "var(--tw-neutral-0)", - 'shadow-stroke-important': 'rgba(55, 93, 251, 0.08)', // 'var(--tw-neutral-500) / 0.08', - 'shadow-stroke-primary': 'rgba(241, 123, 44, 0.08)', // 'var(--tw-orange-base) / 0.08', - 'shadow-stroke-error': 'rgba(233, 53, 53, 0.08)', + "shadow-stroke-important": "rgba(55, 93, 251, 0.08)", // 'var(--tw-neutral-500) / 0.08', + "shadow-stroke-primary": "rgba(241, 123, 44, 0.08)", // 'var(--tw-orange-base) / 0.08', + "shadow-stroke-error": "rgba(233, 53, 53, 0.08)", - 'shadow-focus-important': 'var(--tw-neutral-200)', - 'shadow-focus-primary': 'var(--tw-neutral-100)', - 'shadow-focus-error': 'rgba(255, 236, 235, 1)', + "shadow-focus-important": "var(--tw-neutral-200)", + "shadow-focus-primary": "var(--tw-neutral-100)", + "shadow-focus-error": "rgba(255, 236, 235, 1)", - // 'primary-base': 'var(--tw-orange-base)', - 'primary-base': 'rgb(241 123 44 / )', - // 'primary-dark': 'var(--tw-orange-dark)', - 'primary-dark': 'rgb(194 84 10 / )', - // 'primary-darker': 'var(--tw-orange-darker)', - 'primary-darker': 'rgb(110 51 12 / )', - // 'primary-light': 'var(--tw-orange-light)', - 'primary-light': 'rgb(255 218 194 / )', - // 'primary-lighter': 'var(--tw-orange-lighter)', - 'primary-lighter': 'rgb(254 243 235 / )', + // 'primary-base': 'var(--tw-orange-base)', + "primary-base": "rgb(241 123 44 / )", + // 'primary-dark': 'var(--tw-orange-dark)', + "primary-dark": "rgb(194 84 10 / )", + // 'primary-darker': 'var(--tw-orange-darker)', + "primary-darker": "rgb(110 51 12 / )", + // 'primary-light': 'var(--tw-orange-light)', + "primary-light": "rgb(255 218 194 / )", + // 'primary-lighter': 'var(--tw-orange-lighter)', + "primary-lighter": "rgb(254 243 235 / )", - textMain: 'var(--tw-neutral-900)', - textSub: 'var(--tw-neutral-500)', - textSoft: 'var(--tw-neutral-400)', - textDisabled: 'var(--tw-neutral-300)', - }, - fontSize: { - 'title-1': [ - '56px', - { - letterSpacing: '-0.001em', - lineHeight: '64px', - }, - ], - 'title-2': [ - '48px', - { - letterSpacing: '-0.001em', - lineHeight: '56px', - }, - ], - 'title-3': [ - '40px', - { - letterSpacing: '-0.001em', - lineHeight: '48px', - }, - ], - 'title-4': [ - '32px', - { - letterSpacing: '0', - lineHeight: '40px', - }, - ], - 'title-5': [ - '24px', - { - letterSpacing: '0', - lineHeight: '32px', - }, - ], - 'title-6': [ - '20px', - { - letterSpacing: '0', - lineHeight: '28px', - }, - ], - // - 'label-xl': [ - '24px', - { - letterSpacing: '-0.0015em', - lineHeight: '32px', - }, - ], - 'label-lg': [ - '18px', - { - letterSpacing: '-0.0015em', - lineHeight: '24px', - }, - ], - 'label-md': [ - '16px', - { - letterSpacing: '-0.0011em', - lineHeight: '24px', - }, - ], - 'label-sm': [ - '14px', - { - letterSpacing: '-0.006em', - lineHeight: '20px', - }, - ], - 'label-xs': [ - '12px', - { - letterSpacing: '0', - lineHeight: '16px', - }, - ], - // - 'p-xl': [ - '24px', - { - letterSpacing: '-0.0015em', - lineHeight: '32px', - }, - ], - 'p-lg': [ - '18px', - { - letterSpacing: '-0.0015em', - lineHeight: '24px', - }, - ], - 'p-md': [ - '16px', - { - letterSpacing: '-0.0011em', - lineHeight: '24px', - }, - ], - 'p-sm': [ - '14px', - { - letterSpacing: '-0.006em', - lineHeight: '20px', - }, - ], - 'p-xs': [ - '12px', - { - letterSpacing: '0', - lineHeight: '16px', - }, - ], - // - 'sub-md': [ - '16px', - { - letterSpacing: '0.006em', - lineHeight: '24px', - }, - ], - 'sub-sm': [ - '14px', - { - letterSpacing: '0.006em', - lineHeight: '20px', - }, - ], - 'sub-xs': [ - '12px', - { - letterSpacing: '0.004em', - lineHeight: '16px', - }, - ], - 'sub-2xs': [ - '11px', - { - letterSpacing: '0.002em', - lineHeight: '12px', - }, - ], - }, - extend: { - transitionProperty: { - height: 'height', - spacing: 'margin, padding', - }, - backgroundColor: { - success: 'var(--tw-state-success)', - warning: 'var(--tw-state-warning)', - error: 'var(--tw-state-error)', - feature: 'var(--tw-state-feature)', - neutral: 'var(--tw-state-neutral)', - information: 'var(--tw-state-information)', - verified: 'var(--tw-state-verified)', - away: 'var(--tw-state-away)', + textMain: "var(--tw-neutral-900)", + textSub: "var(--tw-neutral-500)", + textSoft: "var(--tw-neutral-400)", + textDisabled: "var(--tw-neutral-300)", + }, + fontSize: { + "title-1": [ + "56px", + { + letterSpacing: "-0.001em", + lineHeight: "64px", + }, + ], + "title-2": [ + "48px", + { + letterSpacing: "-0.001em", + lineHeight: "56px", + }, + ], + "title-3": [ + "40px", + { + letterSpacing: "-0.001em", + lineHeight: "48px", + }, + ], + "title-4": [ + "32px", + { + letterSpacing: "0", + lineHeight: "40px", + }, + ], + "title-5": [ + "24px", + { + letterSpacing: "0", + lineHeight: "32px", + }, + ], + "title-6": [ + "20px", + { + letterSpacing: "0", + lineHeight: "28px", + }, + ], + // + "label-xl": [ + "24px", + { + letterSpacing: "-0.0015em", + lineHeight: "32px", + }, + ], + "label-lg": [ + "18px", + { + letterSpacing: "-0.0015em", + lineHeight: "24px", + }, + ], + "label-md": [ + "16px", + { + letterSpacing: "-0.0011em", + lineHeight: "24px", + }, + ], + "label-sm": [ + "14px", + { + letterSpacing: "-0.006em", + lineHeight: "20px", + }, + ], + "label-xs": [ + "12px", + { + letterSpacing: "0", + lineHeight: "16px", + }, + ], + // + "p-xl": [ + "24px", + { + letterSpacing: "-0.0015em", + lineHeight: "32px", + }, + ], + "p-lg": [ + "18px", + { + letterSpacing: "-0.0015em", + lineHeight: "24px", + }, + ], + "p-md": [ + "16px", + { + letterSpacing: "-0.0011em", + lineHeight: "24px", + }, + ], + "p-sm": [ + "14px", + { + letterSpacing: "-0.006em", + lineHeight: "20px", + }, + ], + "p-xs": [ + "12px", + { + letterSpacing: "0", + lineHeight: "16px", + }, + ], + // + "sub-md": [ + "16px", + { + letterSpacing: "0.006em", + lineHeight: "24px", + }, + ], + "sub-sm": [ + "14px", + { + letterSpacing: "0.006em", + lineHeight: "20px", + }, + ], + "sub-xs": [ + "12px", + { + letterSpacing: "0.004em", + lineHeight: "16px", + }, + ], + "sub-2xs": [ + "11px", + { + letterSpacing: "0.002em", + lineHeight: "12px", + }, + ], + }, + extend: { + transitionProperty: { + height: "height", + spacing: "margin, padding", + }, + backgroundColor: { + success: "var(--tw-state-success)", + warning: "var(--tw-state-warning)", + error: "var(--tw-state-error)", + feature: "var(--tw-state-feature)", + neutral: "var(--tw-state-neutral)", + information: "var(--tw-state-information)", + verified: "var(--tw-state-verified)", + away: "var(--tw-state-away)", - // 'soft': 'var(--tw-neutral-200)', - soft: 'rgb(226 228 233 / )', - // 'strong': 'var(--tw-neutral-900)', - strong: 'rgb(10 13 20 / )', - // 'surface': 'var(--tw-neutral-700)', - surface: 'rgb(32 35 45 / )', - // 'weak': 'var(--tw-neutral-100)', - weak: 'rgb(249 249 249 / )', - // 'white': 'var(--tw-white)', - white: 'rgb(255 255 255 / )', + // 'soft': 'var(--tw-neutral-200)', + soft: "rgb(226 228 233 / )", + // 'strong': 'var(--tw-neutral-900)', + strong: "rgb(10 13 20 / )", + // 'surface': 'var(--tw-neutral-700)', + surface: "rgb(32 35 45 / )", + // 'weak': 'var(--tw-neutral-100)', + weak: "rgb(249 249 249 / )", + // 'white': 'var(--tw-white)', + white: "rgb(255 255 255 / )", - border: 'var(--tw-stroke-soft)', - }, - textColor: { - main: 'var(--tw-neutral-900)', - sub: 'var(--tw-neutral-500)', - soft: 'var(--tw-neutral-400)', - disabled: 'var(--tw-neutral-300)', - white: 'var(--tw-neutral-0)', - }, - borderColor: { - disabled: 'var(--tw-stroke-disabled)', - white: 'var(--tw-stroke-white)', - sub: 'var(--tw-stroke-sub)', - strong: 'var(--tw-stroke-strong)', - border: 'var(--tw-stroke-soft)', + border: "var(--tw-stroke-soft)", + }, + textColor: { + main: "var(--tw-neutral-900)", + sub: "var(--tw-neutral-500)", + soft: "var(--tw-neutral-400)", + disabled: "var(--tw-neutral-300)", + white: "var(--tw-neutral-0)", + }, + borderColor: { + disabled: "var(--tw-stroke-disabled)", + white: "var(--tw-stroke-white)", + sub: "var(--tw-stroke-sub)", + strong: "var(--tw-stroke-strong)", + border: "var(--tw-stroke-soft)", - success: 'var(--tw-state-success)', - warning: 'var(--tw-state-warning)', - error: 'var(--tw-state-error)', - feature: 'var(--tw-state-feature)', - neutral: 'var(--tw-state-neutral)', - information: 'var(--tw-state-information)', - verified: 'var(--tw-state-verified)', - away: 'var(--tw-state-away)', - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - fontFamily: { - sans: ['var(--font-sans)', ...fontFamily.sans], - mono: ['var(--font-mono)', ...fontFamily.mono], - }, - boxShadow: { - strokeImportant: '0 1px 3px 0 var(--tw-shadow-stroke-important)', - strokePrimary: '0 1px 2px 0 var(--tw-shadow-stroke-primary)', - strokeError: '0 1px 2px 0 var(--tw-shadow-stroke-error)', - focusImportant: - '0 0 0 2px var(--tw-shadow-focus-important), 0 0 0 4px var(--tw-shadow-focus-important)', - focusPrimary: - '0 0 0 2px var(--tw-shadow-focus-primary), 0 0 0 4px var(--tw-shadow-focus-primary)', - focusError: - '0 0 0 2px var(--tw-shadow-focus-error), 0 0 0 4px var(--tw-shadow-focus-error)', + success: "var(--tw-state-success)", + warning: "var(--tw-state-warning)", + error: "var(--tw-state-error)", + feature: "var(--tw-state-feature)", + neutral: "var(--tw-state-neutral)", + information: "var(--tw-state-information)", + verified: "var(--tw-state-verified)", + away: "var(--tw-state-away)", + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans], + mono: ["var(--font-mono)", ...fontFamily.mono], + }, + boxShadow: { + strokeImportant: "0 1px 3px 0 var(--tw-shadow-stroke-important)", + strokePrimary: "0 1px 2px 0 var(--tw-shadow-stroke-primary)", + strokeError: "0 1px 2px 0 var(--tw-shadow-stroke-error)", + focusImportant: + "0 0 0 2px var(--tw-shadow-focus-important), 0 0 0 4px var(--tw-shadow-focus-important)", + focusPrimary: + "0 0 0 2px var(--tw-shadow-focus-primary), 0 0 0 4px var(--tw-shadow-focus-primary)", + focusError: + "0 0 0 2px var(--tw-shadow-focus-error), 0 0 0 4px var(--tw-shadow-focus-error)", - outline: 'var(--tw-shadow-stroke-primary) 0 0 0 6px', - 'outline-lg': 'var(--tw-shadow-stroke-primary) 0 0 0 8px', - }, - keyframes: { - 'accordion-down': { - from: { height: '0' }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: '0' }, - }, - 'collapsible-down': { - from: { height: '0' }, - to: { height: 'var(--radix-collapsible-content-height)' }, - }, - 'collapsible-up': { - from: { height: 'var(--radix-collapsible-content-height)' }, - to: { height: '0' }, - }, - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', - 'collapsible-down': 'collapsible-down 0.2s ease-out', - 'collapsible-up': 'collapsible-up 0.2s ease-out', - }, - }, - }, - plugins: [ - require('tailwindcss-animate'), - gridBackground, - require('@tailwindcss/container-queries'), - ], -} satisfies Omit; + outline: "var(--tw-shadow-stroke-primary) 0 0 0 6px", + "outline-lg": "var(--tw-shadow-stroke-primary) 0 0 0 8px", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + "collapsible-down": { + from: { height: "0" }, + to: { height: "var(--radix-collapsible-content-height)" }, + }, + "collapsible-up": { + from: { height: "var(--radix-collapsible-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + "collapsible-down": "collapsible-down 0.2s ease-out", + "collapsible-up": "collapsible-up 0.2s ease-out", + }, + }, + }, + plugins: [ + require("tailwindcss-animate"), + gridBackground, + require("@tailwindcss/container-queries"), + ], +} satisfies Omit; diff --git a/packages/design/src/ui/accordion.tsx b/packages/design/src/ui/accordion.tsx index a2c52f6..2804abb 100644 --- a/packages/design/src/ui/accordion.tsx +++ b/packages/design/src/ui/accordion.tsx @@ -1,63 +1,63 @@ -'use client'; +"use client"; -import * as AccordionPrimitive from '@radix-ui/react-accordion'; -import { cn } from '~/utils'; -import * as React from 'react'; -import IconChevronDown from './icons/IconChevronDown.tsx'; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { cn } from "~/utils"; +import * as React from "react"; +import IconChevronDown from "./icons/IconChevronDown.tsx"; const Accordion = AccordionPrimitive.Root; const AccordionItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); -AccordionItem.displayName = 'AccordionItem'; +AccordionItem.displayName = "AccordionItem"; const AccordionTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - svg]:rotate-180', - className, - )} - {...props} - > - {children} - - - + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + + + )); AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; const AccordionContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - -
{children}
-
+ +
{children}
+
)); AccordionContent.displayName = AccordionPrimitive.Content.displayName; diff --git a/packages/design/src/ui/aspect-ratio.tsx b/packages/design/src/ui/aspect-ratio.tsx index aaabffb..359bc94 100644 --- a/packages/design/src/ui/aspect-ratio.tsx +++ b/packages/design/src/ui/aspect-ratio.tsx @@ -1,6 +1,6 @@ -'use client'; +"use client"; -import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'; +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; const AspectRatio = AspectRatioPrimitive.Root; diff --git a/packages/design/src/ui/avatar.tsx b/packages/design/src/ui/avatar.tsx index 3fa6080..888d436 100644 --- a/packages/design/src/ui/avatar.tsx +++ b/packages/design/src/ui/avatar.tsx @@ -1,45 +1,48 @@ -'use client'; +"use client"; -import * as AvatarPrimitive from '@radix-ui/react-avatar'; -import { cn } from '~/utils'; -import * as React from 'react'; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; +import { cn } from "~/utils"; +import * as React from "react"; const Avatar = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); Avatar.displayName = AvatarPrimitive.Root.displayName; const AvatarImage = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AvatarImage.displayName = AvatarPrimitive.Image.displayName; const AvatarFallback = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; diff --git a/packages/design/src/ui/badge.tsx b/packages/design/src/ui/badge.tsx index 7aa9f40..cd1fb12 100644 --- a/packages/design/src/ui/badge.tsx +++ b/packages/design/src/ui/badge.tsx @@ -1,28 +1,30 @@ -import { cn } from '~/utils'; -import { cva } from 'class-variance-authority'; -import type { VariantProps } from 'class-variance-authority'; -import * as React from 'react'; +import { cn } from "~/utils"; +import { cva } from "class-variance-authority"; +import type { VariantProps } from "class-variance-authority"; +import type * as React from "react"; const badgeVariants = cva( - 'bg-white inline-flex items-center rounded-full border px-2.5 py-0.5 text-sub-xs font-semibold transition-colors focus-ring', - { - variants: { - variant: { - outline: '', - }, - }, - defaultVariants: { - variant: 'outline', - }, - }, + "bg-white inline-flex items-center rounded-full border px-2.5 py-0.5 text-sub-xs font-semibold transition-colors focus-ring", + { + variants: { + variant: { + outline: "", + }, + }, + defaultVariants: { + variant: "outline", + }, + }, ); export interface BadgeProps - extends React.HTMLAttributes, - VariantProps {} + extends React.HTMLAttributes, + VariantProps {} function Badge({ className, variant, ...props }: BadgeProps) { - return
; + return ( +
+ ); } export { Badge, badgeVariants }; diff --git a/packages/design/src/ui/burger/index.tsx b/packages/design/src/ui/burger/index.tsx index dd33e47..b5e62fa 100644 --- a/packages/design/src/ui/burger/index.tsx +++ b/packages/design/src/ui/burger/index.tsx @@ -1,23 +1,24 @@ -import { cn } from '~/utils'; -import React from 'react'; -import classes from './burger.module.scss'; +import { cn } from "~/utils"; +import React from "react"; +import classes from "./burger.module.scss"; export interface BurgerProps { - onClick?: () => void; - active?: boolean; - title?: string; - className?: string; + onClick?: () => void; + active?: boolean; + title?: string; + className?: string; } export const Burger = ({ onClick, active, title, className }: BurgerProps) => { - return ( - - ); + return ( + + ); }; diff --git a/packages/design/src/ui/button.tsx b/packages/design/src/ui/button.tsx index d09b958..be1110e 100644 --- a/packages/design/src/ui/button.tsx +++ b/packages/design/src/ui/button.tsx @@ -1,105 +1,118 @@ -import { Slot } from '@radix-ui/react-slot'; -import { cn } from '~/utils'; -import type { VariantProps } from 'class-variance-authority'; -import { cva } from 'class-variance-authority'; -import * as React from 'react'; +import { Slot } from "@radix-ui/react-slot"; +import { cn } from "~/utils"; +import type { VariantProps } from "class-variance-authority"; +import { cva } from "class-variance-authority"; +import * as React from "react"; const buttonVariants = cva( - [ - 'group inline-flex items-center justify-center text-label-sm no-underline font-medium transition-colors', - 'disabled:border-disabled data-[disabled=true]:border-disabled disabled:hover:border-disabled data-[disabled=true]:hover:border-disabled', - 'disabled:cursor-not-allowed data-[disabled=true]:cursor-not-allowed', - 'disabled:pointer-events-none data-[disabled=true]:pointer-events-none', - 'disabled:bg-weak data-[disabled=true]:bg-weak', - 'disabled:text-disabled data-[disabled=true]:text-disabled', - ], - { - variants: { - variant: { - // Filled - 'filled-primary': ['bg-primary-base hover:bg-primary-dark', 'text-white'], - 'filled-neutral': ['bg-surface hover:bg-strong', 'text-white'], - 'filled-error': ['bg-red-base hover:bg-red-dark', 'text-white'], + [ + "group inline-flex items-center justify-center text-label-sm no-underline font-medium transition-colors", + "disabled:border-disabled data-[disabled=true]:border-disabled disabled:hover:border-disabled data-[disabled=true]:hover:border-disabled", + "disabled:cursor-not-allowed data-[disabled=true]:cursor-not-allowed", + "disabled:pointer-events-none data-[disabled=true]:pointer-events-none", + "disabled:bg-weak data-[disabled=true]:bg-weak", + "disabled:text-disabled data-[disabled=true]:text-disabled", + ], + { + variants: { + variant: { + // Filled + "filled-primary": [ + "bg-primary-base hover:bg-primary-dark", + "text-white", + ], + "filled-neutral": ["bg-surface hover:bg-strong", "text-white"], + "filled-error": ["bg-red-base hover:bg-red-dark", "text-white"], - // Stroke - 'stroke-primary-main': ['bg-transparent', 'border hover:border-primary-base', 'text-main'], - 'stroke-primary': [ - 'bg-transparent', - 'border hover:border-primary-base', - 'text-primary-base', - ], - stroke: ['bg-transparent', 'border', 'text-main'], + // Stroke + "stroke-primary-main": [ + "bg-transparent", + "border hover:border-primary-base", + "text-main", + ], + "stroke-primary": [ + "bg-transparent", + "border hover:border-primary-base", + "text-primary-base", + ], + stroke: ["bg-transparent", "border", "text-main"], - // Ghost - 'ghost-primary': [ - 'bg-transparent', - 'border border-transparent', - 'text-main hover:text-primary-base', - ], - 'ghost-sub': [ - 'bg-transparent hover:bg-weak', - 'border border-transparent', - 'text-sub hover:text-main', - ], - 'ghost-error': [ - 'bg-transparent hover:bg-weak', - 'border border-transparent', - 'text-red-base hover:text-red-dark', - ], + // Ghost + "ghost-primary": [ + "bg-transparent", + "border border-transparent", + "text-main hover:text-primary-base", + ], + "ghost-sub": [ + "bg-transparent hover:bg-weak", + "border border-transparent", + "text-sub hover:text-main", + ], + "ghost-error": [ + "bg-transparent hover:bg-weak", + "border border-transparent", + "text-red-base hover:text-red-dark", + ], - // Lighter - 'lighter-primary': [ - 'bg-primary-lighter hover:bg-primary-light', - 'border border-transparent', - 'text-primary-base hover:text-primary-dark', - ], - }, - size: { - lg: 'h-12 space-x-3 px-6 py-3', - md: 'h-10 space-x-2 px-4 py-2.5', - sm: 'h-9 space-x-1 px-2.5 py-2', - xs: 'h-8 space-x-0.5 px-1.5 py-1.5', - icon: 'h-10 w-10', - }, - rounded: { - default: 'rounded-md', - full: 'rounded-full', - }, - ring: { - default: [ - 'focus-ring disabled:ring-0 data-[disabled=true]:ring-disabled', - 'data-[state=open]:ring-2 data-[state=open]:ring-shadow-focus-primary data-[state=open]:ring-offset-1', - ], - none: '', - }, - }, - defaultVariants: { - variant: 'filled-primary', - size: 'md', - rounded: 'default', - ring: 'default', - }, - }, + // Lighter + "lighter-primary": [ + "bg-primary-lighter hover:bg-primary-light", + "border border-transparent", + "text-primary-base hover:text-primary-dark", + ], + }, + size: { + lg: "h-12 space-x-3 px-6 py-3", + md: "h-10 space-x-2 px-4 py-2.5", + sm: "h-9 space-x-1 px-2.5 py-2", + xs: "h-8 space-x-0.5 px-1.5 py-1.5", + icon: "h-10 w-10", + }, + rounded: { + default: "rounded-md", + full: "rounded-full", + }, + ring: { + default: [ + "focus-ring disabled:ring-0 data-[disabled=true]:ring-disabled", + "data-[state=open]:ring-2 data-[state=open]:ring-shadow-focus-primary data-[state=open]:ring-offset-1", + ], + none: "", + }, + }, + defaultVariants: { + variant: "filled-primary", + size: "md", + rounded: "default", + ring: "default", + }, + }, ); export interface ButtonProps - extends Omit, 'style'>, - VariantProps { - asChild?: boolean; + extends Omit, "style">, + VariantProps { + asChild?: boolean; } const Button = React.forwardRef( - ({ className, variant, ring, size, rounded, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : 'button'; - return ( - - ); - }, + ( + { className, variant, ring, size, rounded, asChild = false, ...props }, + ref, + ) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, ); -Button.displayName = 'Button'; +Button.displayName = "Button"; export { Button, buttonVariants }; diff --git a/packages/design/src/ui/calendar.tsx b/packages/design/src/ui/calendar.tsx index 5c7c618..c48889b 100644 --- a/packages/design/src/ui/calendar.tsx +++ b/packages/design/src/ui/calendar.tsx @@ -1,104 +1,112 @@ -'use client'; +"use client"; -import { IconChevronLeft } from '~/ui/icons'; -import IconChevronRight from '~/ui/icons/IconChevronRight.tsx'; -import { ScrollArea } from '~/ui/scroll-area.tsx'; -import { cn } from '~/utils'; -import * as React from 'react'; -import { DayPicker } from 'react-day-picker'; -import type { DropdownProps } from 'react-day-picker'; -import { buttonVariants } from './button.tsx'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select.tsx'; +import { IconChevronLeft } from "~/ui/icons"; +import IconChevronRight from "~/ui/icons/IconChevronRight.tsx"; +import { ScrollArea } from "~/ui/scroll-area.tsx"; +import { cn } from "~/utils"; +import * as React from "react"; +import { DayPicker } from "react-day-picker"; +import type { DropdownProps } from "react-day-picker"; +import { buttonVariants } from "./button.tsx"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "./select.tsx"; export type CalendarProps = React.ComponentProps; -const Calendar = React.forwardRef, CalendarProps>( - ({ className, classNames, showOutsideDays = true, ...props }, ref) => { - return ( - , - IconRight: ({ ...props }) => , - Dropdown: ({ value, onChange, children, ...props }: DropdownProps) => { - const options = React.Children.toArray(children) as React.ReactElement< - React.HTMLProps - >[]; - const selected = options.find(child => child.props.value === value); - const handleChange = (value: string) => { - const changeEvent = { - target: { value }, - } as React.ChangeEvent; - onChange?.(changeEvent); - }; - return ( - - ); - }, - }} - {...props} - /> - ); - }, -); -Calendar.displayName = 'Calendar'; +const Calendar = React.forwardRef< + React.ElementRef, + CalendarProps +>(({ className, classNames, showOutsideDays = true, ...props }, ref) => { + return ( + , + IconRight: ({ ...props }) => , + Dropdown: ({ value, onChange, children, ...props }: DropdownProps) => { + const options = React.Children.toArray( + children, + ) as React.ReactElement>[]; + const selected = options.find((child) => child.props.value === value); + const handleChange = (value: string) => { + const changeEvent = { + target: { value }, + } as React.ChangeEvent; + onChange?.(changeEvent); + }; + return ( + + ); + }, + }} + {...props} + /> + ); +}); +Calendar.displayName = "Calendar"; export { Calendar }; diff --git a/packages/design/src/ui/callout/InfoCallout.tsx b/packages/design/src/ui/callout/InfoCallout.tsx index 775a47f..dcefdc5 100644 --- a/packages/design/src/ui/callout/InfoCallout.tsx +++ b/packages/design/src/ui/callout/InfoCallout.tsx @@ -1,22 +1,22 @@ -import { cn } from '~/utils'; -import React from 'react'; -import IconInfoCircle from '../icons/IconInfoCircle.tsx'; -import { Callout, CalloutContent, CalloutIcon } from './index.tsx'; +import { cn } from "~/utils"; +import type React from "react"; +import IconInfoCircle from "../icons/IconInfoCircle.tsx"; +import { Callout, CalloutContent, CalloutIcon } from "./index.tsx"; interface InfoCalloutProps { - children: React.ReactNode; - className?: string; + children: React.ReactNode; + className?: string; } const InfoCallout = ({ children, className }: InfoCalloutProps) => { - return ( - - - - - {children} - - ); + return ( + + + + + {children} + + ); }; export default InfoCallout; diff --git a/packages/design/src/ui/callout/SuccessCallout.tsx b/packages/design/src/ui/callout/SuccessCallout.tsx index af8881d..cf0cc7f 100644 --- a/packages/design/src/ui/callout/SuccessCallout.tsx +++ b/packages/design/src/ui/callout/SuccessCallout.tsx @@ -1,22 +1,22 @@ -import IconCheck from '~/ui/icons/IconCheck.tsx'; -import { cn } from '~/utils'; -import React from 'react'; -import { Callout, CalloutContent, CalloutIcon } from './index.tsx'; +import IconCheck from "~/ui/icons/IconCheck.tsx"; +import { cn } from "~/utils"; +import type React from "react"; +import { Callout, CalloutContent, CalloutIcon } from "./index.tsx"; interface InfoCalloutProps { - children: React.ReactNode; - className?: string; + children: React.ReactNode; + className?: string; } const SuccessCallout = ({ children, className }: InfoCalloutProps) => { - return ( - - - - - {children} - - ); + return ( + + + + + {children} + + ); }; export default SuccessCallout; diff --git a/packages/design/src/ui/callout/index.tsx b/packages/design/src/ui/callout/index.tsx index b8c0116..fc0fa40 100644 --- a/packages/design/src/ui/callout/index.tsx +++ b/packages/design/src/ui/callout/index.tsx @@ -1,44 +1,53 @@ -import { cn } from '~/utils'; -import * as React from 'react'; +import { cn } from "~/utils"; +import * as React from "react"; -const Callout = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ), -); -Callout.displayName = 'Callout'; +const Callout = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Callout.displayName = "Callout"; const CalloutIcon = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)); -CalloutIcon.displayName = 'CalloutIcon'; +CalloutIcon.displayName = "CalloutIcon"; const CalloutContent = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+
)); -CalloutContent.displayName = 'CalloutContent'; +CalloutContent.displayName = "CalloutContent"; export { Callout, CalloutIcon, CalloutContent }; diff --git a/packages/design/src/ui/card.tsx b/packages/design/src/ui/card.tsx index 71e2aaa..9e42199 100644 --- a/packages/design/src/ui/card.tsx +++ b/packages/design/src/ui/card.tsx @@ -1,58 +1,79 @@ -import { cn } from '~/utils'; -import * as React from 'react'; +import { cn } from "~/utils"; +import * as React from "react"; const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & { withShadow?: boolean } + HTMLDivElement, + React.HTMLAttributes & { withShadow?: boolean } >(({ className, withShadow = true, ...props }, ref) => ( -
+
)); -Card.displayName = 'Card'; +Card.displayName = "Card"; -const CardHeader = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ), -); -CardHeader.displayName = 'CardHeader'; +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; -const CardTitle = React.forwardRef>( - ({ className, ...props }, ref) => ( -

- ), -); -CardTitle.displayName = 'CardTitle'; +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardTitle.displayName = "CardTitle"; const CardDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes + HTMLParagraphElement, + React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+

)); -CardDescription.displayName = 'CardDescription'; +CardDescription.displayName = "CardDescription"; -const CardContent = React.forwardRef>( - ({ className, ...props }, ref) =>

, -); -CardContent.displayName = 'CardContent'; +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardContent.displayName = "CardContent"; -const CardFooter = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ), -); -CardFooter.displayName = 'CardFooter'; +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; -export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }; +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/packages/design/src/ui/checkbox-card.tsx b/packages/design/src/ui/checkbox-card.tsx index 42c3429..bae058c 100644 --- a/packages/design/src/ui/checkbox-card.tsx +++ b/packages/design/src/ui/checkbox-card.tsx @@ -1,52 +1,61 @@ -'use client'; +"use client"; -import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; -import IconCheck from '~/ui/icons/IconCheck.tsx'; -import { cn } from '~/utils'; -import * as React from 'react'; +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import IconCheck from "~/ui/icons/IconCheck.tsx"; +import { cn } from "~/utils"; +import * as React from "react"; type CheckboxProps = Omit< - React.ComponentPropsWithoutRef, - 'children' + React.ComponentPropsWithoutRef, + "children" > & { - title: string | React.ReactNode; - description: string | React.ReactNode; - left?: React.ReactNode; + title: string | React.ReactNode; + description: string | React.ReactNode; + left?: React.ReactNode; }; -const Checkbox = ({ className }: React.ComponentPropsWithoutRef) => { - return ( -
- - - -
- ); +const Checkbox = ({ + className, +}: React.ComponentPropsWithoutRef) => { + return ( +
+ + + +
+ ); }; const CheckboxCard = React.forwardRef< - React.ElementRef, - CheckboxProps + React.ElementRef, + CheckboxProps >(({ left, title, description, className, ...props }, ref) => ( - - {left &&
{left}
} -
-
- {title} -
-

{description}

-
- -
+ + {left &&
{left}
} +
+
+ + {title} + +
+

+ {description} +

+
+ +
)); CheckboxCard.displayName = CheckboxPrimitive.Root.displayName; diff --git a/packages/design/src/ui/checkbox.tsx b/packages/design/src/ui/checkbox.tsx index 129b1d7..d0eeada 100644 --- a/packages/design/src/ui/checkbox.tsx +++ b/packages/design/src/ui/checkbox.tsx @@ -1,26 +1,28 @@ -'use client'; +"use client"; -import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; -import IconCheck from '~/ui/icons/IconCheck.tsx'; -import { cn } from '~/utils'; -import * as React from 'react'; +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import IconCheck from "~/ui/icons/IconCheck.tsx"; +import { cn } from "~/utils"; +import * as React from "react"; const Checkbox = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - - - - - + + + + + )); Checkbox.displayName = CheckboxPrimitive.Root.displayName; diff --git a/packages/design/src/ui/collapsible.tsx b/packages/design/src/ui/collapsible.tsx index f33bd96..5af3447 100644 --- a/packages/design/src/ui/collapsible.tsx +++ b/packages/design/src/ui/collapsible.tsx @@ -1,29 +1,31 @@ -'use client'; +"use client"; -import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'; -import { cn } from '~/utils'; -import * as React from 'react'; +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; +import { cn } from "~/utils"; +import * as React from "react"; const Collapsible = CollapsiblePrimitive.Root; const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; const CollapsibleContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - animate?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef< + typeof CollapsiblePrimitive.CollapsibleContent + > & { + animate?: boolean; + } >(({ className, animate, ...props }, ref) => ( - + )); -CollapsibleContent.displayName = 'CollapsibleContent'; +CollapsibleContent.displayName = "CollapsibleContent"; export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/packages/design/src/ui/color-pill.tsx b/packages/design/src/ui/color-pill.tsx index 907285b..f8bd923 100644 --- a/packages/design/src/ui/color-pill.tsx +++ b/packages/design/src/ui/color-pill.tsx @@ -1,17 +1,20 @@ -import { cn } from '~/utils'; -import React from 'react'; +import { cn } from "~/utils"; +import type React from "react"; export interface ColorPillProps extends React.HTMLAttributes { - // CSS color value - color: string; + // CSS color value + color: string; } export const ColorPill = ({ color, className, ...props }: ColorPillProps) => { - return ( -
- ); + return ( +
+ ); }; diff --git a/packages/design/src/ui/daypicker.tsx b/packages/design/src/ui/daypicker.tsx index 85c6ae8..6d42e47 100644 --- a/packages/design/src/ui/daypicker.tsx +++ b/packages/design/src/ui/daypicker.tsx @@ -1,53 +1,54 @@ -'use client'; +"use client"; -import { Button } from '~/ui/button.tsx'; -import IconCalendar from '~/ui/icons/IconCalendar.tsx'; -import { cn } from '~/utils'; -import * as React from 'react'; -import { Calendar } from './calendar.tsx'; -import { Popover, PopoverContent, PopoverTrigger } from './popover.tsx'; +import { Button } from "~/ui/button.tsx"; +import IconCalendar from "~/ui/icons/IconCalendar.tsx"; +import { cn } from "~/utils"; +import * as React from "react"; +import { Calendar } from "./calendar.tsx"; +import { Popover, PopoverContent, PopoverTrigger } from "./popover.tsx"; interface DayPickerProps { - date: Date | undefined; - setDate: (date: Date | string | undefined) => void; - format: (date: Date | string | undefined) => string; - disabled?: boolean; - className?: string; - calendarProps?: React.ComponentProps; + date: Date | undefined; + setDate: (date: Date | string | undefined) => void; + format: (date: Date | string | undefined) => string; + disabled?: boolean; + className?: string; + calendarProps?: React.ComponentProps; } -const DayPicker = React.forwardRef, DayPickerProps>( - ({ date, setDate, format, disabled, className, calendarProps }, ref) => { - return ( - - - - - - - - - ); - }, -); -DayPicker.displayName = 'DayPicker'; +const DayPicker = React.forwardRef< + React.ElementRef, + DayPickerProps +>(({ date, setDate, format, disabled, className, calendarProps }, ref) => { + return ( + + + + + + + + + ); +}); +DayPicker.displayName = "DayPicker"; export default DayPicker; diff --git a/packages/design/src/ui/dialog.tsx b/packages/design/src/ui/dialog.tsx index 4508d32..b85354b 100644 --- a/packages/design/src/ui/dialog.tsx +++ b/packages/design/src/ui/dialog.tsx @@ -1,9 +1,9 @@ -'use client'; +"use client"; -import * as DialogPrimitive from '@radix-ui/react-dialog'; -import { cn } from '~/utils'; -import * as React from 'react'; -import IconX from './icons/IconX.tsx'; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { cn } from "~/utils"; +import * as React from "react"; +import IconX from "./icons/IconX.tsx"; const Dialog = DialogPrimitive.Root; @@ -12,91 +12,106 @@ const DialogTrigger = DialogPrimitive.Trigger; const DialogPortal = DialogPrimitive.Portal; const DialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - + + + + {children} + + + Close + + + )); DialogContent.displayName = DialogPrimitive.Content.displayName; -const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
+const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
); -DialogHeader.displayName = 'DialogHeader'; +DialogHeader.displayName = "DialogHeader"; -const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
+const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
); -DialogFooter.displayName = 'DialogFooter'; +DialogFooter.displayName = "DialogFooter"; const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogTitle.displayName = DialogPrimitive.Title.displayName; const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DialogDescription.displayName = DialogPrimitive.Description.displayName; export { - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogFooter, - DialogTitle, - DialogDescription, + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, }; diff --git a/packages/design/src/ui/dropdown-menu.tsx b/packages/design/src/ui/dropdown-menu.tsx index 9ea0064..9295f65 100644 --- a/packages/design/src/ui/dropdown-menu.tsx +++ b/packages/design/src/ui/dropdown-menu.tsx @@ -1,11 +1,11 @@ -'use client'; +"use client"; -import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; -import { cn } from '~/utils'; -import * as React from 'react'; -import IconCheck from './icons/IconCheck.tsx'; -import IconChevronRight from './icons/IconChevronRight.tsx'; -import IconCircle from './icons/IconCircle.tsx'; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { cn } from "~/utils"; +import * as React from "react"; +import IconCheck from "./icons/IconCheck.tsx"; +import IconChevronRight from "./icons/IconChevronRight.tsx"; +import IconCircle from "./icons/IconCircle.tsx"; const DropdownMenu = DropdownMenuPrimitive.Root; @@ -20,188 +20,200 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub; const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, children, ...props }, ref) => ( - -
{children}
- -
+ +
{children}
+ +
)); -DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName; +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName; const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); -DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName; +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName; const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, sideOffset = 4, ...props }, ref) => ( - - - + + + )); DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } >(({ className, inset, ...props }, ref) => ( - + )); DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - + + + + + + + {children} + )); -DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName; +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName; const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, children, ...props }, ref) => ( - - - - - - - {children} - + + + + + + + {children} + )); DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )); DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; -const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => { - return ( - - ); +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); }; -DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'; +DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; export { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup, + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, }; diff --git a/packages/design/src/ui/dropzone.tsx b/packages/design/src/ui/dropzone.tsx index a184ebd..805c248 100644 --- a/packages/design/src/ui/dropzone.tsx +++ b/packages/design/src/ui/dropzone.tsx @@ -1,71 +1,77 @@ // https://github.com/shadcn-ui/ui/issues/163#issuecomment-1871434185 -import { Input } from '~/ui/input.tsx'; -import { cn } from '~/utils/cn'; -import type { ChangeEvent } from 'react'; -import React, { useRef } from 'react'; +import { Input } from "~/ui/input.tsx"; +import { cn } from "~/utils/cn"; +import type { ChangeEvent } from "react"; +import React, { useRef } from "react"; interface DropzoneProps - extends Omit, 'value' | 'onChange'> { - className?: string; - children?: React.ReactNode; - handleOnDrop: (acceptedFiles: FileList | null) => void; - accept?: string; + extends Omit< + React.InputHTMLAttributes, + "value" | "onChange" + > { + className?: string; + children?: React.ReactNode; + handleOnDrop: (acceptedFiles: FileList | null) => void; + accept?: string; } const Dropzone = React.forwardRef( - ({ className, children, handleOnDrop, accept, ...props }, ref) => { - const inputRef = useRef(null); - // Function to handle drag over event - const handleDragOver = (e: React.DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - handleOnDrop(null); - }; + ({ className, children, handleOnDrop, accept, ...props }, ref) => { + const inputRef = useRef(null); + // Function to handle drag over event + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + handleOnDrop(null); + }; - // Function to handle drop event - const handleDrop = (e: React.DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - const { files } = e.dataTransfer; - if (inputRef.current) { - inputRef.current.files = files; - handleOnDrop(files); - } - }; + // Function to handle drop event + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + const { files } = e.dataTransfer; + if (inputRef.current) { + inputRef.current.files = files; + handleOnDrop(files); + } + }; - // Function to simulate a click on the file input element - const handleButtonClick = () => { - if (inputRef.current) { - inputRef.current.click(); - } - }; - return ( -
- ) => handleOnDrop(e.target.files)} - /> + // Function to simulate a click on the file input element + const handleButtonClick = () => { + if (inputRef.current) { + inputRef.current.click(); + } + }; + return ( + // biome-ignore lint/a11y/useKeyWithClickEvents: +
+ ) => + handleOnDrop(e.target.files) + } + /> - {children} -
- ); - }, + {children} +
+ ); + }, ); -Dropzone.displayName = 'Dropzone'; +Dropzone.displayName = "Dropzone"; export default Dropzone; diff --git a/packages/design/src/ui/form.tsx b/packages/design/src/ui/form.tsx index b5941ec..d4d92d8 100644 --- a/packages/design/src/ui/form.tsx +++ b/packages/design/src/ui/form.tsx @@ -1,176 +1,195 @@ -import type * as LabelPrimitive from '@radix-ui/react-label'; -import { Slot } from '@radix-ui/react-slot'; -import { cn } from '~/utils'; -import * as React from 'react'; -import { Controller, FormProvider, useFormContext } from 'react-hook-form'; -import type { ControllerProps, FieldPath, FieldValues } from 'react-hook-form'; -import { Label } from './label.tsx'; +import type * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { cn } from "~/utils"; +import * as React from "react"; +import { Controller, FormProvider, useFormContext } from "react-hook-form"; +import type { ControllerProps, FieldPath, FieldValues } from "react-hook-form"; +import { Label } from "./label.tsx"; const Form = FormProvider; interface FormFieldContextValue< - TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, > { - name: TName; + name: TName; } -const FormFieldContext = React.createContext({} as FormFieldContextValue); +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); const FormField = < - TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, >({ - ...props + ...props }: ControllerProps) => { - return ( - - - - ); + return ( + + + + ); }; const useFormField = () => { - const fieldContext = React.useContext(FormFieldContext); - const itemContext = React.useContext(FormItemContext); - const { getFieldState, formState } = useFormContext(); - - const fieldState = getFieldState(fieldContext.name, formState); - - if (!fieldContext) { - throw new Error('useFormField should be used within '); - } - - const { id } = itemContext; - - return { - id, - name: fieldContext.name, - formItemId: `${id}-form-item`, - formDescriptionId: `${id}-form-item-description`, - formMessageId: `${id}-form-item-message`, - ...fieldState, - }; + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; }; interface FormItemContextValue { - id: string; + id: string; } -const FormItemContext = React.createContext({} as FormItemContextValue); +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); -const FormItem = React.forwardRef>( - ({ className, ...props }, ref) => { - const id = React.useId(); +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); - return ( - -
- - ); - }, -); -FormItem.displayName = 'FormItem'; + return ( + +
+ + ); +}); +FormItem.displayName = "FormItem"; const FormLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef + React.ElementRef, + React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { - const { formItemId } = useFormField(); - - return ( -