diff --git a/@acme/.git_ b/@acme/.git_ deleted file mode 100644 index 8913c447..00000000 --- a/@acme/.git_ +++ /dev/null @@ -1 +0,0 @@ -gitdir: ../.git/modules/@acme diff --git a/@acme/.gitignore b/@acme/.gitignore deleted file mode 100644 index 4dbf58ae..00000000 --- a/@acme/.gitignore +++ /dev/null @@ -1,57 +0,0 @@ -# dependencies -node_modules -.pnp -.pnp.js - -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? -*.pem - -# Local files -*.local - -# next.js -.next/ -out/ -next-env.d.ts -# expo -.expo/ -dist/ -expo-env.d.ts -apps/expo/.gitignore -# tauri -dist -dist-ssr - -# production -build - -# testing -coverage - -# typescript -*.tsbuildinfo - -# turbo -.turbo - -# vercel -.vercel - diff --git a/@acme/README.md b/@acme/README.md deleted file mode 100644 index 2342395f..00000000 --- a/@acme/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# vyductan-react - diff --git a/@acme/eslint/base.js b/@acme/eslint/base.js deleted file mode 100644 index e69de29b..00000000 diff --git a/@acme/eslint/drizzle.js b/@acme/eslint/drizzle.js deleted file mode 100644 index 3d5793b3..00000000 --- a/@acme/eslint/drizzle.js +++ /dev/null @@ -1,7 +0,0 @@ -/** @type {import('eslint').Linter.Config} */ -const config = { - extends: ["plugin:drizzle/recommended"], - plugins: ["drizzle"], -}; - -module.exports = config; diff --git a/@acme/eslint/nextjs.js b/@acme/eslint/nextjs.js deleted file mode 100644 index b876fc84..00000000 --- a/@acme/eslint/nextjs.js +++ /dev/null @@ -1,17 +0,0 @@ -import nextPlugin from "@next/eslint-plugin-next"; - -/** @type {Awaited} */ -export default [ - { - files: ["**/*.ts", "**/*.tsx"], - plugins: { - "@next/next": nextPlugin, - }, - rules: { - ...nextPlugin.configs.recommended.rules, - ...nextPlugin.configs["core-web-vitals"].rules, - // TypeError: context.getAncestors is not a function - "@next/next/no-duplicate-head": "off", - }, - }, -]; diff --git a/@acme/eslint/package.json b/@acme/eslint/package.json deleted file mode 100644 index 3af409f2..00000000 --- a/@acme/eslint/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/package.json", - "name": "@acme/eslint-config", - "version": "0.3.0", - "private": true, - "type": "module", - "exports": { - "./base": "./base.js", - "./nextjs": "./nextjs.js", - "./react": "./react.js", - "./tailwind": "./tailwind.js" - }, - "scripts": { - "clean": "git clean -xdf .cache .turbo node_modules", - "format": "prettier --check . --ignore-path ../../.gitignore", - "typecheck": "tsc --noEmit" - }, - "prettier": "@acme/prettier-config", - "dependencies": { - "@eslint/compat": "^1.1.1", - "@next/eslint-plugin-next": "^14.2.5", - "eslint-plugin-drizzle": "^0.2.3", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsx-a11y": "^6.9.0", - "eslint-plugin-react": "^7.35.0", - "eslint-plugin-react-hooks": "rc", - "eslint-plugin-tailwindcss": "^3.17.4", - "eslint-plugin-turbo": "^2.0.14", - "typescript-eslint": "^8.2.0" - }, - "devDependencies": { - "@acme/prettier-config": "workspace:*", - "@acme/tsconfig": "workspace:*", - "eslint": "catalog:", - "eslint-plugin-unicorn": "^55.0.0", - "prettier": "catalog:", - "typescript": "catalog:" - } -} diff --git a/@acme/eslint/react.js b/@acme/eslint/react.js deleted file mode 100644 index abbf8f1d..00000000 --- a/@acme/eslint/react.js +++ /dev/null @@ -1,22 +0,0 @@ -import reactPlugin from "eslint-plugin-react"; -import hooksPlugin from "eslint-plugin-react-hooks"; - -/** @type {Awaited} */ -export default [ - { - files: ["**/*.ts", "**/*.tsx"], - plugins: { - react: reactPlugin, - "react-hooks": hooksPlugin, - }, - rules: { - ...reactPlugin.configs["jsx-runtime"].rules, - ...hooksPlugin.configs.recommended.rules, - }, - languageOptions: { - globals: { - React: "writable", - }, - }, - }, -]; diff --git a/@acme/eslint/tailwind.js b/@acme/eslint/tailwind.js deleted file mode 100644 index b31249b8..00000000 --- a/@acme/eslint/tailwind.js +++ /dev/null @@ -1,37 +0,0 @@ -import * as path from "node:path"; -import { fileURLToPath } from "url"; -// @ts-expect-error -import tailwindPlugin from "eslint-plugin-tailwindcss"; - -const __filename = fileURLToPath(import.meta.url); -const config = path.join(import.meta.dirname, "../tailwind/web.ts"); - -/** @type {Awaited} */ -export default [ - ...tailwindPlugin.configs["flat/recommended"], - { - // plugins: { - // tailwindcss: tailwindPlugin, - // }, - // rules: { - // ...tailwindPlugin.rules, - // }, - settings: { - tailwindcss: { - callees: ["clsm", "cva", "cx"], - config, - // config: fileURLToPath( - // new URL("../tailwind/web.ts", "file://" + __filename), - // ), - cssFiles: [ - "**/*.{css,scss}", - "!**/node_modules", - "!**/.*", - "!**/dist", - "!**/build", - ], - whitelist: ["_.+"], - }, - }, - }, -]; diff --git a/@acme/eslint/tsconfig.json b/@acme/eslint/tsconfig.json deleted file mode 100644 index 528357af..00000000 --- a/@acme/eslint/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig.json", - "extends": "@acme/tsconfig/base.json", - "include": ["."], - "exclude": ["node_modules"] -} diff --git a/@acme/eslint/types.d.ts b/@acme/eslint/types.d.ts deleted file mode 100644 index f0dba6d0..00000000 --- a/@acme/eslint/types.d.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Since the ecosystem hasn't fully migrated to ESLint's new FlatConfig system yet, - * we "need" to type some of the plugins manually :( - */ - -declare module "@eslint/js" { - // Why the hell doesn't eslint themselves export their types? - import type { Linter } from "eslint"; - - export const configs: { - readonly recommended: { readonly rules: Readonly }; - readonly all: { readonly rules: Readonly }; - }; -} - -declare module "eslint-plugin-import" { - import type { Linter, Rule } from "eslint"; - - export const configs: { - recommended: { rules: Linter.RulesRecord }; - }; - export const rules: Record; -} - -declare module "eslint-plugin-react" { - import type { Linter, Rule } from "eslint"; - - export const configs: { - recommended: { rules: Linter.RulesRecord }; - all: { rules: Linter.RulesRecord }; - "jsx-runtime": { rules: Linter.RulesRecord }; - }; - export const rules: Record; -} - -declare module "eslint-plugin-react-hooks" { - import type { Linter, Rule } from "eslint"; - - export const configs: { - recommended: { - rules: { - "rules-of-hooks": Linter.RuleEntry; - "exhaustive-deps": Linter.RuleEntry; - }; - }; - }; - export const rules: Record; -} - -declare module "@next/eslint-plugin-next" { - import type { Linter, Rule } from "eslint"; - - export const configs: { - recommended: { rules: Linter.RulesRecord }; - "core-web-vitals": { rules: Linter.RulesRecord }; - }; - export const rules: Record; -} - -declare module "eslint-plugin-turbo" { - import type { Linter, Rule } from "eslint"; - - export const configs: { - recommended: { rules: Linter.RulesRecord }; - }; - export const rules: Record; -} diff --git a/@acme/eslint/unicorn.js b/@acme/eslint/unicorn.js deleted file mode 100644 index e01901a5..00000000 --- a/@acme/eslint/unicorn.js +++ /dev/null @@ -1,31 +0,0 @@ -import eslintPluginUnicorn from "eslint-plugin-unicorn"; - -export default [ - eslintPluginUnicorn.configs["flat/recommended"], - { - rules: { - "unicorn/prevent-abbreviations": [ - "error", - { - allowList: { - args: true, - Args: true, - def: true, - Def: true, - ref: true, - Ref: true, - params: true, - Params: true, - prev: true, - Prev: true, - prop: true, - Prop: true, - props: true, - Props: true, - }, - }, - ], - "unicorn/no-nested-ternary": "off", - }, - }, -]; diff --git a/@acme/github/package.json b/@acme/github/package.json deleted file mode 100644 index 54217d67..00000000 --- a/@acme/github/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@acme/github" -} diff --git a/@acme/github/setup/action.yml b/@acme/github/setup/action.yml deleted file mode 100644 index 6e29909b..00000000 --- a/@acme/github/setup/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: "Setup and install" -description: "Common setup steps for Actions" - -runs: - using: composite - steps: - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "pnpm" - - - shell: bash - run: pnpm add -g turbo - - - shell: bash - run: pnpm install diff --git a/@acme/hooks/eslint.config.js b/@acme/hooks/eslint.config.js deleted file mode 100644 index fea4eba5..00000000 --- a/@acme/hooks/eslint.config.js +++ /dev/null @@ -1,11 +0,0 @@ -import baseConfig from "@acme/eslint-config/base"; -import reactConfig from "@acme/eslint-config/react"; - -/** @type {import('typescript-eslint').Config} */ -export default [ - { - ignores: ["dist/**"], - }, - ...baseConfig, - ...reactConfig, -]; diff --git a/@acme/hooks/package.json b/@acme/hooks/package.json deleted file mode 100644 index 4ff74703..00000000 --- a/@acme/hooks/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/package.json", - "name": "@acme/hooks", - "version": "0.1.0", - "private": true, - "type": "module", - "exports": { - "./*": { - "types": "./dist/src/*/index.d.ts", - "default": "./src/*/index.ts" - }, - "./next/*": { - "types": "./dist/src/next/*/index.d.ts", - "default": "./src/next/*/index.ts" - } - }, - "scripts": { - "build": "tsc", - "clean": "git clean -xdf .cache .turbo dist node_modules", - "dev": "tsc", - "format": "prettier --check . --ignore-path ../../.gitignore", - "lint": "eslint", - "typecheck": "tsc --noEmit --emitDeclarationOnly false" - }, - "prettier": "@acme/prettier-config", - "dependencies": { - "@tanstack/react-router": "^1.49.1" - }, - "devDependencies": { - "@acme/eslint-config": "workspace:*", - "@acme/prettier-config": "workspace:*", - "@acme/tsconfig": "workspace:*", - "@types/react": "catalog:react18", - "@types/react-dom": "catalog:react18", - "eslint": "catalog:", - "prettier": "catalog:", - "react": "catalog:react18", - "typescript": "catalog:" - } -} diff --git a/@acme/hooks/src/next/useNavigate/index.ts b/@acme/hooks/src/next/useNavigate/index.ts deleted file mode 100644 index 54231d72..00000000 --- a/@acme/hooks/src/next/useNavigate/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { - RegisteredRouter, - UseNavigateResult, -} from "@tanstack/react-router"; -import { useRouter } from "next/navigation"; - -export const useNavigate = < - TDefaultFrom extends string = string, ->(_defaultOptions?: { - from?: RegisteredRouter["routeTree"]; - // from?: RoutePathsAutoComplete; - // -}): UseNavigateResult => { - const router = useRouter(); - const navigate: UseNavigateResult = async (parameters) => { - const href = parameters.to; - if (href) router.push(href); - }; - return navigate; -}; diff --git a/@acme/hooks/src/use-at-bottom/index.ts b/@acme/hooks/src/use-at-bottom/index.ts deleted file mode 100644 index cf8624d2..00000000 --- a/@acme/hooks/src/use-at-bottom/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from "react"; - -export function useAtBottom(offset = 0) { - const [isAtBottom, setIsAtBottom] = React.useState(false); - - React.useEffect(() => { - const handleScroll = () => { - setIsAtBottom( - window.innerHeight + window.scrollY >= - document.body.offsetHeight - offset, - ); - }; - - window.addEventListener("scroll", handleScroll, { passive: true }); - handleScroll(); - - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, [offset]); - - return isAtBottom; -} diff --git a/@acme/hooks/src/use-local-storage/index.ts b/@acme/hooks/src/use-local-storage/index.ts deleted file mode 100644 index 4062c57a..00000000 --- a/@acme/hooks/src/use-local-storage/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect, useState } from "react"; - -export const useLocalStorage = ( - key: string, - initialValue: T, -): [T, (value: T) => void] => { - const [storedValue, setStoredValue] = useState(initialValue); - - useEffect(() => { - // Retrieve from localStorage - const item = window.localStorage.getItem(key); - if (item) { - setStoredValue(JSON.parse(item)); - } - }, [key]); - - const setValue = (value: T) => { - // Save state - setStoredValue(value); - // Save to localStorage - window.localStorage.setItem(key, JSON.stringify(value)); - }; - return [storedValue, setValue]; -}; diff --git a/@acme/hooks/src/use-resize-observer/index.ts b/@acme/hooks/src/use-resize-observer/index.ts deleted file mode 100644 index db11197b..00000000 --- a/@acme/hooks/src/use-resize-observer/index.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from "react"; - -type ObserverRect = Omit; - -const defaultState: ObserverRect = { - x: 0, - y: 0, - width: 0, - height: 0, - top: 0, - left: 0, - bottom: 0, - right: 0, -}; - -export function useResizeObserver() { - const frameID = useRef(0); - const ref = useRef(null); - - const [rect, setRect] = useState(defaultState); - - const observer = useMemo( - () => - typeof window === "undefined" - ? null - : new ResizeObserver((entries) => { - const entry = entries[0]; - - if (entry) { - cancelAnimationFrame(frameID.current); - - frameID.current = requestAnimationFrame(() => { - if (ref.current) { - setRect(entry.contentRect); - } - }); - } - }), - [], - ); - - useEffect(() => { - if (ref.current) { - observer?.observe(ref.current); - } - - return () => { - observer?.disconnect(); - - if (frameID.current) { - cancelAnimationFrame(frameID.current); - } - }; - }, [observer]); - - return { ref, rect }; -} diff --git a/@acme/hooks/src/useEnterSubmit/index.ts b/@acme/hooks/src/useEnterSubmit/index.ts deleted file mode 100644 index 181b4e08..00000000 --- a/@acme/hooks/src/useEnterSubmit/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { RefObject } from "react"; -import { useRef } from "react"; - -export function useEnterSubmit(): { - formRef: RefObject; - onKeyDown: (event: React.KeyboardEvent) => void; -} { - const formRef = useRef(null); - - const handleKeyDown = ( - event: React.KeyboardEvent, - ): void => { - if ( - event.key === "Enter" && - !event.shiftKey && - !event.nativeEvent.isComposing - ) { - formRef.current?.requestSubmit(); - event.preventDefault(); - } - }; - - return { formRef, onKeyDown: handleKeyDown }; -} diff --git a/@acme/hooks/src/useResponsive/index.ts b/@acme/hooks/src/useResponsive/index.ts deleted file mode 100644 index 0c42e274..00000000 --- a/@acme/hooks/src/useResponsive/index.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-condition */ -import { useEffect, useState } from "react"; -import { theme } from "tailwindcss/defaultConfig"; - -import isBrowser from "../utils/is-browser"; - -type Subscriber = () => void; - -const subscribers = new Set(); - -type Screens = "xs" | "sm" | "md" | "lg" | "xl" | "2xl"; -type ResponsiveInfo = Record; - -let info: ResponsiveInfo; - -const tailwindScreensConfig = theme?.screens as Record; - -let responsiveConfig = (() => { - const c: Record = {}; - Object.keys(tailwindScreensConfig).map((x) => { - c[x] = Number(tailwindScreensConfig[x]?.replace("px", "")); - }); - return { - xs: 0, - ...c, - }; -})() as Record; -type ResponsiveConfig = typeof responsiveConfig; - -function handleResize() { - const oldInfo = info; - calculate(); - if (oldInfo === info) return; - for (const subscriber of subscribers) { - subscriber(); - } -} - -let listening = false; - -function calculate() { - const width = window.innerWidth; - const newInfo = {} as ResponsiveInfo; - let shouldUpdate = false; - for (const key of Object.keys(responsiveConfig) as Screens[]) { - newInfo[key] = width >= responsiveConfig[key]; - if (newInfo[key] !== info[key]) { - shouldUpdate = true; - } - } - if (shouldUpdate) { - info = newInfo; - } -} - -export function configResponsive(config: ResponsiveConfig) { - responsiveConfig = config; - if (info) calculate(); -} - -export const useResponsive = () => { - if (isBrowser && !listening) { - info = { - xs: false, - sm: false, - md: false, - lg: false, - xl: false, - "2xl": false, - }; - calculate(); - window.addEventListener("resize", handleResize); - listening = true; - } - const [state, setState] = useState(info); - - useEffect(() => { - if (!isBrowser) return; - - // In React 18's StrictMode, useEffect perform twice, resize listener is remove, so handleResize is never perform. - // https://github.com/alibaba/hooks/issues/1910 - if (!listening) { - window.addEventListener("resize", handleResize); - } - - const subscriber = () => { - setState(info); - }; - - subscribers.add(subscriber); - return () => { - subscribers.delete(subscriber); - if (subscribers.size === 0) { - window.removeEventListener("resize", handleResize); - listening = false; - } - }; - }, []); - - return state; -}; diff --git a/@acme/hooks/src/useSearchParams/index.ts_ b/@acme/hooks/src/useSearchParams/index.ts_ deleted file mode 100644 index 5c26f54d..00000000 --- a/@acme/hooks/src/useSearchParams/index.ts_ +++ /dev/null @@ -1,7 +0,0 @@ -import { router, useSearch } from "@tanstack/react-router"; - -export const useSearchParams = () => { - const x = useSearch(); - - return [searchparam]; -}; diff --git a/@acme/hooks/src/utils/is-browser.ts b/@acme/hooks/src/utils/is-browser.ts deleted file mode 100644 index 1f9154df..00000000 --- a/@acme/hooks/src/utils/is-browser.ts +++ /dev/null @@ -1,5 +0,0 @@ -// May 16, 2022 -const isBrowser = - typeof window !== "undefined" && !!window.document.createElement; - -export default isBrowser; diff --git a/@acme/hooks/tsconfig.json b/@acme/hooks/tsconfig.json deleted file mode 100644 index 1ff89735..00000000 --- a/@acme/hooks/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig.json", - "extends": "@acme/tsconfig/internal-package.json", - "compilerOptions": { - "lib": ["ES2022", "dom", "dom.iterable"], - "rootDir": "." - }, - "include": ["src"], - "exclude": ["node_modules"] -} diff --git a/@acme/prettier/index.js b/@acme/prettier/index.js deleted file mode 100644 index 1ad267b8..00000000 --- a/@acme/prettier/index.js +++ /dev/null @@ -1,48 +0,0 @@ -import { fileURLToPath } from "url"; - -/** @typedef {import("prettier").Config} PrettierConfig */ -/** @typedef {import("prettier-plugin-tailwindcss").PluginOptions} TailwindConfig */ -/** @typedef {import("@ianvs/prettier-plugin-sort-imports").PluginConfig} SortImportsConfig */ - -/** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */ -const config = { - plugins: [ - "@ianvs/prettier-plugin-sort-imports", - "prettier-plugin-packagejson", - "prettier-plugin-tailwindcss", - ], - tailwindConfig: fileURLToPath(new URL("../tailwind/web.ts", import.meta.url)), - importOrder: [ - "", - "^(react/(.*)$)|^(react$)|^(react-native(.*)$)", - "^(next/(.*)$)|^(next$)", - "^(expo(.*)$)|^(expo$)", - "", - "", - "^@acme", - "^@acme/(.*)$", - "", - "^[.|..|~]", - "^~/", - "^[../]", - "^[./]", - ], - importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"], - importOrderTypeScriptVersion: "4.4.0", - overrides: [ - { - files: "*.json.hbs", - options: { - parser: "json", - }, - }, - { - files: "*.js.hbs", - options: { - parser: "babel", - }, - }, - ], -}; - -export default config; diff --git a/@acme/prettier/package.json b/@acme/prettier/package.json deleted file mode 100644 index abf8cfb6..00000000 --- a/@acme/prettier/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/package.json", - "name": "@acme/prettier-config", - "version": "0.1.0", - "private": true, - "type": "module", - "exports": { - ".": "./index.js" - }, - "scripts": { - "clean": "git clean -xdf .cache .turbo node_modules", - "format": "prettier --check . --ignore-path ../../.gitignore", - "typecheck": "tsc --noEmit" - }, - "prettier": "@acme/prettier-config", - "dependencies": { - "@ianvs/prettier-plugin-sort-imports": "^4.3.1", - "prettier": "catalog:", - "prettier-plugin-packagejson": "^2.5.0", - "prettier-plugin-tailwindcss": "^0.6.6" - }, - "devDependencies": { - "@acme/tsconfig": "workspace:*", - "typescript": "catalog:" - } -} diff --git a/@acme/prettier/tsconfig.json b/@acme/prettier/tsconfig.json deleted file mode 100644 index 528357af..00000000 --- a/@acme/prettier/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig.json", - "extends": "@acme/tsconfig/base.json", - "include": ["."], - "exclude": ["node_modules"] -} diff --git a/@acme/tailwind/base.ts b/@acme/tailwind/base.ts deleted file mode 100644 index 9bbacd05..00000000 --- a/@acme/tailwind/base.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { Config } from "tailwindcss"; -import type { - RecursiveKeyValuePair, - ResolvableTo, -} from "tailwindcss/types/config"; -import { addDynamicIconSelectors } from "@iconify/tailwind"; -import { fontSize } from "tailwindcss/defaultTheme"; - -const baseColors = { - surface: { - DEFAULT: "oklch(var(--ds-background-100) / )", - secondary: "oklch(var(--ds-background-200) / )", - }, - primary: { - DEFAULT: "hsl(var(--primary-600))", - hover: "hsl(var(--primary-700))", - foreground: "hsl(var(--primary-foreground))", - 100: "hsl(var(--primary-100))", - 200: "hsl(var(--primary-200))", - 300: "hsl(var(--primary-300))", - 400: "hsl(var(--primary-400))", - 500: "hsl(var(--primary-500))", - 600: "hsl(var(--primary-600))", - 700: "hsl(var(--primary-700))", - 800: "hsl(var(--primary-800))", - 900: "hsl(var(--primary-900))", - 950: "hsl(var(--primary-950))", - }, - gray: { - 100: "oklch(var(--ds-gray-100) / )", - 200: "oklch(var(--ds-gray-200) / )", - 300: "oklch(var(--ds-gray-300) / )", - 400: "oklch(var(--ds-gray-400) / )", - 500: "oklch(var(--ds-gray-500) / )", - 600: "oklch(var(--ds-gray-600) / )", - 700: "oklch(var(--ds-gray-700) / )", - 800: "oklch(var(--ds-gray-800) / )", - 900: "oklch(var(--ds-gray-900) / )", - 950: "oklch(var(--ds-gray-950) / )", - }, - blue: { - 100: "oklch(var(--ds-blue-100) / )", - 200: "oklch(var(--ds-blue-200) / )", - 300: "oklch(var(--ds-blue-300) / )", - 400: "oklch(var(--ds-blue-400) / )", - 500: "oklch(var(--ds-blue-500) / )", - 600: "oklch(var(--ds-blue-600) / )", - 700: "oklch(var(--ds-blue-700) / )", - 800: "oklch(var(--ds-blue-800) / )", - 900: "oklch(var(--ds-blue-900) / )", - 950: "oklch(var(--ds-blue-950) / )", - }, - green: { - 100: "oklch(var(--ds-green-100) / )", - 200: "oklch(var(--ds-green-200) / )", - 300: "oklch(var(--ds-green-300) / )", - 400: "oklch(var(--ds-green-400) / )", - 500: "oklch(var(--ds-green-500) / )", - 600: "oklch(var(--ds-green-600) / )", - 700: "oklch(var(--ds-green-700) / )", - 800: "oklch(var(--ds-green-800) / )", - 900: "oklch(var(--ds-green-900) / )", - 950: "oklch(var(--ds-green-950) / )", - }, - red: { - 100: "oklch(var(--ds-red-100) / )", - 200: "oklch(var(--ds-red-200) / )", - 300: "oklch(var(--ds-red-300) / )", - 400: "oklch(var(--ds-red-400) / )", - 500: "oklch(var(--ds-red-500) / )", - 600: "oklch(var(--ds-red-600) / )", - 700: "oklch(var(--ds-red-700) / )", - 800: "oklch(var(--ds-red-800) / )", - 900: "oklch(var(--ds-red-900) / )", - 950: "oklch(var(--ds-red-950) / )", - }, - teal: { - 100: "oklch(var(--ds-teal-100) / )", - 200: "oklch(var(--ds-teal-200) / )", - 300: "oklch(var(--ds-teal-300) / )", - 400: "oklch(var(--ds-teal-400) / )", - 500: "oklch(var(--ds-teal-500) / )", - 600: "oklch(var(--ds-teal-600) / )", - 700: "oklch(var(--ds-teal-700) / )", - 800: "oklch(var(--ds-teal-800) / )", - 900: "oklch(var(--ds-teal-900) / )", - 950: "oklch(var(--ds-teal-950) / )", - }, -} satisfies ResolvableTo; - -export default { - darkMode: ["class"], - content: ["src/**/*.{ts,tsx}"], - theme: { - extend: { - colors: { - ...baseColors, - foreground: { - DEFAULT: "hsl(var(--foreground))", - muted: { - DEFAULT: baseColors.gray[900], - }, - }, - background: { - DEFAULT: baseColors.gray[100], - hover: baseColors.gray[200], - active: baseColors.gray[300], - muted: { - DEFAULT: baseColors.gray[100], - }, - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: baseColors.blue[600], - hover: baseColors.blue[700], - foreground: "hsl(var(--accent-foreground))", - muted: { - DEFAULT: baseColors.blue[100], - }, - }, - error: { - DEFAULT: baseColors.red[800], - hover: baseColors.red[900], - muted: { - DEFAULT: baseColors.red[100], - hover: baseColors.red[200], - }, - }, - success: { - DEFAULT: baseColors.green[600], - hover: baseColors.green[700], - muted: { - DEFAULT: baseColors.green[100], - }, - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - - border: { - DEFAULT: baseColors.gray[400], - hover: baseColors.gray[500], - active: baseColors.gray[600], - }, - input: "hsl(var(--input))", - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - placeholder: baseColors.gray[700], - ring: "hsl(var(--ring))", - }, - /* End Colors */ - - fontSize: { - md: fontSize.base, - }, - // width: { - // "screen-sm": "640px", - // "screen-md": "1024px", - // }, - width: (pluginUtils) => ({ - ...pluginUtils.breakpoints(pluginUtils.theme("screens")), - // ...theme('spacing'), - // none: 'none', - // xs: '20rem', - // sm: '24rem', - // md: '28rem', - // lg: '32rem', - // xl: '36rem', - // '2xl': '42rem', - // '3xl': '48rem', - // '4xl': '56rem', - // '5xl': '64rem', - // '6xl': '72rem', - // '7xl': '80rem', - // full: '100%', - // min: 'min-content', - // max: 'max-content', - // fit: 'fit-content', - // prose: '65ch', - // ...breakpoints(theme('screens')), - }), - /** - * Colors - */ - textColor: { - description: "hsl(var(--text-description))", - }, - backgroundColor: { - warning: "hsl(var(--warning-bg))", - }, - borderColor: { - warning: "hsl(var(--warning-border))", - }, - }, - }, - plugins: [addDynamicIconSelectors()], -} satisfies Config; diff --git a/@acme/tailwind/eslint.config.js b/@acme/tailwind/eslint.config.js deleted file mode 100644 index e185014e..00000000 --- a/@acme/tailwind/eslint.config.js +++ /dev/null @@ -1,6 +0,0 @@ -// FIXME: This kinda stinks... -/// - -import baseConfig from "@acme/eslint-config/base"; - -export default [...baseConfig]; diff --git a/@acme/tailwind/globals.css b/@acme/tailwind/globals.css deleted file mode 100644 index 9dc86685..00000000 --- a/@acme/tailwind/globals.css +++ /dev/null @@ -1,294 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-sm text-foreground; - } -} - -@layer base { - :root { - --primary-100: 202 100% 95%; - --primary-200: 204 100% 82%; - --primary-300: 206 100% 74%; - --primary-400: 208 100% 66%; - --primary-500: 210 100% 58%; - --primary-600: 212 100% 48%; - --primary-700: 214 100% 40%; - --primary-800: 216 100% 33%; - --primary-900: 218 100% 25%; - --primary-950: 220 100% 17%; - --ds-background-100: 100% 0 0; - --ds-background-200: 98.51% 0 0; - --ds-gray-100: 96.19% 0 0; - --ds-gray-200: 93.89% 0 0; - --ds-gray-300: 92.34% 0 0; - --ds-gray-400: 93.89% 0 0; - --ds-gray-500: 83.73% 0 0; - --ds-gray-600: 73.26% 0 0; - --ds-gray-700: 64.94% 0 0; - --ds-gray-800: 58.95% 0 0; - --ds-gray-900: 51.03% 0 0; - --ds-gray-950: 20.44% 0 0; - --ds-amber-100: 97.48% 0.0331 85.79; - --ds-amber-200: 96.81% 0.0495 90.24227879900472; - --ds-amber-300: 95.93% 0.0636 90.52; - --ds-amber-400: 91.02% 0.1322 88.25; - --ds-amber-500: 86.55% 0.1583 79.63; - --ds-amber-600: 80.25% 0.1953 73.59; - --ds-amber-700: 81.87% 0.1969 76.46; - --ds-amber-800: 77.21% 0.1991 64.28; - --ds-amber-900: 52.79% 0.1496 54.65; - --ds-amber-950: 30.83% 0.099 45.48; - --ds-blue-100: 97.32% 0.0141 251.56; - --ds-blue-200: 96.29% 0.0195 250.59; - --ds-blue-300: 94.58% 0.0293 249.84870859673202; - --ds-blue-400: 91.58% 0.0473 245.11621922481282; - --ds-blue-500: 82.75% 0.0979 248.48; - --ds-blue-600: 73.08% 0.1583 248.133320980386; - --ds-blue-700: 57.61% 0.2508 258.23; - --ds-blue-800: 51.51% 0.2399 257.85; - --ds-blue-900: 53.18% 0.2399 256.9900584162342; - --ds-blue-950: 26.67% 0.1099 254.34; - --ds-green-100: 97.59% 0.0289 145.42; - --ds-green-200: 96.92% 0.037 147.15; - --ds-green-300: 94.6% 0.0674 144.23; - --ds-green-400: 91.49% 0.0976 146.24; - --ds-green-500: 85.45% 0.1627 146.3; - --ds-green-600: 80.25% 0.214 145.18; - --ds-green-700: 64.58% 0.1746 147.27; - --ds-green-800: 57.81% 0.1507 147.5; - --ds-green-900: 51.75% 0.1453 147.65; - --ds-green-950: 29.15% 0.1197 147.38; - --ds-red-100: 96.5% 0.0223 13.09; - --ds-red-200: 95.41% 0.0299 14.252646656611997; - --ds-red-300: 94.33% 0.0369 15.011509923860523; - --ds-red-400: 91.51% 0.0471 19.8; - --ds-red-500: 84.47% 0.1018 17.71; - --ds-red-600: 71.12% 0.1881 21.22; - --ds-red-700: 62.56% 0.2524 23.03; - --ds-red-800: 58.19% 0.2482 25.15; - --ds-red-900: 54.99% 0.232 25.29; - --ds-red-950: 24.8% 0.1041 18.86; - --ds-pink-100: 95.69% 0.0359 344.6218910697224; - --ds-pink-200: 95.71% 0.0321 353.14; - --ds-pink-300: 93.83% 0.0451 356.29; - --ds-pink-400: 91.12% 0.0573 358.82; - --ds-pink-500: 84.28% 0.0915 356.99; - --ds-pink-600: 74.33% 0.1547 0.24; - --ds-pink-700: 63.52% 0.238 1.01; - --ds-pink-800: 59.51% 0.2339 4.21; - --ds-pink-900: 53.5% 0.2058 2.84; - --ds-pink-950: 26% 0.0977 359; - --ds-purple-100: 96.65% 0.0244 312.1890119359961; - --ds-purple-200: 96.73% 0.0228 309.8; - --ds-purple-300: 94.85% 0.0364 310.15; - --ds-purple-400: 91.77% 0.0614 312.82; - --ds-purple-500: 81.26% 0.1409 310.8; - --ds-purple-600: 72.07% 0.2083 308.19; - --ds-purple-700: 55.5% 0.3008 306.12; - --ds-purple-800: 48.58% 0.2638 305.73; - --ds-purple-900: 47.18% 0.2579 304; - --ds-purple-950: 23.96% 0.13 305.66; - --ds-teal-100: 97.72% 0.0359 186.7; - --ds-teal-200: 97.06% 0.0347 180.66; - --ds-teal-300: 94.92% 0.0478 182.07; - --ds-teal-400: 92.76% 0.0718 183.78; - --ds-teal-500: 86.88% 0.1344 182.42; - --ds-teal-600: 81.5% 0.161 178.96; - --ds-teal-700: 64.92% 0.1572 181.95; - --ds-teal-800: 57.53% 0.1392 181.66; - --ds-teal-900: 52.08% 0.1251 182.93; - --ds-teal-950: 32.11% 0.0788 179.82; - - --accents-500: 0 0% 40%; - - --gray-100: 0 0% 95%; - --gray-700: 0 0% 56%; - - --background-200: 0 0% 98%; - --background: 0 0% 100%; - --background-hover: 0 0% 95%; - --background-disabled: var(--gray-100); - - --foreground: 0 0% 9%; - - --secondary: var(--accents-500); - - --popover: 0 0% 100%; - --popover-foreground: 0 0% 3.9%; - - /* --primary-foreground: 0 0% 98%; */ - --primary-foreground: flush-orange-600; - - --muted: 0 0% 96.1%; - --muted-foreground: 0 0% 45.1%; - - --accent: 0 0% 96.1%; - --accent-foreground: 0 0% 9%; - - --error: 358 70% 52%; - --error-bg: 358 70% 52%; - --error-active: 358 73% 28%; - --error-hover: 358 66% 48%; - - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - - --warning: 40 96% 53%; - --warning-bg: 50 100% 95%; - --warning-border: 46 100% 78%; - - --border: hsla(0 0% 0% / 0.08); - --border-hover: hsla(0 0% 0% / 0.12); - - --ring: 0 0% 3.9%; - --radius: 0.375rem; - --placeholder: var(--gray-700); - - --link: 14 79% 62%; - --input: 0 0% 89.8%; - - --text-description: 0 0% 0% / 45%; - } - - .dark { - --primary-100: 216 100% 5%; - --primary-200: 204 90% 80%; - --primary-300: 206 89% 72%; - --primary-400: 208 86% 63%; - --primary-500: 210 79% 52%; - --primary-600: 212 100% 41%; - --primary-700: 212 100% 31%; - --primary-800: 212 100% 21%; - --primary-900: 212 100% 14%; - --primary-950: 214 100% 10%; - --ds-background-100: 14.48% 0 0; - --ds-background-200: 0% 0 0; - --ds-gray-100: 0, 0%, 10%; - --ds-gray-200: 0, 0%, 12%; - --ds-gray-300: 0, 0%, 16%; - --ds-gray-400: 0, 0%, 18%; - --ds-gray-500: 0, 0%, 27%; - --ds-gray-600: 0, 0%, 53%; - --ds-gray-700: 0, 0%, 56%; - --ds-gray-800: 0, 0%, 49%; - --ds-gray-900: 0, 0%, 63%; - --ds-gray-950: 0, 0%, 93%; - --ds-amber-100: 22.46% 0.0538 76.04; - --ds-amber-200: 24.95% 0.0642 64.78; - --ds-amber-300: 32.34% 0.0837 63.83; - --ds-amber-400: 35.53% 0.0903 66.29707162673735; - --ds-amber-500: 41.55% 0.1044 67.98; - --ds-amber-600: 75.04% 0.1737 74.49; - --ds-amber-700: 81.87% 0.1969 76.46; - --ds-amber-800: 77.21% 0.1991 64.28; - --ds-amber-900: 77.21% 0.1991 64.28; - --ds-amber-950: 96.7% 0.0418 84.59; - --ds-blue-100: 22.17% 0.069 259.89; - --ds-blue-200: 25.45% 0.0811 255.8; - --ds-blue-300: 30.86% 0.1022 255.21; - --ds-blue-400: 34.1% 0.121 254.74; - --ds-blue-500: 38.5% 0.1403 254.4; - --ds-blue-600: 64.94% 0.1982 251.8131841760864; - --ds-blue-700: 57.61% 0.2321 258.23; - --ds-blue-800: 51.51% 0.2307 257.85; - --ds-blue-900: 71.7% 0.1648 250.79360374054167; - --ds-blue-950: 96.75% 0.0179 242.4234217368056; - --ds-green-100: 23.09% 0.0716 149.68; - --ds-green-200: 27.12% 0.0895 150.09; - --ds-green-300: 29.84% 0.096 149.25; - --ds-green-400: 34.39% 0.1039 147.78; - --ds-green-500: 44.19% 0.1484 147.2; - --ds-green-600: 58.11% 0.1815 146.55; - --ds-green-700: 64.58% 0.199 147.27; - --ds-green-800: 57.81% 0.1776 147.5; - --ds-green-900: 73.1% 0.2158 148.29; - --ds-green-950: 96.76% 0.056 154.18; - --ds-red-100: 22.1% 0.0657 15.11; - --ds-red-200: 25.93% 0.0834 19.02; - --ds-red-300: 31.47% 0.1105 20.96; - --ds-red-400: 35.27% 0.1273 21.23; - --ds-red-500: 40.68% 0.1479 23.16; - --ds-red-600: 62.56% 0.2277 23.03; - --ds-red-700: 62.56% 0.2234 23.03; - --ds-red-800: 58.01% 0.227 25.12; - --ds-red-900: 69.96% 0.2136 22.03; - --ds-red-950: 95.6% 0.0293 6.61; - --ds-pink-100: 22.67% 0.0628 354.73; - --ds-pink-200: 26.2% 0.0859 356.68; - --ds-pink-300: 31.15% 0.1067 355.93; - --ds-pink-400: 32.13% 0.1174 356.71; - --ds-pink-500: 37.01% 0.1453 358.39; - --ds-pink-600: 50.33% 0.2089 4.33; - --ds-pink-700: 63.52% 0.2346 1.01; - --ds-pink-800: 59.51% 0.2429 4.21; - --ds-pink-900: 69.36% 0.2223 3.91; - --ds-pink-950: 95.74% 0.0326 350.08; - --ds-purple-100: 22.34% 0.0779 316.87; - --ds-purple-200: 25.91% 0.0921 314.41; - --ds-purple-300: 31.98% 0.1219 312.41; - --ds-purple-400: 35.93% 0.1504 309.78; - --ds-purple-500: 40.99% 0.1721 307.92; - --ds-purple-600: 55.5% 0.2191 306.12; - --ds-purple-700: 55.5% 0.2186 306.12; - --ds-purple-800: 48.58% 0.2102 305.73; - --ds-purple-900: 69.87% 0.2037 309.51; - --ds-purple-950: 96.1% 0.0304 316.46; - --ds-teal-100: 22.1% 0.0544 178.74; - --ds-teal-200: 25.06% 0.062 178.76; - --ds-teal-300: 31.5% 0.0767 180.99; - --ds-teal-400: 32.43% 0.0763 180.13; - --ds-teal-500: 43.35% 0.1055 180.97; - --ds-teal-600: 60.71% 0.1485 180.24; - --ds-teal-700: 64.92% 0.1403 181.95; - --ds-teal-800: 57.53% 0.1392 181.66; - --ds-teal-900: 74.56% 0.1765 182.8; - --ds-teal-950: 96.46% 0.056 180.29; - - --accents-500: 0 0% 53%; - - --gray-100: 0 0% 10%; - --gray-700: 0 0% 56%; - - --background-200: 0 0% 0%; - --background: 0 0% 4%; - --background-hover: 0 0% 12%; - - --foreground: 0 0% 98%; - - --secondary: var(--accents-500); - - --popover: 0 0% 3.9%; - --popover-foreground: 0 0% 98%; - - --primary-foreground: 0 0% 9%; - - --muted: 0 0% 14.9%; - --muted-foreground: 0 0% 63.9%; - - --accent: 0 0% 14.9%; - --accent-foreground: 0 0% 98%; - - --error: 358 69% 52%; - --error-active: 5 100% 82%; - --error-hover: 358 100% 69%; - - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - - --border: hsla(0 0% 100% / 0.14); - --border-hover: hsla(0 0% 100% / 0.17); - - /* --border: 0 0% 89.8%; */ - --input: 0 0% 14.9%; - --ring: 0 0% 83.1%; - - --text-description: 0 0% 100% / 45%; - } -} diff --git a/@acme/tailwind/package.json b/@acme/tailwind/package.json deleted file mode 100644 index f6edc1e7..00000000 --- a/@acme/tailwind/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/package.json", - "name": "@acme/tailwind-config", - "version": "0.1.0", - "private": true, - "license": "MIT", - "type": "module", - "exports": { - "./web": "./web.ts", - "./globals.css": "./globals.css", - "./postcss.cjs": "./postcss.cjs" - }, - "scripts": { - "clean": "git clean -xdf .cache .turbo node_modules", - "format": "prettier --check . --ignore-path ../../.gitignore", - "lint": "eslint", - "typecheck": "tsc --noEmit" - }, - "prettier": "@acme/prettier-config", - "dependencies": { - "postcss": "8.4.41", - "tailwindcss": "catalog:", - "tailwindcss-animate": "^1.0.7" - }, - "devDependencies": { - "@acme/eslint-config": "workspace:*", - "@acme/prettier-config": "workspace:*", - "@acme/tsconfig": "workspace:*", - "@iconify/tailwind": "^1.1.1", - "eslint": "catalog:", - "prettier": "catalog:", - "typescript": "catalog:" - } -} diff --git a/@acme/tailwind/postcss.cjs b/@acme/tailwind/postcss.cjs deleted file mode 100644 index 4cdb2f43..00000000 --- a/@acme/tailwind/postcss.cjs +++ /dev/null @@ -1,7 +0,0 @@ -const config = { - plugins: { - tailwindcss: {}, - }, -}; - -module.exports = config; diff --git a/@acme/tailwind/token.ts b/@acme/tailwind/token.ts deleted file mode 100644 index 20ed0164..00000000 --- a/@acme/tailwind/token.ts +++ /dev/null @@ -1,27 +0,0 @@ -// import { theme } from "antd"; -// import type { GlobalToken } from "antd"; - -// import defaultColors from "tailwindcss/colors"; - -/** - * Colors - * Button bg - default: 600, hover: 500 - * - */ -export const THEME_TOKEN = { - // ...theme.getDesignToken(), - // colorPrimary: defaultColors.blue[600], - // colorPrimaryBorder: defaultColors.blue[300], - // colorPrimaryHover: defaultColors.blue[500], - // - // colorText: defaultColors.gray[600], - // - colorTextDisabled: "#00000040", - // colorBorder: defaultColors.gray[200], - // colorBorderSecondary: defaultColors.gray[200], - controlHeightSM: 32, - controlHeight: 40, - controlHeightLG: 48, - // colorError: defaultColors.red[500], -}; -// } satisfies GlobalToken; diff --git a/@acme/tailwind/tsconfig.json b/@acme/tailwind/tsconfig.json deleted file mode 100644 index 528357af..00000000 --- a/@acme/tailwind/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig.json", - "extends": "@acme/tsconfig/base.json", - "include": ["."], - "exclude": ["node_modules"] -} diff --git a/@acme/tailwind/web.ts b/@acme/tailwind/web.ts deleted file mode 100644 index ecd72f86..00000000 --- a/@acme/tailwind/web.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Config } from "tailwindcss"; -import animate from "tailwindcss-animate"; - -import base from "./base"; - -export default { - content: base.content, - presets: [base], - theme: { - container: { - center: true, - padding: "2rem", - screens: { - "2xl": "1400px", - }, - }, - extend: { - borderRadius: { - sm: `calc(var(--radius) - 2px)`, - md: `var(--radius)`, - lg: `calc(var(--radius) + 2px)`, - }, - 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" }, - }, - /* Vercel */ - "skeleton-loading": { - "0%": { backgroundPosition: "200% 0" }, - "100%": { backgroundPosition: "-200% 0" }, - }, - }, - animation: { - "accordion-down": "accordion-down 0.2s ease-out", - "accordion-up": "accordion-up 0.2s ease-out", - "skeleton-loading": "skeleton-loading 8s ease-in-out infinite", - }, - }, - }, - plugins: [animate], -} satisfies Config; diff --git a/@acme/typescript/base.json b/@acme/typescript/base.json deleted file mode 100644 index 805687dd..00000000 --- a/@acme/typescript/base.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "compilerOptions": { - /** Base Options */ - "esModuleInterop": true, - "skipLibCheck": true, - "target": "ES2022", - "lib": ["ES2022"], - "allowJs": true, - "resolveJsonModule": true, - "moduleDetection": "force", - "isolatedModules": true, - - /** Keep TSC performant in monorepos */ - "incremental": true, - "disableSourceOfProjectReferenceRedirect": true, - "tsBuildInfoFile": "${configDir}/.cache/tsbuildinfo.json", - - /** Strictness */ - "strict": true, - "noUncheckedIndexedAccess": true, - "checkJs": true, - - /** Transpile using Bundler (not tsc) */ - "module": "Preserve", - "moduleResolution": "Bundler", - "noEmit": true - }, - "exclude": ["node_modules", "build", "dist", ".next", ".expo"] -} diff --git a/@acme/typescript/internal-package.json b/@acme/typescript/internal-package.json deleted file mode 100644 index f20c57bf..00000000 --- a/@acme/typescript/internal-package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "./base.json", - "compilerOptions": { - /** Emit types for internal packages to speed up editor performance. */ - "declaration": true, - "declarationMap": true, - "emitDeclarationOnly": true, - "noEmit": false, - "outDir": "${configDir}/dist" - } -} diff --git a/@acme/typescript/package.json b/@acme/typescript/package.json deleted file mode 100644 index 447de4d1..00000000 --- a/@acme/typescript/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/package.json", - "name": "@acme/tsconfig", - "version": "0.1.0", - "private": true, - "files": [ - "*.json" - ] -} diff --git a/@acme/ui/eslint.config.js b/@acme/ui/eslint.config.js deleted file mode 100644 index 0fade271..00000000 --- a/@acme/ui/eslint.config.js +++ /dev/null @@ -1,13 +0,0 @@ -import baseConfig from "@acme/eslint-config/base"; -import reactConfig from "@acme/eslint-config/react"; -import tailwindConfig from "@acme/eslint-config/tailwind"; - -/** @type {import('typescript-eslint').Config} */ -export default [ - { - ignores: ["dist/**"], - }, - ...baseConfig, - ...reactConfig, - ...tailwindConfig, -]; diff --git a/@acme/ui/package.json b/@acme/ui/package.json deleted file mode 100644 index 4d035596..00000000 --- a/@acme/ui/package.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/package.json", - "name": "@acme/ui", - "version": "0.1.0", - "private": true, - "license": "MIT", - "type": "module", - "exports": { - ".": { - "types": "./dist/src/index.d.ts", - "default": "./src/index.ts" - }, - "./*": { - "types": "./dist/src/*/index.d.ts", - "default": "./src/*/index.ts" - }, - "./pro/*": { - "types": "./dist/src/pro/*/index.d.ts", - "default": "./src/pro/*/index.ts" - } - }, - "scripts": { - "build": "tsc", - "clean": "git clean -xdf .cache .turbo dist node_modules", - "dev": "tsc", - "format": "prettier --check . --ignore-path ../../.gitignore", - "lint": "eslint", - "trace": "tsc --generateTrace .", - "typecheck": "tsc --noEmit --emitDeclarationOnly false" - }, - "prettier": "@acme/prettier-config", - "dependencies": { - "@acme/hooks": "workspace:*", - "@acme/utils": "workspace:*", - "@hookform/resolvers": "^3.9.0", - "@iconify/json": "^2.2.239", - "@lexical/react": "^0.17.0", - "@radix-ui/react-accordion": "^1.2.0", - "@radix-ui/react-alert-dialog": "^1.1.1", - "@radix-ui/react-avatar": "^1.1.0", - "@radix-ui/react-checkbox": "^1.1.1", - "@radix-ui/react-dialog": "^1.1.1", - "@radix-ui/react-dropdown-menu": "^2.1.1", - "@radix-ui/react-label": "^2.1.0", - "@radix-ui/react-navigation-menu": "^1.2.0", - "@radix-ui/react-popover": "^1.1.1", - "@radix-ui/react-radio-group": "^1.2.0", - "@radix-ui/react-scroll-area": "^1.1.0", - "@radix-ui/react-select": "^2.1.1", - "@radix-ui/react-separator": "^1.1.0", - "@radix-ui/react-slot": "^1.1.0", - "@radix-ui/react-switch": "^1.1.0", - "@radix-ui/react-tabs": "^1.1.0", - "@radix-ui/react-tooltip": "^1.1.2", - "@rc-component/mini-decimal": "^1.1.0", - "@tanstack/react-table": "^8.20.1", - "@typescript/analyze-trace": "^0.10.1", - "ahooks": "^3.8.1", - "class-variance-authority": "^0.7.0", - "cmdk": "^1.0.0", - "date-fns": "^3.6.0", - "embla-carousel-react": "^8.2.0", - "framer-motion": "^11.3.28", - "lexical": "^0.17.0", - "next-themes": "^0.3.0", - "rc-util": "^5.43.0", - "react-day-picker": "^9.0.7", - "react-dropzone": "^14.2.3", - "react-hook-form": "^7.52.2", - "react-markdown": "^9.0.1", - "react-resizable-panels": "^2.1.0", - "react-router-dom": "^6.26.1", - "react-syntax-highlighter": "^15.5.0", - "react-textarea-autosize": "^8.5.3", - "sonner": "^1.5.0", - "tailwind-merge": "^2.5.2", - "usehooks-ts": "^3.1.0", - "zustand": "^4.5.4" - }, - "devDependencies": { - "@acme/eslint-config": "workspace:*", - "@acme/prettier-config": "workspace:*", - "@acme/tsconfig": "workspace:*", - "@types/lodash": "^4.17.7", - "@types/react": "catalog:react18", - "@types/react-syntax-highlighter": "^15.5.11", - "eslint": "catalog:", - "prettier": "catalog:", - "react": "catalog:react18", - "typescript": "catalog:", - "zod": "catalog:" - }, - "peerDependencies": { - "react": "catalog:react18", - "zod": "catalog:" - } -} diff --git a/@acme/ui/src/_util/event.ts b/@acme/ui/src/_util/event.ts deleted file mode 100644 index d9da6289..00000000 --- a/@acme/ui/src/_util/event.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * inspired by https://stackoverflow.com/a/71340077 - * triggerNativeEventFor(inputRef.current, { event: 'input', value: '' }); - * triggerNativeEventFor(checkBoxRef.current, { event: 'input', checked: false }); - */ -export function triggerNativeEventFor( - element: T, - { - event, - ...valueObject - }: { event: keyof HTMLElementEventMap; [key: string]: string }, -) { - if (!(element instanceof Element)) { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new TypeError(`Expected an Element but received ${element} instead!`); - } - - const [prop, value] = Object.entries(valueObject)[0] ?? []; - - const prototype = Object.getPrototypeOf(element); - const desc = Object.getOwnPropertyDescriptor(prototype, prop!); - - desc?.set?.call(element, value); - element.dispatchEvent(new Event(event as string, { bubbles: true })); -} diff --git a/@acme/ui/src/_util/translation/index.ts b/@acme/ui/src/_util/translation/index.ts deleted file mode 100644 index e4eb18de..00000000 --- a/@acme/ui/src/_util/translation/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const t = (keyword: string, ns: Record) => { - return ns[keyword]; -}; -export type TranslationFn = typeof t; diff --git a/@acme/ui/src/_util/wave/WaveEffect.tsx b/@acme/ui/src/_util/wave/WaveEffect.tsx deleted file mode 100644 index 394f3c8d..00000000 --- a/@acme/ui/src/_util/wave/WaveEffect.tsx +++ /dev/null @@ -1,181 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-condition */ -import * as React from "react"; -import { cubicBezier, motion } from "framer-motion"; -import raf from "rc-util/lib/raf"; -import { render, unmount } from "rc-util/lib/React/render"; - -import type { ShowWaveEffect, WaveAllowedComponent } from "./interface"; -import { getTargetWaveColor, isNotGrey } from "./util"; - -function validateNumber(value: number) { - return Number.isNaN(value) ? 0 : value; -} - -export interface WaveEffectProps { - className: string; - target: HTMLElement; - component?: WaveAllowedComponent; -} - -const WaveEffect: React.FC = (props) => { - const { className, target, component } = props; - const divRef = React.useRef(null); - - const [color, setWaveColor] = React.useState(null); - const [borderRadius, setBorderRadius] = React.useState([]); - const [left, setLeft] = React.useState(0); - const [top, setTop] = React.useState(0); - const [width, setWidth] = React.useState(0); - const [height, setHeight] = React.useState(0); - const [enabled, setEnabled] = React.useState(false); - - const waveStyle = { - left, - top, - width, - height, - borderRadius: borderRadius.map((radius) => `${radius}px`).join(" "), - } as React.CSSProperties & Record; - - // if (color) { - // waveStyle["--wave-color"] = color; - // } - - function syncPos() { - const nodeStyle = getComputedStyle(target); - - // Get wave color from target - setWaveColor(getTargetWaveColor(target)); - - const isStatic = nodeStyle.position === "static"; - - // Rect - const { borderLeftWidth, borderTopWidth } = nodeStyle; - setLeft( - isStatic ? target.offsetLeft : validateNumber(-Number.parseFloat(borderLeftWidth)), - ); - setTop( - isStatic ? target.offsetTop : validateNumber(-Number.parseFloat(borderTopWidth)), - ); - setWidth(target.offsetWidth); - setHeight(target.offsetHeight); - - // Get border radius - const { - borderTopLeftRadius, - borderTopRightRadius, - borderBottomLeftRadius, - borderBottomRightRadius, - } = nodeStyle; - - setBorderRadius( - [ - borderTopLeftRadius, - borderTopRightRadius, - borderBottomRightRadius, - borderBottomLeftRadius, - ].map((radius) => validateNumber(Number.parseFloat(radius))), - ); - } - - React.useEffect(() => { - if (target) { - // We need delay to check position here - // since UI may change after click - const id = raf(() => { - syncPos(); - - setEnabled(true); - }); - - // Add resize observer to follow size - let resizeObserver: ResizeObserver; - if (typeof ResizeObserver !== "undefined") { - resizeObserver = new ResizeObserver(syncPos); - - resizeObserver.observe(target); - } - - return () => { - raf.cancel(id); - resizeObserver?.disconnect(); - }; - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - if (!enabled) { - return null; - } - - const isSmallComponent = component === "Checkbox" || component === "Radio"; - - // styles - // https://github.com/ant-design/ant-design/blob/2c029eacada8446da060ca3953400a2f2c02ab07/components/_util/wave/style.ts#L22 - // https://github.com/ant-design/ant-design/blob/2c029eacada8446da060ca3953400a2f2c02ab07/components/theme/themes/seed.ts#L48C23-L48C56 - const easeOutCirc = cubicBezier(0.08, 0.82, 0.17, 1); - const easeInOut = cubicBezier(0.645, 0.045, 0.355, 1); - - const inititalOpacity = isNotGrey(color ?? "") ? 0.2 : 0.6; - - return ( - { - const holder = divRef.current?.parentElement; - void unmount(holder!).then(() => { - holder?.remove(); - }); - }} - /> - ); -}; - -const showWaveEffect: ShowWaveEffect = (target, info) => { - const { component } = info; - - // Skip for unchecked checkbox - if (component === "Checkbox" && !target.querySelector("input")?.checked) { - return; - } - - // Create holder - const holder = document.createElement("div"); - holder.style.position = "absolute"; - holder.style.left = "0px"; - holder.style.top = "0px"; - target?.insertBefore(holder, target?.firstChild); - - render(, holder); -}; - -export default showWaveEffect; diff --git a/@acme/ui/src/_util/wave/index.ts b/@acme/ui/src/_util/wave/index.ts deleted file mode 100644 index 5c4bdcdf..00000000 --- a/@acme/ui/src/_util/wave/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-condition */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ - -import React, { cloneElement, useRef } from "react"; -import isVisible from "rc-util/lib/Dom/isVisible"; -import { composeRef, supportRef } from "rc-util/lib/ref"; - -import type { WaveAllowedComponent } from "./interface"; -import useWave from "./useWave"; - -export interface WaveProps { - disabled?: boolean; - children?: React.ReactNode; - component?: WaveAllowedComponent; -} - -const Wave: React.FC = (props) => { - const { children, disabled, component } = props; - const containerRef = useRef(null); - - // =============================== Wave =============================== - const showWave = useWave(containerRef, "", component); - - // ============================== Effect ============================== - React.useEffect(() => { - const node = containerRef.current; - if (!node || node.nodeType !== 1 || disabled) { - return; - } - - // Click handler - const onClick = (e: MouseEvent) => { - // Fix radio button click twice - if ( - !isVisible(e.target as HTMLElement) || - // No need wave - !node.getAttribute || - node.getAttribute("disabled") || - (node as HTMLInputElement).disabled || - // node.className.includes("disabled") || - node.className.includes("-leave") - ) { - return; - } - - showWave(e); - }; - - // Bind events - node.addEventListener("click", onClick, true); - return () => { - node.removeEventListener("click", onClick, true); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disabled]); - - // ============================== Render ============================== - if (!React.isValidElement(children)) { - return children ?? null; - } - - const ref = supportRef(children) - ? composeRef((children as any).ref, containerRef) - : containerRef; - - return cloneElement(children, { ref }); -}; - -if (process.env.NODE_ENV !== "production") { - Wave.displayName = "Wave"; -} - -export default Wave; diff --git a/@acme/ui/src/_util/wave/interface.ts b/@acme/ui/src/_util/wave/interface.ts deleted file mode 100644 index 9153053b..00000000 --- a/@acme/ui/src/_util/wave/interface.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type WaveAllowedComponent = "Button" | "Checkbox" | "Radio"; -export type ShowWaveEffect = ( - element: HTMLElement, - info: { - className: string; - component?: WaveAllowedComponent; - event: MouseEvent; - }, -) => void; - -export type ShowWave = (event: MouseEvent) => void; diff --git a/@acme/ui/src/_util/wave/useWave.ts b/@acme/ui/src/_util/wave/useWave.ts deleted file mode 100644 index b01febaf..00000000 --- a/@acme/ui/src/_util/wave/useWave.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-condition */ -import * as React from "react"; -import { useEvent } from "rc-util"; -import raf from "rc-util/lib/raf"; - -import type { ShowWave, WaveAllowedComponent } from "./interface"; -import showWaveEffect from "./WaveEffect"; - -export default function useWave( - nodeRef: React.RefObject, - className: string, - component?: WaveAllowedComponent, -) { - const showWave = useEvent((event) => { - const node = nodeRef.current!; - - if (!node) { - return; - } - - const targetNode = node; - - // Customize wave effect - showWaveEffect(targetNode, { - className, - component, - event, - }); - }); - - const rafId = React.useRef(); - - // Merge trigger event into one for each frame - const showDebounceWave: ShowWave = (event) => { - raf.cancel(rafId.current!); - - rafId.current = raf(() => { - showWave(event); - }); - }; - - return showDebounceWave; -} diff --git a/@acme/ui/src/_util/wave/util.ts b/@acme/ui/src/_util/wave/util.ts deleted file mode 100644 index 34de2801..00000000 --- a/@acme/ui/src/_util/wave/util.ts +++ /dev/null @@ -1,35 +0,0 @@ -export function isNotGrey(color: string) { - const match = /rgba?\((\d*), (\d*), (\d*)(, [\d.]*)?\)/.exec(color || ""); - if (match?.[1] && match[2] && match[3]) { - return !(match[1] === match[2] && match[2] === match[3]); - } - return true; -} - -export function isValidWaveColor(color: string) { - return ( - color && - color !== "#fff" && - color !== "#ffffff" && - color !== "rgb(255, 255, 255)" && - color !== "rgba(255, 255, 255, 1)" && - // isNotGrey(color) && // disable this line to allow show wave on grey color - !/rgba\((?:\d*, ){3}0\)/.test(color) && // any transparent rgba color - color !== "transparent" - ); -} - -export function getTargetWaveColor(node: HTMLElement) { - const { borderTopColor, borderColor, backgroundColor } = - getComputedStyle(node); - if (isValidWaveColor(borderTopColor)) { - return borderTopColor; - } - if (isValidWaveColor(borderColor)) { - return borderColor; - } - if (isValidWaveColor(backgroundColor)) { - return backgroundColor; - } - return null; -} diff --git a/@acme/ui/src/alert-modal/AlertModal.tsx b/@acme/ui/src/alert-modal/AlertModal.tsx deleted file mode 100644 index 6deebf6f..00000000 --- a/@acme/ui/src/alert-modal/AlertModal.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type { ModalProps } from "../modal"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "./_components"; - -export type AlertModalProps = Omit & { - onConfirm?: () => void; -}; - -export const AlertModal = ({ - className, - description, - okText, - okLoading, - title, - trigger, - onConfirm, - onCancel, - onOpenChange, - ...rest -}: AlertModalProps) => { - return ( - { - onOpenChange?.(isOpen); - if (!isOpen) { - onCancel?.(); - } - }} - {...rest} - > - {trigger} - - - {title && {title}} - {description} - - - - Cancel - e.key === "Enter" && onConfirm?.()} - > - {okText ?? "Confirm"} - - - - - ); -}; diff --git a/@acme/ui/src/alert-modal/_components.tsx b/@acme/ui/src/alert-modal/_components.tsx deleted file mode 100644 index f83a8d85..00000000 --- a/@acme/ui/src/alert-modal/_components.tsx +++ /dev/null @@ -1,145 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; - -import type { ButtonProps } from "../button"; -import { clsm } from ".."; -import { Button, buttonVariants } from "../button"; - -const AlertDialog = AlertDialogPrimitive.Root; - -const AlertDialogTrigger = AlertDialogPrimitive.Trigger; - -const AlertDialogPortal = AlertDialogPrimitive.Portal; - -const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; - -const AlertDialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - -)); -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; - -const AlertDialogHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-); -AlertDialogHeader.displayName = "AlertDialogHeader"; - -const AlertDialogFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-); -AlertDialogFooter.displayName = "AlertDialogFooter"; - -const AlertDialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; - -const AlertDialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AlertDialogDescription.displayName = - AlertDialogPrimitive.Description.displayName; - -const AlertDialogAction = React.forwardRef< - React.ElementRef, - ButtonProps & { - asChild?: boolean; - isControlled?: boolean; - } ->(({ asChild, isControlled, ...props }, ref) => { - return !asChild || isControlled ? ( - - - ); -}; - -export const AutoComplete = React.forwardRef(AutoCompleteInner) as < - T extends ValueType, ->( - props: AutoCompleteProps & { - ref?: React.ForwardedRef; - }, -) => ReturnType; diff --git a/@acme/ui/src/autocomplete/index.ts b/@acme/ui/src/autocomplete/index.ts deleted file mode 100644 index 8b7b342b..00000000 --- a/@acme/ui/src/autocomplete/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./AutoComplete"; diff --git a/@acme/ui/src/avatar/Avatar.tsx b/@acme/ui/src/avatar/Avatar.tsx deleted file mode 100644 index dfe2b4c0..00000000 --- a/@acme/ui/src/avatar/Avatar.tsx +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; - -import type { ReactNode } from "react"; - -import type { AvatarImageProps, AvatarRootProps } from "./_components"; -import { AvatarFallback, AvatarImage, AvatarRoot } from "./_components"; - -export type AvatarProps = AvatarRootProps & - AvatarImageProps & { - fallback?: ReactNode; - className?: string; - }; -export const Avatar = ({ fallback, className, size, ...rest }: AvatarProps) => { - return ( - - - {fallback} - - ); -}; diff --git a/@acme/ui/src/avatar/_components.tsx b/@acme/ui/src/avatar/_components.tsx deleted file mode 100644 index d9bb3211..00000000 --- a/@acme/ui/src/avatar/_components.tsx +++ /dev/null @@ -1,61 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as AvatarPrimitive from "@radix-ui/react-avatar"; - -import { clsm } from ".."; - -export type AvatarRootProps = React.ComponentPropsWithoutRef< - typeof AvatarPrimitive.Root -> & { - size?: "default" | "lg" | "xl"; -}; -const AvatarRoot = React.forwardRef< - React.ElementRef, - AvatarRootProps ->(({ className, size, ...props }, ref) => ( - -)); -AvatarRoot.displayName = AvatarPrimitive.Root.displayName; - -export type AvatarImageProps = React.ComponentPropsWithoutRef< - typeof AvatarPrimitive.Image ->; -const AvatarImage = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AvatarImage.displayName = AvatarPrimitive.Image.displayName; - -const AvatarFallback = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; - -export { AvatarRoot, AvatarImage, AvatarFallback }; diff --git a/@acme/ui/src/avatar/index.ts b/@acme/ui/src/avatar/index.ts deleted file mode 100644 index b3b01c75..00000000 --- a/@acme/ui/src/avatar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Avatar, type AvatarProps } from "./Avatar"; diff --git a/@acme/ui/src/badge/Badge.tsx b/@acme/ui/src/badge/Badge.tsx deleted file mode 100644 index 9c64fec3..00000000 --- a/@acme/ui/src/badge/Badge.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export type BadgeProps = { - children: React.ReactNode; - count?: React.ReactNode; -}; -export const Badge = ({ count }: BadgeProps) => { - return
{count}
; -}; diff --git a/@acme/ui/src/badge/index.ts b/@acme/ui/src/badge/index.ts deleted file mode 100644 index ae21190b..00000000 --- a/@acme/ui/src/badge/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Badge"; diff --git a/@acme/ui/src/breadcrumbs/Breadcrumbs.tsx b/@acme/ui/src/breadcrumbs/Breadcrumbs.tsx deleted file mode 100644 index e7f27272..00000000 --- a/@acme/ui/src/breadcrumbs/Breadcrumbs.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { Key } from "react"; -import { Fragment } from "react"; - -import { clsm } from ".."; - -type BreadcrumbsItem = { key?: Key; label: React.ReactNode; active?: boolean }; -type BreadcrumbsProps = { - items: BreadcrumbsItem[]; -}; -const Breadcrumbs = ({ items }: BreadcrumbsProps) => { - return ( - - ); -}; - -export type { BreadcrumbsItem, BreadcrumbsProps }; -export { Breadcrumbs }; diff --git a/@acme/ui/src/breadcrumbs/index.ts b/@acme/ui/src/breadcrumbs/index.ts deleted file mode 100644 index c1874ece..00000000 --- a/@acme/ui/src/breadcrumbs/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Breadcrumbs"; diff --git a/@acme/ui/src/button/Button.tsx b/@acme/ui/src/button/Button.tsx deleted file mode 100644 index 18ddc581..00000000 --- a/@acme/ui/src/button/Button.tsx +++ /dev/null @@ -1,318 +0,0 @@ -"use client"; - -import type { VariantProps } from "class-variance-authority"; -import * as React from "react"; -import Link from "next/link"; -import { Slot } from "@radix-ui/react-slot"; -import { cva } from "class-variance-authority"; - -import type { IconProps } from "../icons"; -import { clsm } from ".."; -import Wave from "../_util/wave"; -import { GenericSlot } from "../slot"; -import { LoadingIcon } from "./loading-icon"; - -const buttonVariants = cva( - [ - "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium ring-offset-white transition-colors", - "border", - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-950 focus-visible:ring-offset-2", - "dark:ring-offset-gray-950 dark:focus-visible:ring-gray-300", - ], - { - variants: { - primary: { - true: ["border-primary", "hover:border-primary-hover "], - }, - action: { - true: [], - }, - color: { - default: [], - accent: [], - danger: [], - success: [], - }, - variant: { - default: [ - "bg-primary text-white", - "hover:bg-primary-hover hover:text-white", - "active:ring-primary", - ], - outline: [ - "border-border", - "hover:border-border-hover hover:bg-background-hover", - ], - dashed: [ - "border border-dashed border-border", - "hover:border-primary-hover hover:text-primary-hover", - ], - ghost: [ - "border-transparent", - "hover:bg-background-hover", - "data-[state=open]:bg-background-hover", - ], - light: ["border-transparent", "hover:bg-background-hover"], - link: "underline-offset-4 hover:underline", - }, - size: { - sm: "h-6 rounded-sm px-2 py-0 font-normal", - default: "h-8 rounded-md px-3 py-1", - lg: "h-10 rounded-lg px-4 py-2", - }, - shape: { - default: "", - icon: [ - // "p-0 ", - // "sm:[&:has(span.sm:not-sr-only)]:w-auto [&:not(:has(span.sm\\:not-sr-only))]:p-0", - // '[&:has(span[class*="sm:not-sr-only"])]:w-auto [&:not(:has(span.sm\\:not-sr-only))]:p-0', - // "sm:[&:has(span.size-4)]:w-auto [&:not(:has(span.sm\\:not-sr-only))]:p-0", - // "sm:has-[span.size-4]:w-auto [&:not(:has(span.sm\\:not-sr-only))]:p-0", - // "has-[span.sm\\:not-sr-only]:w-auto [&:not(:has(span.sm\\:not-sr-only))]:p-0", - // "has-[span.sm\\:not-sr-only]:w-auto", - // 'has-[span[class*="sm:not-sr-only"]]:sm:w-auto [&:not(:has(span.sm\\:not-sr-only))]:p-0', - // '[&:has(span[class*="sm:not-sr-only"])]:sm:w-auto [&:not(:has(span.sm\\:not-sr-only))]:p-0', - // "!p-[unset]", - // 'has-[span[class*="sm:not-sr-only"]]:sm:w-auto has-[span[class*="sm:not-sr-only"]]:p-0 has-[span[class*="sm:not-sr-only"]]:sm:p-[auto]', - 'has-[span[class*="sm:not-sr-only"]]:sm:w-auto', - 'has-[span[class*="sm:not-sr-only"]]:max-sm:p-0', - '[&:not(:has(span[class*="sm:not-sr-only"]))]:p-0', - ], - circle: "rounded-full", - }, - /* Whenever to hidden text at mobile view */ - srOnly: { - true: "", - false: "", - }, - disabled: { - true: "pointer-events-none opacity-50", - false: "", - }, - }, - compoundVariants: [ - // primary - { - primary: true, - // danger: true, - color: "danger", - className: [ - "border-error bg-error", - "hover:border-error-hover hover:bg-error-hover", - ], - }, - - // light - { - variant: "light", - className: [ - "bg-gray-200 text-gray-950", - "hover:bg-gray-700 hover:text-white", - ], - }, - { - variant: "light", - primary: true, - className: [ - "bg-primary-300 text-primary-600", - "hover:bg-primary-700 hover:text-white", - ], - }, - { - variant: "light", - color: "accent", - className: [ - "bg-accent-muted text-accent", - // "hover:text-white hover:bg-info", - "hover:bg-accent-hover hover:text-white", - ], - }, - { - variant: "light", - color: "danger", - className: [ - "bg-error-muted text-error", - "hover:bg-error-hover hover:text-white", - ], - }, - { - variant: "light", - color: "success", - className: [ - "bg-success/10 text-success", - "hover:bg-success hover:text-white", - ], - }, - { - primary: true, - variant: "outline", - className: [ - "border-primary bg-transparent text-primary", - "hover:bg-primary-active hover:border-primary-hover hover:text-primary-hover", - "hover:bg-blue-100 dark:hover:bg-blue-300", - ], - }, - // outline - { - variant: "outline", - color: "danger", - className: [ - "border-error", - "hover:border-error-hover hover:text-error-hover", - "hover:bg-red-100 dark:hover:bg-red-300", - ], - }, - // Ghost - { - variant: "ghost", - color: "danger", - className: [ - "text-error", - "hover:text-error-hover", - "hover:bg-red-100 dark:hover:bg-red-300", - ], - }, - { - variant: "ghost", - color: "success", - className: [ - "text-success", - "hover:text-success-hover", - "hover:bg-green-100", - ], - }, - // Size - { - size: "sm", - shape: ["icon", "circle"], - className: "w-6", - }, - { - size: "default", - shape: ["icon", "circle"], - className: "w-8", - }, - { - size: "lg", - shape: ["icon", "circle"], - className: "w-10", - }, - { - size: "lg", - shape: ["icon", "circle"], - className: "w-12", - }, - ], - defaultVariants: { - variant: "default", - color: "default", - size: "default", - shape: "default", - }, - }, -); - -type ButtonVariants = VariantProps; - -export interface ButtonProps - extends Omit, "color">, - Omit { - asChild?: boolean; - href?: string; - loading?: boolean; - icon?: React.ReactNode; - // icon?: React.ReactElement; - color?: NonNullable; - variant?: Exclude; -} - -const Button = React.forwardRef( - ( - { - asChild = false, - href, - children, - className, - color, - disabled, - loading, - primary, - size, - shape, - srOnly, - variant, - icon, - ...props - }, - ref, - ) => { - const Comp = asChild || href ? Slot : "button"; - - const ChildrenToRender = ( - <> - {(!!loading || icon) && ( - > - className={clsm( - size === "sm" ? "" : "size-4", - // children ? "mr-2" : "", - )} - // srOnly={ - // srOnly && typeof children === "string" - // ? children - // : undefined - // } - > - {loading ? : icon} - - )} - {srOnly && typeof children === "string" ? ( - - {children} - - ) : ( - children - )} - - ); - - return ( - - - {asChild ? ( - children - ) : href ? ( - {ChildrenToRender} - ) : ( - ChildrenToRender - )} - - - ); - }, -); -Button.displayName = "Button"; - -export { Button, buttonVariants }; - -export { LoadingIcon } from "./loading-icon"; diff --git a/@acme/ui/src/button/index.ts b/@acme/ui/src/button/index.ts deleted file mode 100644 index 43c9a58f..00000000 --- a/@acme/ui/src/button/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type { ButtonProps } from "./button"; - -export { Button, buttonVariants } from "./button"; -export { LoadingIcon } from "./loading-icon"; diff --git a/@acme/ui/src/button/loading-icon.tsx b/@acme/ui/src/button/loading-icon.tsx deleted file mode 100644 index d89f24d0..00000000 --- a/@acme/ui/src/button/loading-icon.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { clsm } from ".."; - -export const LoadingIcon = ({ - className, - ...props -}: React.SVGProps) => { - return ( - - - - - - - - - - - - - - - - - - ); -}; diff --git a/@acme/ui/src/calendar/Calendar.tsx b/@acme/ui/src/calendar/Calendar.tsx deleted file mode 100644 index 6d806a08..00000000 --- a/@acme/ui/src/calendar/Calendar.tsx +++ /dev/null @@ -1,114 +0,0 @@ -"use client"; - -import type { - PropsBase, - PropsMulti, - PropsMultiRequired, - PropsRange, - PropsRangeRequired, - PropsSingle, - PropsSingleRequired, -} from "react-day-picker"; -import * as React from "react"; -import { DayPicker } from "react-day-picker"; - -import { clsm } from ".."; -import { buttonVariants } from "../button"; -import { Icon } from "../icons"; - -export type CalendarProps = Omit & - ( - | PropsSingle - | PropsSingleRequired - | PropsMulti - | PropsMultiRequired - | PropsRange - | PropsRangeRequired - | { - mode?: undefined; - required?: undefined; - } - ) & { - disabled?: boolean; - disabledDays?: PropsBase["disabled"]; - }; - -function Calendar({ - className, - classNames, - disabled: _, - showOutsideDays = true, - disabledDays, - ...props -}: CalendarProps) { - return ( - { - if (props.orientation === "left") { - return ( - - ); - } - return ( - - ); - }, - }} - {...props} - /> - ); -} -Calendar.displayName = "Calendar"; - -export { Calendar }; diff --git a/@acme/ui/src/calendar/index.ts b/@acme/ui/src/calendar/index.ts deleted file mode 100644 index 0acdf2fe..00000000 --- a/@acme/ui/src/calendar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Calendar"; diff --git a/@acme/ui/src/card/Card.tsx b/@acme/ui/src/card/Card.tsx deleted file mode 100644 index e197bfea..00000000 --- a/@acme/ui/src/card/Card.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type { ReactNode } from "react"; - -import type { CardRootProps } from "./_components"; -import { clsm } from ".."; -import { - CardContent, - CardDescription, - CardHeader, - CardRoot, - CardTitle, -} from "./_components"; - -type CardProps = CardRootProps & { - classNames?: { - title?: string; - description?: string; - content?: string; - }; - bordered?: boolean; - title?: ReactNode; - description?: ReactNode; - children: ReactNode; - extra?: ReactNode; -}; -const Card = ({ - classNames, - bordered = true, - className, - title, - description, - children, - extra, - ...props -}: CardProps) => { - return ( - - {(!!title || !!description) && ( - -
- {title} - {extra &&
{extra}
} -
- - {description} - -
- )} - {children} -
- ); -}; - -export { Card }; diff --git a/@acme/ui/src/card/_components.tsx b/@acme/ui/src/card/_components.tsx deleted file mode 100644 index 6ccf054a..00000000 --- a/@acme/ui/src/card/_components.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import * as React from "react"; - -import { clsm } from ".."; - -type CardRootProps = React.HTMLAttributes; -const CardRoot = React.forwardRef( - ({ className, ...props }, ref) => ( -
- ), -); -CardRoot.displayName = "CardRoot"; - -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardHeader.displayName = "CardHeader"; - -const CardTitle = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -

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

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

-)); -CardContent.displayName = "CardContent"; - -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardFooter.displayName = "CardFooter"; - -export type { CardRootProps }; - -export { - CardRoot, - CardHeader, - CardFooter, - CardTitle, - CardDescription, - CardContent, -}; diff --git a/@acme/ui/src/card/index.ts b/@acme/ui/src/card/index.ts deleted file mode 100644 index e6d5c0f5..00000000 --- a/@acme/ui/src/card/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./Card"; -export * from "./_components"; diff --git a/@acme/ui/src/carousel/_components.tsx b/@acme/ui/src/carousel/_components.tsx deleted file mode 100644 index 5cc67fd4..00000000 --- a/@acme/ui/src/carousel/_components.tsx +++ /dev/null @@ -1,260 +0,0 @@ -"use client"; - -import type { UseEmblaCarouselType } from "embla-carousel-react"; -import * as React from "react"; -import useEmblaCarousel from "embla-carousel-react"; - -import { clsm } from ".."; -import { Button } from "../button"; -import { Icon } from "../icons"; - -type CarouselApi = UseEmblaCarouselType[1]; -type UseCarouselParameters = Parameters; -type CarouselOptions = UseCarouselParameters[0]; -type CarouselPlugin = UseCarouselParameters[1]; - -type CarouselProps = { - opts?: CarouselOptions; - plugins?: CarouselPlugin; - orientation?: "horizontal" | "vertical"; - setApi?: (api: CarouselApi) => void; -}; - -type CarouselContextProps = { - carouselRef: ReturnType[0]; - api: ReturnType[1]; - scrollPrev: () => void; - scrollNext: () => void; - canScrollPrev: boolean; - canScrollNext: boolean; -} & CarouselProps; - -const CarouselContext = React.createContext(null); - -function useCarousel() { - const context = React.useContext(CarouselContext); - - if (!context) { - throw new Error("useCarousel must be used within a "); - } - - return context; -} - -const Carousel = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & CarouselProps ->( - ( - { - orientation = "horizontal", - opts, - setApi, - plugins, - className, - children, - ...props - }, - ref, - ) => { - const [carouselRef, api] = useEmblaCarousel( - { - ...opts, - axis: orientation === "horizontal" ? "x" : "y", - }, - plugins, - ); - const [canScrollPrev, setCanScrollPrev] = React.useState(false); - const [canScrollNext, setCanScrollNext] = React.useState(false); - - const onSelect = React.useCallback((api: CarouselApi) => { - if (!api) { - return; - } - - setCanScrollPrev(api.canScrollPrev()); - setCanScrollNext(api.canScrollNext()); - }, []); - - const scrollPrev = React.useCallback(() => { - api?.scrollPrev(); - }, [api]); - - const scrollNext = React.useCallback(() => { - api?.scrollNext(); - }, [api]); - - const handleKeyDown = React.useCallback( - (event: React.KeyboardEvent) => { - if (event.key === "ArrowLeft") { - event.preventDefault(); - scrollPrev(); - } else if (event.key === "ArrowRight") { - event.preventDefault(); - scrollNext(); - } - }, - [scrollPrev, scrollNext], - ); - - React.useEffect(() => { - if (!api || !setApi) { - return; - } - - setApi(api); - }, [api, setApi]); - - React.useEffect(() => { - if (!api) { - return; - } - - onSelect(api); - api.on("reInit", onSelect); - api.on("select", onSelect); - - return () => { - api.off("select", onSelect); - }; - }, [api, onSelect]); - - return ( - -
- {children} -
-
- ); - }, -); -Carousel.displayName = "Carousel"; - -const CarouselContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => { - const { carouselRef, orientation } = useCarousel(); - - return ( -
-
-
- ); -}); -CarouselContent.displayName = "CarouselContent"; - -const CarouselItem = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => { - const { orientation } = useCarousel(); - - return ( -
- ); -}); -CarouselItem.displayName = "CarouselItem"; - -const CarouselPrevious = React.forwardRef< - HTMLButtonElement, - React.ComponentProps ->(({ className, variant = "outline", shape = "icon", ...props }, ref) => { - const { orientation, scrollPrev, canScrollPrev } = useCarousel(); - - return ( - - ); -}); -CarouselPrevious.displayName = "CarouselPrevious"; - -const CarouselNext = React.forwardRef< - HTMLButtonElement, - React.ComponentProps ->(({ className, variant = "outline", shape = "icon", ...props }, ref) => { - const { orientation, scrollNext, canScrollNext } = useCarousel(); - - return ( - - ); -}); -CarouselNext.displayName = "CarouselNext"; - -export { - type CarouselApi, - Carousel, - CarouselContent, - CarouselItem, - CarouselPrevious, - CarouselNext, -}; diff --git a/@acme/ui/src/carousel/index.ts b/@acme/ui/src/carousel/index.ts deleted file mode 100644 index dcaafd49..00000000 --- a/@acme/ui/src/carousel/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./_components"; diff --git a/@acme/ui/src/checkbox/Checkbox.tsx b/@acme/ui/src/checkbox/Checkbox.tsx deleted file mode 100644 index a65f349a..00000000 --- a/@acme/ui/src/checkbox/Checkbox.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import * as React from "react"; -import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; - -import { clsm } from ".."; -import { Icon } from "../icons"; - -type CheckboxProps = Omit< - React.ComponentPropsWithoutRef, - "checked" | "defaultChecked" | "onChange" | "onCheckedChange" -> & { - checked?: boolean; - defaultChecked?: boolean; - indeterminate?: boolean; - onChange?: (checked: boolean) => void; -}; -const Checkbox = React.forwardRef< - React.ElementRef, - CheckboxProps ->( - ( - { - id, - "aria-describedby": ariaDescribedBy, - "aria-invalid": ariaInvalid, - - children, - checked, - defaultChecked, - className, - indeterminate, - onChange, - ...props - }, - ref, - ) => { - return ( - - ); - }, -); -Checkbox.displayName = CheckboxPrimitive.Root.displayName; - -export { Checkbox }; diff --git a/@acme/ui/src/checkbox/CheckboxGroup.tsx b/@acme/ui/src/checkbox/CheckboxGroup.tsx deleted file mode 100644 index 282f7821..00000000 --- a/@acme/ui/src/checkbox/CheckboxGroup.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { forwardRef } from "react"; - -import type { ValueType } from "../form"; -import { Checkbox } from "./Checkbox"; - -type CheckboxGroupProps = { - name?: string; - value?: T[]; - options?: { label: string; value: T }[]; - onChange?: (checkedValue: T[]) => void; -}; -const CheckboxGroupInner = ( - { name, value, options = [], onChange }: CheckboxGroupProps, - _: React.ForwardedRef, -) => { - return ( -
- {options.map((o) => { - return ( - { - onChange?.( - checked - ? [...(value ?? []), o.value] - : (value ?? []).filter((x) => x !== o.value), - ); - }} - > - {o.label} - - ); - })} -
- ); -}; - -export const CheckboxGroup = forwardRef(CheckboxGroupInner) as < - T extends ValueType, ->( - props: CheckboxGroupProps & { - ref?: React.ForwardedRef; - }, -) => ReturnType; diff --git a/@acme/ui/src/checkbox/index.ts b/@acme/ui/src/checkbox/index.ts deleted file mode 100644 index 0e2a8462..00000000 --- a/@acme/ui/src/checkbox/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./Checkbox"; -export * from "./CheckboxGroup"; diff --git a/@acme/ui/src/code-block/CodeBlock.tsx b/@acme/ui/src/code-block/CodeBlock.tsx deleted file mode 100644 index 53aa35d9..00000000 --- a/@acme/ui/src/code-block/CodeBlock.tsx +++ /dev/null @@ -1,96 +0,0 @@ -"use client"; - -import type { ComponentProps } from "react"; -import { memo } from "react"; -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism"; -import { useCopyToClipboard } from "usehooks-ts"; - -import { Button } from "../button"; -import { Icon } from "../icons"; - -export const programmingLanguages = { - javascript: ".js", - python: ".py", - java: ".java", - c: ".c", - cpp: ".cpp", - "c++": ".cpp", - "c#": ".cs", - ruby: ".rb", - php: ".php", - swift: ".swift", - "objective-c": ".m", - kotlin: ".kt", - typescript: ".ts", - go: ".go", - perl: ".pl", - rust: ".rs", - scala: ".scala", - haskell: ".hs", - lua: ".lua", - shell: ".sh", - sql: ".sql", - html: ".html", - css: ".css", - // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component -}; - -export type CodeBlockProps = Omit< - ComponentProps, - "language" | "children" -> & { - language?: keyof typeof programmingLanguages; - children: string; -}; -export const CodeBlock = memo( - ({ language, children, ...props }: CodeBlockProps) => { - const [isCopied, copyToClipboard] = useCopyToClipboard(); - const onCopy = async () => { - if (isCopied) return; - await copyToClipboard(children); - }; - return ( -
-
- {language} -
- -
-
- - {children} - -
- ); - }, -); -CodeBlock.displayName = "CodeBlock"; diff --git a/@acme/ui/src/code-block/index.ts b/@acme/ui/src/code-block/index.ts deleted file mode 100644 index ca111193..00000000 --- a/@acme/ui/src/code-block/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./CodeBlock"; diff --git a/@acme/ui/src/collapse/Collapse.tsx b/@acme/ui/src/collapse/Collapse.tsx deleted file mode 100644 index 9e274279..00000000 --- a/@acme/ui/src/collapse/Collapse.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import type { - AccordionMultipleProps, - AccordionSingleProps, -} from "@radix-ui/react-accordion"; - -import type { - AccordionContentProps, - AccordionRootProps, - AccordionTriggerProps, -} from "./_components"; -import { - AccordionContent, - AccordionItem, - AccordionRoot, - AccordionTrigger, -} from "./_components"; - -type CollapseItemDef = { - key?: string; - label: React.ReactNode; - children: React.ReactNode; -}; - -type CollapseProps = Omit< - AccordionRootProps, - | "type" - | "collapsible" - | "value" - | "defaultValue" - | "onChange" - | "onValueChange" -> & { - activeKey?: string[]; - defaultActiveKey?: string[]; - items: CollapseItemDef[]; - type?: "single" | "multiple"; - triggerProps?: AccordionTriggerProps; - contentProps?: AccordionContentProps; - onChange?: (activeKeys: string[]) => void; -}; -export const Collapse = ({ - activeKey, - defaultActiveKey, - items, - type = "multiple", - triggerProps, - contentProps, - onChange, - ...props -}: CollapseProps) => { - const singleOrMultipleProps = - type === "single" - ? ({ - type: "single", - collapsible: true, - value: activeKey?.[0], - defaultValue: defaultActiveKey?.[0], - onValueChange: (string) => { - onChange?.([string]); - }, - } satisfies AccordionSingleProps) - : ({ - type: "multiple", - onValueChange: (string) => { - onChange?.(string); - }, - } satisfies AccordionMultipleProps); - return ( - - {items.map((item, index) => { - const key = item.key ?? index; - return ( - - {item.label} - - {item.children} - - - ); - })} - - ); -}; diff --git a/@acme/ui/src/collapse/_components.tsx b/@acme/ui/src/collapse/_components.tsx deleted file mode 100644 index 99723a25..00000000 --- a/@acme/ui/src/collapse/_components.tsx +++ /dev/null @@ -1,77 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as AccordionPrimitive from "@radix-ui/react-accordion"; - -import { clsm } from ".."; -import { Icon } from "../icons"; - -type AccordionRootProps = React.ComponentPropsWithoutRef< - typeof AccordionPrimitive.Root ->; -const AccordionRoot = AccordionPrimitive.Root; - -const AccordionItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ ...props }, ref) => ); -AccordionItem.displayName = "AccordionItem"; - -type AccordionTriggerProps = React.ComponentPropsWithoutRef< - typeof AccordionPrimitive.Trigger ->; -const AccordionTrigger = React.forwardRef< - React.ElementRef, - AccordionTriggerProps ->(({ className, children, ...props }, ref) => ( - - svg]:rotate-180", - className, - )} - {...props} - > - {children} - - - -)); -AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; - -type AccordionContentProps = Omit< - React.ComponentPropsWithoutRef, - "asChild" -> & { - as?: React.ElementType; -}; -const AccordionContent = React.forwardRef< - React.ElementRef, - AccordionContentProps ->(({ className, children, as, ...props }, ref) => { - const Comp = as ?? "div"; - return ( - - {children} - - ); -}); - -AccordionContent.displayName = AccordionPrimitive.Content.displayName; - -export type { - AccordionRootProps, - AccordionContentProps, - AccordionTriggerProps, -}; -export { AccordionRoot, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/@acme/ui/src/collapse/index.ts b/@acme/ui/src/collapse/index.ts deleted file mode 100644 index 9aad3595..00000000 --- a/@acme/ui/src/collapse/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Collapse"; diff --git a/@acme/ui/src/command/Command.tsx b/@acme/ui/src/command/Command.tsx deleted file mode 100644 index d267107e..00000000 --- a/@acme/ui/src/command/Command.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { clsm } from "@acme/ui"; - -import type { ValueType } from "../form/types"; -import type { Option } from "../select/types"; -import type { CommandRootProps } from "./_components"; -import { Icon } from "../icons"; -import { - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandRoot, -} from "./_components"; -import { defaultEmpty, defaultPlaceholder } from "./config"; - -export type CommandProps = CommandRootProps & { - value?: T; - options: (Option & { - onSelect: (currentValue: string) => void; - })[]; - - empty?: React.ReactNode; - placeholder?: string; - - onSearchChange?: (search: string) => void; - - groupClassName?: string; - optionRender?: { - checked?: boolean; - icon?: (option: Option) => React.ReactNode; - label?: (option: Option) => React.ReactNode; - }; - optionsRender?: (options: Option[]) => React.ReactNode; -}; - -export const Command = ({ - options, - value, - empty, - placeholder, - onSearchChange, - - groupClassName, - optionRender, - optionsRender, -}: CommandProps) => { - return ( - - - {empty ?? defaultEmpty} - {options.length > 0 && ( - - {optionsRender - ? optionsRender(options) - : options.map((item) => ( - - {!optionRender || - (optionRender.checked && ( - - ))} - {optionRender?.icon ? ( - {optionRender.icon(item)} - ) : ( - item.icon && - )} - {optionRender?.label ? optionRender.label(item) : item.label} - - ))} - - )} - - ); -}; diff --git a/@acme/ui/src/command/_components.tsx b/@acme/ui/src/command/_components.tsx deleted file mode 100644 index dcb15347..00000000 --- a/@acme/ui/src/command/_components.tsx +++ /dev/null @@ -1,179 +0,0 @@ -"use client"; - -import type { DialogProps } from "@radix-ui/react-dialog"; -import * as React from "react"; -import { Command as CommandPrimitive, useCommandState } from "cmdk"; - -import { clsm } from "@acme/ui"; - -import { Icon } from "../icons"; -import { Dialog, DialogContent } from "../modal/_components"; - -type CommandRootProps = React.ComponentPropsWithoutRef; -const CommandRoot = React.forwardRef< - React.ElementRef, - CommandRootProps ->(({ className, ...props }, ref) => ( - -)); -CommandRoot.displayName = CommandPrimitive.displayName; - -type CommandDialogProps = DialogProps; -const CommandDialog = ({ children, ...props }: CommandDialogProps) => { - return ( - - - - {children} - - - - ); -}; - -const CommandInput = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( -
- - -
-)); - -CommandInput.displayName = CommandPrimitive.Input.displayName; - -const CommandList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); - -CommandList.displayName = CommandPrimitive.List.displayName; - -// fix: https://github.com/pacocoursey/cmdk/issues/149#issuecomment-1606206982 -// Command.tsx: add line {options.length > 0 && ( -const CommandEmpty = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->((props, ref) => { - const render = useCommandState((state) => state.filtered.count === 0); - if (!render) return null; - - return ( -
- ); -}); -// const CommandEmpty = React.forwardRef< -// React.ElementRef, -// React.ComponentPropsWithoutRef -// >((props, ref) => ( -// -// )); - -CommandEmpty.displayName = CommandPrimitive.Empty.displayName; - -const CommandGroup = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); - -CommandGroup.displayName = CommandPrimitive.Group.displayName; - -const CommandSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -CommandSeparator.displayName = CommandPrimitive.Separator.displayName; - -const CommandItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); - -CommandItem.displayName = CommandPrimitive.Item.displayName; - -const CommandShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ); -}; -CommandShortcut.displayName = "CommandShortcut"; - -export type { CommandRootProps }; -export { - CommandRoot, - CommandDialog, - CommandInput, - CommandList, - CommandEmpty, - CommandGroup, - CommandItem, - CommandShortcut, - CommandSeparator, -}; diff --git a/@acme/ui/src/command/config.ts b/@acme/ui/src/command/config.ts deleted file mode 100644 index a761e950..00000000 --- a/@acme/ui/src/command/config.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const defaultPlaceholder = "Search..."; -export const defaultEmpty = "No data"; diff --git a/@acme/ui/src/command/index.ts b/@acme/ui/src/command/index.ts deleted file mode 100644 index 4de6159a..00000000 --- a/@acme/ui/src/command/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Command"; diff --git a/@acme/ui/src/date-picker/DatePicker.tsx b/@acme/ui/src/date-picker/DatePicker.tsx deleted file mode 100644 index b6c90a90..00000000 --- a/@acme/ui/src/date-picker/DatePicker.tsx +++ /dev/null @@ -1,179 +0,0 @@ -"use client"; - -import type { VariantProps } from "class-variance-authority"; -import * as React from "react"; -import { useClickAway, useFocusWithin } from "ahooks"; -import { format as formatDate, isValid, parse } from "date-fns"; -import { useMergedState } from "rc-util"; - -import type { inputSizeVariants, InputVariants } from "../input"; -import { clsm } from ".."; -import { Calendar } from "../calendar"; -import { Icon } from "../icons"; -import { Input } from "../input"; -import { Popover } from "../popover"; - -export type DatePickerProps = InputVariants & - VariantProps & { - format?: string; - defaultValue?: Date; - value?: Date; - placeholder?: string; - /** Callback function, can be executed when the selected time is changing */ - onChange?: (date: Date | undefined, dateString: string) => void; - }; -const DatePickerInternal = ( - { - borderless, - format = "dd/MM/yyyy", - size, - status, - defaultValue, - value, - placeholder, - ...props - }: DatePickerProps, - ref: React.Ref, -) => { - const [open, setOpen] = React.useState(false); - const [month, setMonth] = React.useState( - defaultValue === undefined - ? (isValid(value) - ? value - : new Date()) - : (isValid(defaultValue) - ? defaultValue - : new Date()), - ); - - const inputId = React.useId(); - - // ====================== Value ======================= - const preValue = - defaultValue === undefined - ? (isValid(value) - ? formatDate(value!, format) - : "") - : (isValid(defaultValue) - ? formatDate(defaultValue, format) - : ""); - const [inputValue, setInputValue] = useMergedState(preValue); - - // set input value if date value change - React.useEffect(() => { - const x = isValid(value) ? formatDate(value!, format) : ""; - setInputValue(x); - setMonth(value ? value : new Date()); - }, [value, setInputValue, format]); - - const handleChange = (input: string | Date) => { - const inputDate = - typeof input === "string" ? parse(input, format, new Date()) : input; - if (isValid(inputDate)) { - props.onChange?.(inputDate, formatDate(inputDate, format)); - setInputValue(formatDate(inputDate, format)); - setMonth(inputDate); - } else { - setInputValue(preValue); - props.onChange?.(undefined, ""); - } - }; - - const picker = ( - { - handleChange(selectedDate); - setOpen(false); - }} - month={month} - onMonthChange={setMonth} - {...props} - /> - ); - - // handle click outside from input (is focus within) - const [isFocused, setIsFocused] = React.useState(false); - useFocusWithin(() => document.getElementById(inputId), { - onFocus: () => { - setIsFocused(true); - }, - }); - - useClickAway( - (e) => { - if (isFocused && // check if choose a day in panel or not - !(e.target && "name" in e.target && e.target.name === "day")) { - if (inputValue.length === 10) { - handleChange(inputValue); - } else { - setInputValue(preValue); - } - } - }, - () => document.getElementById(inputId), - ); - - return ( - <> - { - if (e.target && "id" in e.target && e.target.id !== inputId) { - setOpen(false); - } - }} - onOpenAutoFocus={(e) => { - e.preventDefault(); - }} - > - - } - value={inputValue} - onClick={() => { - if (!open) setOpen(true); - }} - onKeyUp={(e) => { - e.stopPropagation(); - if (e.key === "Enter" || e.key === "Escape") { - if (e.currentTarget.value.length === 10) { - handleChange(e.currentTarget.value); - } else { - setInputValue(preValue); - } - setOpen(false); - } - }} - onChange={(e) => { - setInputValue(e.currentTarget.value); - if (e.currentTarget.value === "") { - props.onChange?.(undefined, ""); - } else if (e.currentTarget.value.length === 10) { - handleChange(e.currentTarget.value); - } - }} - /> - - - ); -}; - -export const DatePicker = React.forwardRef(DatePickerInternal); diff --git a/@acme/ui/src/date-picker/DateRangePicker.tsx b/@acme/ui/src/date-picker/DateRangePicker.tsx deleted file mode 100644 index 2316fd1c..00000000 --- a/@acme/ui/src/date-picker/DateRangePicker.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Calendar } from "../calendar"; - -// TODO: https://react-day-picker.js.org/guides/input-fields?#example-range-selection -type DateRange = { - start: Date | undefined; - end?: Date | undefined; -}; -export type DateRangePickerProps = { - value?: DateRange; - onChange?: (range: DateRange | undefined) => void; -}; -export const DateRangePicker = (_props: DateRangePickerProps) => { - return ( -
- { - // onChange?.({ - // start: range?.from, - // end: range?.to, - // }); - // }} - numberOfMonths={2} - //{...rest} - /> -
- ); -}; diff --git a/@acme/ui/src/date-picker/index.ts b/@acme/ui/src/date-picker/index.ts deleted file mode 100644 index 6b644385..00000000 --- a/@acme/ui/src/date-picker/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./DatePicker"; -export * from "./DateRangePicker"; diff --git a/@acme/ui/src/descriptions/Descriptions.tsx b/@acme/ui/src/descriptions/Descriptions.tsx deleted file mode 100644 index bc6e8cd3..00000000 --- a/@acme/ui/src/descriptions/Descriptions.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import type { ReactNode } from "react"; -import { Fragment } from "react"; - -import { useResponsive } from "@acme/hooks/useResponsive"; - -import type { Screens } from "../theme"; -import { clsm } from ".."; - -export type DescriptionsItem = { - key: React.Key; - label?: React.ReactNode; - children?: React.ReactNode; -}; -type DescriptionProps = { - title?: React.ReactNode; - items: DescriptionsItem[]; - - bordered?: boolean; - column?: number | Partial>; - layout?: "horizontal" | "vertical"; - classNames?: { - label: string; - content: string; - }; - extra?: ReactNode; -}; -export const Descriptions = ({ - title, - items, - extra, - - classNames, - bordered, - column, - layout = "horizontal", - // ...props -}: DescriptionProps) => { - const x = useResponsive(); - let mergedColumn = 3; - if (typeof column === "number") { - mergedColumn = column; - } else if (typeof column === "object") { - const mergedColumnWithScreen: Partial> = - {}; - for (const [k] of Object.entries(x)) { - mergedColumnWithScreen[k as Screens] = column[k as Screens] ?? undefined; - } - const matched = Object.entries(mergedColumnWithScreen).findLast( - ([, v]) => v, - )![0] as Screens; - mergedColumn = column[matched]!; - } - - const rows = - layout === "horizontal" - ? chunkArray(items, mergedColumn) - : createRows(items, mergedColumn); - - const labelClassName = clsm("text-foreground-muted", classNames?.label); - const contentClassName = clsm(classNames?.content); - const thClassName = clsm( - "text-start text-sm font-normal", - labelClassName, - bordered && "border-e bg-surface-secondary", - layout === "horizontal" && "py-4", - layout === "vertical" && "pb-1", - layout === "vertical" && bordered && "px-6", - ); - const tdClassName = clsm( - "break-all", - bordered && "border-e", - layout === "horizontal" && "py-4", - layout === "vertical" && "pb-4", - layout === "vertical" && bordered && "px-6", - contentClassName, - ); - return ( -
- {(!!title || !!extra) && ( -
-
{title}
- {extra &&
{extra}
} -
- )} - -
- - - {rows.map((cols, rowIndex) => ( - - {cols.map((col, index) => - // horizontal - typeof col === "object" && "label" in col! ? ( - bordered ? ( - - - - - ) : ( - - ) //vertical - ) : (rowIndex % 2 === 0 ? ( - - ) : ( - - )), - )} - - ))} - -
- {col.label} - - {col.children ?? "-"} - - {col.label}: - {col.children} - - {col as React.ReactNode} - - {col as React.ReactNode} -
-
-
- ); -}; - -function chunkArray(array: T[], size: number): T[][] { - const chunkedArray: T[][] = []; - for (let index = 0; index < array.length; index += size) { - const chunk = array.slice(index, index + size); - chunkedArray.push(chunk); - } - return chunkedArray; -} -function createRows( - data: DescriptionsItem[], - columns: number, -): React.ReactNode[][] { - const rows: React.ReactNode[][] = []; - for (let index = 0; index < data.length; index += columns) { - const labels = data.slice(index, index + columns).map((item) => item.label); - const childrens = data - .slice(index, index + columns) - .map((item) => item.children); - rows.push(labels, childrens); - } - return rows; -} diff --git a/@acme/ui/src/descriptions/index.ts b/@acme/ui/src/descriptions/index.ts deleted file mode 100644 index 58d009a1..00000000 --- a/@acme/ui/src/descriptions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Descriptions"; diff --git a/@acme/ui/src/divider/Divider.tsx b/@acme/ui/src/divider/Divider.tsx deleted file mode 100644 index 878e1ae7..00000000 --- a/@acme/ui/src/divider/Divider.tsx +++ /dev/null @@ -1,61 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as SeparatorPrimitive from "@radix-ui/react-separator"; - -import { clsm } from ".."; - -type SeparatorProps = React.ComponentPropsWithoutRef< - typeof SeparatorPrimitive.Root -> & { - as?: "li"; -}; -const Separator = React.forwardRef< - React.ElementRef, - SeparatorProps ->( - ( - { - as, - className, - orientation = "horizontal", - decorative = true, - children, - ...props - }, - ref, - ) => { - const separator = ( - - {as === "li" ?
  • :
    } - - ); - const Comp = children ? "div" : React.Fragment; - return ( - - {separator} - {children &&
    {children}
    } - {children && separator} -
    - ); - }, -); -Separator.displayName = SeparatorPrimitive.Root.displayName; - -export { Separator as Divider }; diff --git a/@acme/ui/src/divider/index.ts b/@acme/ui/src/divider/index.ts deleted file mode 100644 index dde73a62..00000000 --- a/@acme/ui/src/divider/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Divider"; diff --git a/@acme/ui/src/drawer/Drawer.tsx_ b/@acme/ui/src/drawer/Drawer.tsx_ deleted file mode 100644 index 84752b6e..00000000 --- a/@acme/ui/src/drawer/Drawer.tsx_ +++ /dev/null @@ -1,105 +0,0 @@ -import { Fragment, ReactNode, useState } from "react" -import { Dialog, Transition } from "@headlessui/react" -import { Button } from "antd" - -import { useResponsive } from "@acme/hooks" -import { CloseOutlined } from @acme/ui/icons - -type DrawerProps = { - open: boolean - title?: ReactNode - children: ReactNode - onClose: () => void -} -const Drawer = ({ open, title, children, onClose }: DrawerProps) => { - return ( - - - -
    - - -
    -
    -
    - - - {/* */} - {/*
    */} - {/* */} - {/*
    */} - {/*
    */} -
    -
    -
    - - {title} - -
    - -
    -
    -
    -
    -
    {children}
    -
    - {/*
    */} - {/* */} - {/* Panel title */} - {/* */} - {/*
    */} - {/*
    */} - {/* Your content */} - {/*
    */} -
    -
    -
    -
    -
    -
    -
    -
    - ) -} - -export default Drawer diff --git a/@acme/ui/src/drawer/index.ts_ b/@acme/ui/src/drawer/index.ts_ deleted file mode 100644 index 35f9eabc..00000000 --- a/@acme/ui/src/drawer/index.ts_ +++ /dev/null @@ -1,4 +0,0 @@ -// https://tailwindui.com/components/application-ui/overlays/slide-overs -import Drawer from "./Drawer.tsx_" - -export default Drawer diff --git a/@acme/ui/src/dropdown/Dropdown.tsx b/@acme/ui/src/dropdown/Dropdown.tsx deleted file mode 100644 index 4711b9d4..00000000 --- a/@acme/ui/src/dropdown/Dropdown.tsx +++ /dev/null @@ -1,126 +0,0 @@ -"use client"; - -// import type { Placement } from "@popperjs/core"; -import type { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"; -import type { ReactElement, ReactNode } from "react"; -import { cloneElement, Fragment } from "react"; - -import type { Placement } from "../types"; -import { clsm } from ".."; -import { - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuRoot, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from "./_components"; - -type MenuItem = { - key?: string; - label?: ReactNode; - className?: string; - icon?: ReactElement; - shortcut?: ReactNode; - group?: MenuItem[]; - as?: "title" | "separator"; - href?: string; - onSelect?: (event: Event) => void; -}; -type Menu = { - className?: string; - items: MenuItem[]; - itemsClassName?: string; -}; -export type DropdownProps = DropdownMenuTriggerProps & { - className?: string; - children: ReactNode; - menu: Menu; - placement?: Placement; -}; - -export const Dropdown = ({ - className, - children, - menu, - placement, - ...rest -}: DropdownProps) => { - const side = placement?.includes("top") - ? "top" - : placement?.includes("right") - ? "right" - : !placement || placement.includes("bottom") - ? "bottom" - : "left"; - const align = - !placement || placement.includes("auto") - ? "center" - : (placement.includes("start") - ? "start" - : "end"); - const renderMenu = (items: MenuItem[]): ReactNode => { - return items.map( - ( - { as, group, href, key, label, className, icon, shortcut, onSelect }, - index, - ) => ( - - {as === "title" ? ( - {label} - ) : as === "separator" ? ( - - ) : group ? ( - renderMenu(group) - ) : ( - - {href ? ( - - {icon && - cloneElement(icon, { - className: "mr-2 h-4 w-4", - })} - {label} - {shortcut && ( - {shortcut} - )} - - ) : ( - <> - {icon && - cloneElement(icon, { - className: "mr-2 h-4 w-4", - })} - {label} - {shortcut && ( - {shortcut} - )} - - )} - - )} - - ), - ); - }; - return ( - - - {children} - - - {renderMenu(menu.items)} - - - ); -}; diff --git a/@acme/ui/src/dropdown/_components.tsx b/@acme/ui/src/dropdown/_components.tsx deleted file mode 100644 index 89d0dc62..00000000 --- a/@acme/ui/src/dropdown/_components.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import * as React from "react"; -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; - -import { clsm } from "@acme/ui"; - -import { CheckOutlined, ChevronRightOutlined, CircleOutlined } from "../icons"; - -const DropdownMenuRoot = DropdownMenuPrimitive.Root; - -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; - -const DropdownMenuGroup = DropdownMenuPrimitive.Group; - -const DropdownMenuPortal = DropdownMenuPrimitive.Portal; - -const DropdownMenuSub = DropdownMenuPrimitive.Sub; - -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; - -const DropdownMenuSubTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } ->(({ className, inset, children, ...props }, ref) => ( - - {children} - - -)); -DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName; - -const DropdownMenuSubContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName; - -const DropdownMenuContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - - -)); -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; - -const DropdownMenuItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } ->(({ className, inset, ...props }, ref) => ( - -)); -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; - -const DropdownMenuCheckboxItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, checked, ...props }, ref) => ( - - - - - - - {children} - -)); -DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName; - -const DropdownMenuRadioItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - - - - - {children} - -)); -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; - -const DropdownMenuLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & { - inset?: boolean; - } ->(({ className, inset, ...props }, ref) => ( - -)); -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; - -const DropdownMenuSeparator = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; - -const DropdownMenuShortcut = ({ - className, - ...props -}: React.HTMLAttributes) => { - return ( - - ); -}; -DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; - -export { - DropdownMenuRoot, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuGroup, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuRadioGroup, -}; diff --git a/@acme/ui/src/dropdown/index.ts b/@acme/ui/src/dropdown/index.ts deleted file mode 100644 index bf186c30..00000000 --- a/@acme/ui/src/dropdown/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Dropdown, type DropdownProps } from "./Dropdown"; diff --git a/@acme/ui/src/editor/Editor.tsx b/@acme/ui/src/editor/Editor.tsx deleted file mode 100644 index 3b98f940..00000000 --- a/@acme/ui/src/editor/Editor.tsx +++ /dev/null @@ -1,167 +0,0 @@ -"use client"; - -import type { InitialConfigType } from "@lexical/react/LexicalComposer"; -import type { EditorState, LexicalEditor } from "lexical"; -import type { Ref } from "react"; -import { forwardRef, useEffect, useMemo, useRef, useState } from "react"; -import { AutoLinkNode, LinkNode } from "@lexical/link"; -import { LexicalComposer } from "@lexical/react/LexicalComposer"; -import { ContentEditable } from "@lexical/react/LexicalContentEditable"; -import { EditorRefPlugin } from "@lexical/react/LexicalEditorRefPlugin"; -import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary"; -import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; -import { HeadingNode } from "@lexical/rich-text"; - -import { clsm } from "@acme/ui"; - -import { ImageNode } from "./nodes/ImageNode"; -import { AutoLinkPlugin } from "./plugins/AutoLinkPlugin"; -import ComponentPickerMenuPlugin from "./plugins/ComponentPickerPlugin/ComponentPicker"; -import { DraggableBlockPlugin } from "./plugins/DraggableBlockPlugin"; -import { HistoryPlugin } from "./plugins/history"; -import { ImagesPlugin } from "./plugins/ImagesPlugin"; -import { LinkPlugin } from "./plugins/LinkPlugin"; -import { OnChangePlugin } from "./plugins/OnChangePlugin"; - -export type EditorProps = { - value?: string; - onChange?: ( - editorState: EditorState, - editor: LexicalEditor, - tags: Set, - ) => void; -}; -const EditorInternal = ( - { value, onChange }: EditorProps, - ref: Ref, -) => { - const editorRef = useRef(null); - const [floatingAnchorElement, setFloatingAnchorElement] = - useState(null); - - useEffect(() => { - queueMicrotask(() => { - // console.log("sssss", $getRoot().isEmpty()); - if (editorRef.current) { - // clear if value is null or empty string - if (!value) { - // editorRef.current.update(() => { - // $getRoot().clear(); - // }); - } - if ( - value && - value !== JSON.stringify(editorRef.current.getEditorState()) - ) { - editorRef.current.setEditorState( - editorRef.current.parseEditorState(value), - ); - } - } - }); - }, [value]); - - const onRef = (_floatingAnchorElement: HTMLDivElement) => { - setFloatingAnchorElement(_floatingAnchorElement); - }; - const CustomContentEditable = useMemo(() => { - return ( -
    -
    - *]:py-[3px]", - )} - /> -
    -
    - ); - }, []); - const CustomPlaceholder = useMemo(() => { - return ( -
    - Enter some text... -
    - ); - }, []); - - const initialConfig: InitialConfigType = { - namespace: "Editor", - nodes: [AutoLinkNode, HeadingNode, ImageNode, LinkNode], - editable: true, - // editorState: value - // ? (editor) => { - // console.log("editor", editor); - // editor.setEditorState(editor.parseEditorState(value)); - // } - // : undefined, - theme: { - text: { - bold: "text-bold", - italic: "text-italic", - underline: "text-underline", - code: "text-code", - highlight: "text-highlight", - strikethrough: "text-strikethrough", - subscript: "text-subscript", - superscript: "text-superscript", - }, - heading: { - // Flowbite examples: https://flowbite.com/docs/typography/headings/#heading-one-h1 - h1: "text-5xl font-extrabold dark:text-white", - h2: "text-4xl font-bold dark:text-white", - h3: "text-3xl font-bold dark:text-white", - h4: "text-2xl font-bold dark:text-white", - h5: "text-xl font-bold dark:text-white", - }, - paragraph: "text-base", - }, - onError: (error, editor) => { - console.log("error", error, editor); - }, - }; - - return ( - -
    - - - - - - - - - - - - {/* {floatingAnchorElem ? ( */} - {/* <> */} - {/* */} - {/* */} - {/* ) : ( */} - {/* "" */} - {/* )} */} -
    -
    - ); -}; - -export const Editor = forwardRef(EditorInternal); diff --git a/@acme/ui/src/editor/constants.ts b/@acme/ui/src/editor/constants.ts deleted file mode 100644 index a2365110..00000000 --- a/@acme/ui/src/editor/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const DRAGGABLE_KEY = "draggable-key"; - -export const CAN_USE_DOM: boolean = - typeof window !== "undefined" && - window.document.createElement !== undefined; diff --git a/@acme/ui/src/editor/hooks/useDragListeners.ts b/@acme/ui/src/editor/hooks/useDragListeners.ts deleted file mode 100644 index 9932bbb8..00000000 --- a/@acme/ui/src/editor/hooks/useDragListeners.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { useEffect } from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { COMMAND_PRIORITY_LOW, DRAGOVER_COMMAND } from "lexical"; - -import { DRAGGABLE_KEY } from "../constants"; -import { draggableStore } from "../stores/useDraggableStore"; -import { isHTMLElement } from "../utils/guard"; -import { useEditorKeys } from "./useEditorKeys"; -import { useOnDragEnter } from "./useOnDragEnter"; - -export const useDragListeners = () => { - const [editor] = useLexicalComposerContext(); - const { handleOnDragEnter } = useOnDragEnter(); - - // Get editor node keys (code is below) - const { keys } = useEditorKeys(); - - useEffect(() => { - const addListeners = () => { - for (const key of keys) { - // Get HTML element by node "key" - const htmlElement = editor.getElementByKey(key); - - if (!htmlElement) { - console.warn("[useDragListeners] No html element"); - continue; - } - - htmlElement.setAttribute(DRAGGABLE_KEY, key); - - // NOTE: Don't use "mouseover"/"mousemove" because then it will be triggered on children too! - htmlElement.addEventListener("mouseenter", setDraggableElement); - - // We need "dragenter" with "DRAGOVER_COMMAND" because: - // 1. target on "dragenter" -> current html element; - // 2. target on "DRAGOVER_COMMAND" -> editable container; - // 3. without "DRAGOVER_COMMAND" -> "DROP_COMMAND" will not work; - htmlElement.addEventListener("dragenter", handleOnDragEnter); - - // event listeners will be added later ... - } - }; - addListeners(); - - const removeListeners = () => { - for (const key of keys) { - const htmlElement = editor.getElementByKey(key); - - if (!htmlElement) { - console.warn("[useDragListeners] No html element"); - continue; - } - - htmlElement.removeEventListener("mouseenter", setDraggableElement); - htmlElement.removeEventListener("dragenter", handleOnDragEnter); - } - }; - return () => { - removeListeners(); - }; - }, [editor, handleOnDragEnter, keys]); - - useEffect(() => { - // We need to register for drop to work with "dragenter" event - // Without overriding this command "DROP_COMMAND" will not be triggered - editor.registerCommand( - DRAGOVER_COMMAND, - (event) => handleOnDragEnter(event), - COMMAND_PRIORITY_LOW, - ); - }, [editor, handleOnDragEnter]); -}; - -const setDraggableElement = ({ target }: MouseEvent) => { - if (!isHTMLElement(target)) { - console.warn("[callback] target is not HTMLElement"); - return; - } - - const coordinates = target.getBoundingClientRect(); - - draggableStore.getState().setDraggable({ - htmlElement: target, - data: { - top: coordinates.top, - left: coordinates.left, - height: coordinates.height, - }, - }); -}; diff --git a/@acme/ui/src/editor/hooks/useEditorKeys.ts b/@acme/ui/src/editor/hooks/useEditorKeys.ts deleted file mode 100644 index 095208be..00000000 --- a/@acme/ui/src/editor/hooks/useEditorKeys.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { $getRoot } from "lexical"; - -import { useDraggableStore } from "../stores/useDraggableStore"; - -export const useEditorKeys = () => { - const [editor] = useLexicalComposerContext(); - - const { resetState } = useDraggableStore(); - - const getEditorKeys = useCallback(() => { - return editor.getEditorState().read(() => $getRoot().getChildrenKeys()); - }, [editor]); - - // We set keys on app start ... - const [keys, setKeys] = useState(getEditorKeys()); - - useEffect(() => { - return editor.registerUpdateListener(() => { - // ... and on any state change - setKeys(getEditorKeys()); - resetState(); - }); - }, [editor, getEditorKeys, resetState]); - - return { keys }; -}; diff --git a/@acme/ui/src/editor/hooks/useOnDragEnter.ts b/@acme/ui/src/editor/hooks/useOnDragEnter.ts deleted file mode 100644 index 2a2420f9..00000000 --- a/@acme/ui/src/editor/hooks/useOnDragEnter.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useCallback } from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; - -import { DRAGGABLE_KEY } from "../constants"; -import { draggableStore } from "../stores/useDraggableStore"; -import { isHTMLElement } from "../utils/guard"; - -export const useOnDragEnter = () => { - const [editor] = useLexicalComposerContext(); - - const handleOnDragEnter = useCallback( - (event: DragEvent): boolean => { - // Without this drop will not work; - event.preventDefault(); - - const target = event.currentTarget; - - if (!isHTMLElement(target)) { - console.error("[On drag enter] CurrentTarget is not Html element"); - return false; - } - - // Use key-value that we set before in the "listeners" hook. - const key = target.getAttribute(DRAGGABLE_KEY); - - if (key) { - console.log(`Lexical node key is ${key}`); - } else { - return false; - } - - const element = editor.getElementByKey(key); - - if (!isHTMLElement(element)) { - console.error("[handleOnDragEnter] element is not HTMLElement"); - return false; - } - - const coordinates = element.getBoundingClientRect(); - - draggableStore.getState().setLine({ - htmlElement: element, - data: { - top: coordinates.top, - left: coordinates.left, - height: coordinates.height, - width: coordinates.width, - }, - }); - - return true; - }, - [editor], - ); - - return { handleOnDragEnter }; -}; diff --git a/@acme/ui/src/editor/hooks/useOnDrop.ts b/@acme/ui/src/editor/hooks/useOnDrop.ts deleted file mode 100644 index 619b97fb..00000000 --- a/@acme/ui/src/editor/hooks/useOnDrop.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { useCallback, useEffect } from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { $getNodeByKey, COMMAND_PRIORITY_HIGH, DROP_COMMAND } from "lexical"; - -import { DRAGGABLE_KEY } from "../constants"; -import { draggableStore } from "../stores/useDraggableStore"; - -export const useOnDrop = () => { - const [editor] = useLexicalComposerContext(); - - const handleOnDrop = useCallback((): boolean => { - const lineElement = draggableStore.getState().line?.htmlElement; - const draggableElement = draggableStore.getState().draggable?.htmlElement; - - const draggableKey = draggableElement?.getAttribute(DRAGGABLE_KEY); - if (!draggableKey) { - console.error("NO DRAGGABLE KEY"); - return false; - } - - const closestToLineElementKey = lineElement?.getAttribute(DRAGGABLE_KEY); - if (!closestToLineElementKey) { - console.error("[ON DROP] no closestToLineElementKey"); - return false; - } - - const lineLexicalNode = $getNodeByKey(closestToLineElementKey); - - const draggableLexicalNode = $getNodeByKey(draggableKey); - if (!draggableLexicalNode) { - console.error("NO DRAGGABLE ELEMENT FOUND"); - return false; - } - - // Inserts a node after this LexicalNode (as the next sibling). - lineLexicalNode?.insertAfter(draggableLexicalNode); - - return true; - }, []); - - useEffect(() => { - // THIS EVENT WILL BE TRIGGERED ON DROP IN EDITOR - editor.registerCommand( - DROP_COMMAND, - () => { - return handleOnDrop(); - }, - COMMAND_PRIORITY_HIGH, - ); - }, [editor, handleOnDrop]); - - return { handleOnDrop }; -}; diff --git a/@acme/ui/src/editor/index.ts b/@acme/ui/src/editor/index.ts deleted file mode 100644 index ca69f6ed..00000000 --- a/@acme/ui/src/editor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./Editor"; diff --git a/@acme/ui/src/editor/nodes/ImageNode/ImageComponent.tsx b/@acme/ui/src/editor/nodes/ImageNode/ImageComponent.tsx deleted file mode 100644 index ca981beb..00000000 --- a/@acme/ui/src/editor/nodes/ImageNode/ImageComponent.tsx +++ /dev/null @@ -1,437 +0,0 @@ -import type { - BaseSelection, - LexicalCommand, - LexicalEditor, - NodeKey, -} from "lexical"; -import * as React from "react"; -import { Suspense, useCallback, useEffect, useRef, useState } from "react"; -import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary"; -import { HashtagPlugin } from "@lexical/react/LexicalHashtagPlugin"; -import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"; -import { LexicalNestedComposer } from "@lexical/react/LexicalNestedComposer"; -import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; -import { useLexicalNodeSelection } from "@lexical/react/useLexicalNodeSelection"; -import { mergeRegister } from "@lexical/utils"; -import { - $getNodeByKey, - $getSelection, - $isNodeSelection, - $isRangeSelection, - $setSelection, - CLICK_COMMAND, - COMMAND_PRIORITY_LOW, - createCommand, - DRAGSTART_COMMAND, - KEY_BACKSPACE_COMMAND, - KEY_DELETE_COMMAND, - KEY_ENTER_COMMAND, - KEY_ESCAPE_COMMAND, - SELECTION_CHANGE_COMMAND, -} from "lexical"; - -import { useEditorHistory } from "../../stores/useHistory"; -// import { createWebsocketProvider } from "../collaboration"; -// import { useSettings } from "../context/SettingsContext"; -// import { useSharedHistoryContext } from "../context/SharedHistoryContext"; -// import EmojisPlugin from "../plugins/EmojisPlugin"; -// import KeywordsPlugin from "../plugins/KeywordsPlugin"; -// import LinkPlugin from "../plugins/LinkPlugin"; -// import MentionsPlugin from "../plugins/MentionsPlugin"; -// import TreeViewPlugin from "../plugins/TreeViewPlugin"; -// import ContentEditable from "../ui/ContentEditable"; -// import ImageResizer from "../ui/ImageResizer"; -// import Placeholder from "../ui/Placeholder"; -import { $isImageNode } from "./ImageNode"; -import ImageResizer from "./ImageResizer"; - -const imageCache = new Set(); - -export const RIGHT_CLICK_IMAGE_COMMAND: LexicalCommand = - createCommand("RIGHT_CLICK_IMAGE_COMMAND"); - -function useSuspenseImage(source: string) { - if (!imageCache.has(source)) { - // eslint-disable-next-line @typescript-eslint/only-throw-error - throw new Promise((resolve) => { - const img = new Image(); - img.src = source; - // img.src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/1280px-Image_created_with_a_mobile_phone.png" - img.addEventListener('load', () => { - imageCache.add(source); - resolve(null); - }); - img.onerror = () => { - imageCache.add(source); - }; - }); - } -} - -function LazyImage({ - altText, - className, - imageRef, - src, - width, - height: _, - maxWidth: __, -}: { - altText: string; - className: string | null; - height: "inherit" | number; - imageRef: { current: null | HTMLImageElement }; - maxWidth: number; - src: string; - width: "inherit" | number; -}): JSX.Element { - useSuspenseImage(src); - return ( - {altText} - ); -} - -export default function ImageComponent({ - src, - altText, - nodeKey, - width, - height, - maxWidth, - resizable, - showCaption, - caption, - captionsEnabled, -}: { - altText: string; - caption: LexicalEditor; - height: "inherit" | number; - maxWidth: number; - nodeKey: NodeKey; - resizable: boolean; - showCaption: boolean; - src: string; - width: "inherit" | number; - captionsEnabled: boolean; -}): JSX.Element { - const imageRef = useRef(null); - const buttonRef = useRef(null); - const [isSelected, setSelected, clearSelection] = - useLexicalNodeSelection(nodeKey); - const [isResizing, setIsResizing] = useState(false); - // const { isCollabActive } = useCollaborationContext(); - const [editor] = useLexicalComposerContext(); - const [selection, setSelection] = useState(null); - const activeEditorRef = useRef(null); - - const onDelete = useCallback( - (payload: KeyboardEvent) => { - if (isSelected && $isNodeSelection($getSelection())) { - const event: KeyboardEvent = payload; - event.preventDefault(); - const node = $getNodeByKey(nodeKey); - if ($isImageNode(node)) { - node.remove(); - } - } - return false; - }, - [isSelected, nodeKey], - ); - - const onEnter = useCallback( - (event: KeyboardEvent) => { - const latestSelection = $getSelection(); - const buttonElement = buttonRef.current; - if ( - isSelected && - $isNodeSelection(latestSelection) && - latestSelection.getNodes().length === 1 - ) { - if (showCaption) { - // Move focus into nested editor - $setSelection(null); - event.preventDefault(); - caption.focus(); - return true; - } else if ( - buttonElement !== null && - buttonElement !== document.activeElement - ) { - event.preventDefault(); - buttonElement.focus(); - return true; - } - } - return false; - }, - [caption, isSelected, showCaption], - ); - - const onEscape = useCallback( - (event: KeyboardEvent) => { - if ( - activeEditorRef.current === caption || - buttonRef.current === event.target - ) { - $setSelection(null); - editor.update(() => { - setSelected(true); - const parentRootElement = editor.getRootElement(); - if (parentRootElement !== null) { - parentRootElement.focus(); - } - }); - return true; - } - return false; - }, - [caption, editor, setSelected], - ); - - const onClick = useCallback( - (payload: MouseEvent) => { - const event = payload; - - if (isResizing) { - return true; - } - if (event.target === imageRef.current) { - if (event.shiftKey) { - setSelected(!isSelected); - } else { - clearSelection(); - setSelected(true); - } - return true; - } - - return false; - }, - [isResizing, isSelected, setSelected, clearSelection], - ); - - const onRightClick = useCallback( - (event: MouseEvent): void => { - editor.getEditorState().read(() => { - const latestSelection = $getSelection(); - const domElement = event.target as HTMLElement; - if ( - domElement.tagName === "IMG" && - $isRangeSelection(latestSelection) && - latestSelection.getNodes().length === 1 - ) { - editor.dispatchCommand(RIGHT_CLICK_IMAGE_COMMAND, event); - } - }); - }, - [editor], - ); - - useEffect(() => { - let isMounted = true; - const rootElement = editor.getRootElement(); - const unregister = mergeRegister( - editor.registerUpdateListener(({ editorState }) => { - if (isMounted) { - setSelection(editorState.read(() => $getSelection())); - } - }), - editor.registerCommand( - SELECTION_CHANGE_COMMAND, - (_, activeEditor) => { - activeEditorRef.current = activeEditor; - return false; - }, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - CLICK_COMMAND, - onClick, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - RIGHT_CLICK_IMAGE_COMMAND, - onClick, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - DRAGSTART_COMMAND, - (event) => { - if (event.target === imageRef.current) { - // TODO This is just a temporary workaround for FF to behave like other browsers. - // Ideally, this handles drag & drop too (and all browsers). - event.preventDefault(); - return true; - } - return false; - }, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - KEY_DELETE_COMMAND, - onDelete, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - KEY_BACKSPACE_COMMAND, - onDelete, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW), - editor.registerCommand( - KEY_ESCAPE_COMMAND, - onEscape, - COMMAND_PRIORITY_LOW, - ), - ); - - rootElement?.addEventListener("contextmenu", onRightClick); - - return () => { - isMounted = false; - unregister(); - rootElement?.removeEventListener("contextmenu", onRightClick); - }; - }, [ - clearSelection, - editor, - isResizing, - isSelected, - nodeKey, - onDelete, - onEnter, - onEscape, - onClick, - onRightClick, - setSelected, - ]); - - const setShowCaption = () => { - editor.update(() => { - const node = $getNodeByKey(nodeKey); - if ($isImageNode(node)) { - node.setShowCaption(true); - } - }); - }; - - const onResizeEnd = ( - nextWidth: "inherit" | number, - nextHeight: "inherit" | number, - ) => { - // Delay hiding the resize bars for click case - setTimeout(() => { - setIsResizing(false); - }, 200); - - editor.update(() => { - const node = $getNodeByKey(nodeKey); - if ($isImageNode(node)) { - node.setWidthAndHeight(nextWidth, nextHeight); - } - }); - }; - - const onResizeStart = () => { - setIsResizing(true); - }; - - const { historyState } = useEditorHistory(); - // const { - // settings: { showNestedEditorTreeView }, - // } = useSettings(); - - const draggable = isSelected && $isNodeSelection(selection) && !isResizing; - const isFocused = isSelected || isResizing; - return ( - - <> -
    -
    - - -
    -
    - {showCaption && ( -
    - - - {/* */} - {/* */} - {/* */} - - {/* */} - {/* {isCollabActive ? ( */} - {/* */} - {/* ) : ( */} - {/* */} - {/* )} */} - - } - placeholder={
    Enter a caption...
    } - ErrorBoundary={LexicalErrorBoundary} - /> - {/* {showNestedEditorTreeView === true ? : null} */} -
    -
    - )} - {resizable && $isNodeSelection(selection) && isFocused && ( - - )} - -
    - ); -} diff --git a/@acme/ui/src/editor/nodes/ImageNode/ImageNode.tsx b/@acme/ui/src/editor/nodes/ImageNode/ImageNode.tsx deleted file mode 100644 index 4363f086..00000000 --- a/@acme/ui/src/editor/nodes/ImageNode/ImageNode.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import type { - DOMConversionMap, - DOMConversionOutput, - DOMExportOutput, - EditorConfig, - LexicalEditor, - LexicalNode, - NodeKey, - SerializedEditor, - SerializedLexicalNode, - Spread, -} from "lexical"; -import * as React from "react"; -import { Suspense } from "react"; -import { $applyNodeReplacement, createEditor, DecoratorNode } from "lexical"; - -const ImageComponent = React.lazy(() => import("./ImageComponent")); - -export interface ImagePayload { - altText: string; - caption?: LexicalEditor; - height?: number; - key?: NodeKey; - maxWidth?: number; - showCaption?: boolean; - src: string; - width?: number; - captionsEnabled?: boolean; -} - -function convertImageElement(domNode: Node): null | DOMConversionOutput { - if (domNode instanceof HTMLImageElement) { - const { alt: altText, src, width, height } = domNode; - const node = $createImageNode({ altText, height, src, width }); - return { node }; - } - return null; -} - -export type SerializedImageNode = Spread< - { - altText: string; - caption: SerializedEditor; - height?: number; - maxWidth: number; - showCaption: boolean; - src: string; - width?: number; - }, - SerializedLexicalNode ->; - -export class ImageNode extends DecoratorNode { - __src: string; - __altText: string; - __width: "inherit" | number; - __height: "inherit" | number; - __maxWidth: number; - __showCaption: boolean; - __caption: LexicalEditor; - // Captions cannot yet be used within editor cells - __captionsEnabled: boolean; - - static getType(): string { - return "image"; - } - - static clone(node: ImageNode): ImageNode { - return new ImageNode( - node.__src, - node.__altText, - node.__maxWidth, - node.__width, - node.__height, - node.__showCaption, - node.__caption, - node.__captionsEnabled, - node.__key, - ); - } - - static importJSON(serializedNode: SerializedImageNode): ImageNode { - const { altText, height, width, maxWidth, caption, src, showCaption } = - serializedNode; - const node = $createImageNode({ - altText, - height, - maxWidth, - showCaption, - src, - width, - }); - const nestedEditor = node.__caption; - const editorState = nestedEditor.parseEditorState(caption.editorState); - if (!editorState.isEmpty()) { - nestedEditor.setEditorState(editorState); - } - return node; - } - - exportDOM(): DOMExportOutput { - const element = document.createElement("img"); - element.setAttribute("src", this.__src); - element.setAttribute("alt", this.__altText); - element.setAttribute("width", this.__width.toString()); - // element.setAttribute("height", this.__height.toString()); - return { element }; - } - - static importDOM(): DOMConversionMap | null { - return { - img: () => ({ - conversion: convertImageElement, - priority: 0, - }), - }; - } - - constructor( - source: string, - altText: string, - maxWidth: number, - width?: "inherit" | number, - height?: "inherit" | number, - showCaption?: boolean, - caption?: LexicalEditor, - captionsEnabled?: boolean, - key?: NodeKey, - ) { - super(key); - this.__src = source; - this.__altText = altText; - this.__maxWidth = maxWidth; - this.__width = width ?? "inherit"; - this.__height = height ?? "inherit"; - this.__showCaption = showCaption ?? false; - this.__caption = caption ?? createEditor(); - this.__captionsEnabled = !!captionsEnabled || captionsEnabled === undefined; - } - - exportJSON(): SerializedImageNode { - return { - altText: this.getAltText(), - caption: this.__caption.toJSON(), - height: this.__height === "inherit" ? 0 : this.__height, - maxWidth: this.__maxWidth, - showCaption: this.__showCaption, - src: this.getSrc(), - type: "image", - version: 1, - width: this.__width === "inherit" ? 0 : this.__width, - }; - } - - setWidthAndHeight( - width: "inherit" | number, - height: "inherit" | number, - ): void { - const writable = this.getWritable(); - writable.__width = width; - writable.__height = height; - } - - setShowCaption(showCaption: boolean): void { - const writable = this.getWritable(); - writable.__showCaption = showCaption; - } - - // View - - createDOM(config: EditorConfig): HTMLElement { - const span = document.createElement("span"); - const theme = config.theme; - const className = theme.image; - if (className !== undefined) { - span.className = className; - } - return span; - } - - updateDOM(): false { - return false; - } - - getSrc(): string { - return this.__src; - } - - getAltText(): string { - return this.__altText; - } - - decorate(): JSX.Element { - return ( - - - - ); - } -} - -export function $createImageNode({ - altText, - height, - maxWidth = 500, - captionsEnabled, - src, - width, - showCaption, - caption, - key, -}: ImagePayload): ImageNode { - return $applyNodeReplacement( - new ImageNode( - src, - altText, - maxWidth, - width, - height, - showCaption, - caption, - captionsEnabled, - key, - ), - ); -} - -export function $isImageNode( - node: LexicalNode | null | undefined, -): node is ImageNode { - return node instanceof ImageNode; -} diff --git a/@acme/ui/src/editor/nodes/ImageNode/ImageResizer.tsx b/@acme/ui/src/editor/nodes/ImageNode/ImageResizer.tsx deleted file mode 100644 index c4688f81..00000000 --- a/@acme/ui/src/editor/nodes/ImageNode/ImageResizer.tsx +++ /dev/null @@ -1,304 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-condition */ -import type { LexicalEditor } from "lexical"; -import * as React from "react"; -import { useRef } from "react"; - -import { clsm } from "../../.."; -import { Icon } from "../../../icons"; -import { Tooltip } from "../../../tooltip"; - -function clamp(value: number, min: number, max: number) { - return Math.min(Math.max(value, min), max); -} - -const Direction = { - east: Math.trunc(1), - north: 1 << 3, - south: 1 << 1, - west: 1 << 2, -}; - -export default function ImageResizer({ - onResizeStart, - onResizeEnd, - buttonRef, - imageRef, - maxWidth, - editor, - showCaption, - setShowCaption, - captionsEnabled, -}: { - editor: LexicalEditor; - buttonRef: { current: null | HTMLButtonElement }; - imageRef: { current: null | HTMLElement }; - maxWidth?: number; - onResizeEnd: (width: "inherit" | number, height: "inherit" | number) => void; - onResizeStart: () => void; - setShowCaption: (show: boolean) => void; - showCaption: boolean; - captionsEnabled: boolean; -}): JSX.Element { - const controlWrapperRef = useRef(null); - const userSelect = useRef({ - priority: "", - value: "default", - }); - const positioningRef = useRef<{ - currentHeight: "inherit" | number; - currentWidth: "inherit" | number; - direction: number; - isResizing: boolean; - ratio: number; - startHeight: number; - startWidth: number; - startX: number; - startY: number; - }>({ - currentHeight: 0, - currentWidth: 0, - direction: 0, - isResizing: false, - ratio: 0, - startHeight: 0, - startWidth: 0, - startX: 0, - startY: 0, - }); - const editorRootElement = editor.getRootElement(); - // Find max width, accounting for editor padding. - const maxWidthContainer = maxWidth - ? maxWidth - : (editorRootElement === null - ? 100 - : editorRootElement.getBoundingClientRect().width - 20); - const maxHeightContainer = - editorRootElement === null - ? 100 - : editorRootElement.getBoundingClientRect().height - 20; - - const minWidth = 100; - const minHeight = 100; - - const setStartCursor = (_direction: number) => { - // const ew = direction === Direction.east || direction === Direction.west; - // const ns = direction === Direction.north || direction === Direction.south; - // const nwse = - // (direction & Direction.north && direction & Direction.west) || - // (direction & Direction.south && direction & Direction.east); - // - // const cursorDir = ew ? "ew" : ns ? "ns" : nwse ? "nwse" : "nesw"; - - if (editorRootElement !== null) { - editorRootElement.style.setProperty( - "cursor", - // `${cursorDir}-resize`, - `col-resize`, - "important", - ); - editorRootElement.style.setProperty("display", "block"); - } - if (document.body !== null) { - document.body.style.setProperty( - "cursor", - // `${cursorDir}-resize`, - `col-resize`, - "important", - ); - userSelect.current.value = document.body.style.getPropertyValue( - "-webkit-user-select", - ); - userSelect.current.priority = document.body.style.getPropertyPriority( - "-webkit-user-select", - ); - document.body.style.setProperty( - "-webkit-user-select", - `none`, - "important", - ); - } - }; - - const setEndCursor = () => { - if (editorRootElement !== null) { - editorRootElement.style.setProperty("cursor", "text"); - } - if (document.body !== null) { - document.body.style.setProperty("cursor", "default"); - document.body.style.setProperty( - "-webkit-user-select", - userSelect.current.value, - userSelect.current.priority, - ); - } - }; - - const handlePointerDown = ( - event: React.PointerEvent, - direction: number, - ) => { - if (!editor.isEditable()) { - return; - } - - const image = imageRef.current; - const controlWrapper = controlWrapperRef.current; - - if (image !== null && controlWrapper !== null) { - event.preventDefault(); - const { width, height } = image.getBoundingClientRect(); - const positioning = positioningRef.current; - positioning.startWidth = width; - positioning.startHeight = height; - positioning.ratio = width / height; - positioning.currentWidth = width; - positioning.currentHeight = height; - positioning.startX = event.clientX; - positioning.startY = event.clientY; - positioning.isResizing = true; - positioning.direction = direction; - - setStartCursor(direction); - onResizeStart(); - - controlWrapper.classList.add(...clsm("block touch-none").split(" ")); - controlWrapper.classList.remove(...clsm("hidden").split(" ")); - image.style.height = `${height}px`; - image.style.width = `${width}px`; - - document.addEventListener("pointermove", handlePointerMove); - document.addEventListener("pointerup", handlePointerUp); - } - }; - const handlePointerMove = (event: PointerEvent) => { - const image = imageRef.current; - const positioning = positioningRef.current; - - const isHorizontal = - positioning.direction & (Direction.east | Direction.west); - const isVertical = - positioning.direction & (Direction.south | Direction.north); - - if (image !== null && positioning.isResizing) { - // Corner cursor - if (isHorizontal && isVertical) { - let diff = Math.floor(positioning.startX - event.clientX); - diff = positioning.direction & Direction.east ? -diff : diff; - - const width = clamp( - positioning.startWidth + diff, - minWidth, - maxWidthContainer, - ); - - const height = width / positioning.ratio; - image.style.width = `${width}px`; - image.style.height = `${height}px`; - positioning.currentHeight = height; - positioning.currentWidth = width; - } else if (isVertical) { - let diff = Math.floor(positioning.startY - event.clientY); - diff = positioning.direction & Direction.south ? -diff : diff; - - const height = clamp( - positioning.startHeight + diff, - minHeight, - maxHeightContainer, - ); - - image.style.height = `${height}px`; - positioning.currentHeight = height; - } else { - let diff = Math.floor(positioning.startX - event.clientX); - diff = positioning.direction & Direction.east ? -diff : diff; - - const width = clamp( - positioning.startWidth + diff, - minWidth, - maxWidthContainer, - ); - - image.style.width = `${width}px`; - positioning.currentWidth = width; - } - } - }; - const handlePointerUp = () => { - const image = imageRef.current; - const positioning = positioningRef.current; - const controlWrapper = controlWrapperRef.current; - if (image !== null && controlWrapper !== null && positioning.isResizing) { - const width = positioning.currentWidth; - const height = positioning.currentHeight; - positioning.startWidth = 0; - positioning.startHeight = 0; - positioning.ratio = 0; - positioning.startX = 0; - positioning.startY = 0; - positioning.currentWidth = 0; - positioning.currentHeight = 0; - positioning.isResizing = false; - - controlWrapper.classList.remove(...clsm("block touch-none").split(" ")); - controlWrapper.classList.add(...clsm("hidden").split(" ")); - - setEndCursor(); - onResizeEnd(width, height); - - document.removeEventListener("pointermove", handlePointerMove); - document.removeEventListener("pointerup", handlePointerUp); - } - }; - return ( -
    - {!showCaption && captionsEnabled && ( - - )} -
    - -
    - -
    -
    - -
    - -
    -
    -
    -
    { - handlePointerDown(event, Direction.south | Direction.west); - }} - /> -
    { - handlePointerDown(event, Direction.south | Direction.west); - }} - /> -
    - ); -} diff --git a/@acme/ui/src/editor/nodes/ImageNode/index.ts b/@acme/ui/src/editor/nodes/ImageNode/index.ts deleted file mode 100644 index 9cd5727e..00000000 --- a/@acme/ui/src/editor/nodes/ImageNode/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ImageNode } from "./ImageNode"; diff --git a/@acme/ui/src/editor/plugins/AutoLinkPlugin/AutoLinkPlugin.tsx b/@acme/ui/src/editor/plugins/AutoLinkPlugin/AutoLinkPlugin.tsx deleted file mode 100644 index 1143cd9b..00000000 --- a/@acme/ui/src/editor/plugins/AutoLinkPlugin/AutoLinkPlugin.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { - createLinkMatcherWithRegExp, - AutoLinkPlugin as LexicalAutoLinkPlugin, -} from "@lexical/react/LexicalAutoLinkPlugin"; - -const URL_REGEX = - /(https?:\/\/(?:www\.|(?!www))[\dA-Za-z][\dA-Za-z-]+[\dA-Za-z]\.\S{2,}|www\.[\dA-Za-z][\dA-Za-z-]+[\dA-Za-z]\.\S{2,}|https?:\/\/(?:www\.|(?!www))[\dA-Za-z]+\.\S{2,}|www\.[\dA-Za-z]+\.\S{2,})/; -const EMAIL_REGEX = - /(([^\s"(),.:;<>@[\\\]]+(\.[^\s"(),.:;<>@[\\\]]+)*)|(".+"))@((\[(?:\d{1,3}\.){3}\d{1,3}])|(([\dA-Za-z\-]+\.)+[A-Za-z]{2,}))/; -const MATCHERS = [ - createLinkMatcherWithRegExp(URL_REGEX, (text) => { - return text; - }), - createLinkMatcherWithRegExp(EMAIL_REGEX, (text) => { - return `mailto:${text}`; - }), -]; - -export const AutoLinkPlugin = () => { - return ; -}; diff --git a/@acme/ui/src/editor/plugins/AutoLinkPlugin/index.ts b/@acme/ui/src/editor/plugins/AutoLinkPlugin/index.ts deleted file mode 100644 index ba03c6a3..00000000 --- a/@acme/ui/src/editor/plugins/AutoLinkPlugin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./AutoLinkPlugin"; diff --git a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/ComponentPicker.tsx b/@acme/ui/src/editor/plugins/ComponentPickerPlugin/ComponentPicker.tsx deleted file mode 100644 index e6d2d075..00000000 --- a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/ComponentPicker.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import type { TextNode } from "lexical"; -import { useCallback, useMemo, useState } from "react"; -import * as React from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { - LexicalTypeaheadMenuPlugin, - useBasicTypeaheadTriggerMatch, -} from "@lexical/react/LexicalTypeaheadMenuPlugin"; -import * as ReactDOM from "react-dom"; - -import type { ComponentPickerOption } from "./types"; -import { useModal } from "../../../modal"; -import { ComponentPickerMenuItem } from "./ComponentPickerMenuItem"; -import { getBaseOptions } from "./options"; - -export default function ComponentPickerMenuPlugin(): JSX.Element { - const [editor] = useLexicalComposerContext(); - const [modal, showModal] = useModal(); - const [queryString, setQueryString] = useState(null); - - const checkForTriggerMatch = useBasicTypeaheadTriggerMatch("/", { - minLength: 0, - }); - - const options = useMemo(() => { - const baseOptions = getBaseOptions(editor, showModal); - - if (!queryString) { - return baseOptions; - } - - const regex = new RegExp(queryString, "i"); - - return ( - // ...getDynamicOptions(editor, queryString), - baseOptions.filter( - (option) => - regex.test(option.title) || - option.keywords.some((keyword) => regex.test(keyword)), - ) - ); - }, [editor, queryString, showModal]); - - const onSelectOption = useCallback( - ( - selectedOption: ComponentPickerOption, - nodeToRemove: TextNode | null, - closeMenu: () => void, - matchingString: string, - ) => { - editor.update(() => { - nodeToRemove?.remove(); - selectedOption.onSelect(matchingString); - closeMenu(); - }); - }, - [editor], - ); - - return ( - <> - {modal} - - onQueryChange={setQueryString} - onSelectOption={onSelectOption} - triggerFn={checkForTriggerMatch} - options={options} - menuRenderFn={( - anchorElementRef, - { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }, - ) => - anchorElementRef.current && options.length > 0 - ? ReactDOM.createPortal( -
    -
      - {options.map((option, index: number) => ( - { - setHighlightedIndex(index); - selectOptionAndCleanUp(option); - }} - onMouseEnter={() => { - setHighlightedIndex(index); - }} - key={option.key} - option={option} - /> - ))} -
    -
    , - anchorElementRef.current, - ) - : null - } - /> - - ); -} diff --git a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/ComponentPickerMenuItem.tsx b/@acme/ui/src/editor/plugins/ComponentPickerPlugin/ComponentPickerMenuItem.tsx deleted file mode 100644 index 377b5bc8..00000000 --- a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/ComponentPickerMenuItem.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { clsm } from "@acme/ui"; - -import type { ComponentPickerOption } from "./types"; - -export const ComponentPickerMenuItem = ({ - index, - isSelected, - onClick, - onMouseEnter, - option, -}: { - index: number; - isSelected: boolean; - onClick: () => void; - onMouseEnter: () => void; - option: ComponentPickerOption; -}) => { - return ( -
  • - ); -}; diff --git a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/index.ts b/@acme/ui/src/editor/plugins/ComponentPickerPlugin/index.ts deleted file mode 100644 index e105aa98..00000000 --- a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./ComponentPicker"; diff --git a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/options.tsx b/@acme/ui/src/editor/plugins/ComponentPickerPlugin/options.tsx deleted file mode 100644 index 4903cc82..00000000 --- a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/options.tsx +++ /dev/null @@ -1,240 +0,0 @@ -import type { LexicalEditor } from "lexical"; -import { $createCodeNode } from "@lexical/code"; -import { - INSERT_CHECK_LIST_COMMAND, - INSERT_ORDERED_LIST_COMMAND, - INSERT_UNORDERED_LIST_COMMAND, -} from "@lexical/list"; -import { $createHeadingNode, $createQuoteNode } from "@lexical/rich-text"; -import { $setBlocksType } from "@lexical/selection"; -// import { INSERT_TABLE_COMMAND } from "@lexical/table"; -import { - $createParagraphNode, - $getSelection, - $isRangeSelection, -} from "lexical"; - -import type { useModal } from "../../../modal"; -import { Icon } from "../../../icons"; -import { InsertImage } from "../ImagesPlugin/InsertImage"; -import { ComponentPickerOption } from "./types"; - -// function getDynamicOptions(editor: LexicalEditor, queryString: string) { -// const options: Array = []; -// -// if (queryString == null) { -// return options; -// } -// -// const tableMatch = queryString.match(/^([1-9]\d?)(?:x([1-9]\d?)?)?$/); -// -// if (tableMatch !== null) { -// const rows = tableMatch[1]; -// const colOptions = tableMatch[2] -// ? [tableMatch[2]] -// : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(String); -// -// options.push( -// ...colOptions.map( -// (columns) => -// new ComponentPickerOption(`${rows}x${columns} Table`, { -// icon: , -// keywords: ["table"], -// onSelect: () => { -// rows && -// editor.dispatchCommand(INSERT_TABLE_COMMAND, { columns, rows }); -// }, -// }), -// ), -// ); -// } -// -// return options; -// } - -type ShowModal = ReturnType[1]; - -export function getBaseOptions( - editor: LexicalEditor, - showModal: ShowModal, -): ComponentPickerOption[] { - return [ - new ComponentPickerOption("Text", { - icon: , - keywords: ["normal", "paragraph", "p", "text"], - onSelect: () => - editor.update(() => { - const selection = $getSelection(); - if ($isRangeSelection(selection)) { - $setBlocksType(selection, () => $createParagraphNode()); - } - }), - }), - ...([1, 2, 3] as const).map( - (n) => - new ComponentPickerOption(`Heading ${n}`, { - icon: ( - - ), - keywords: ["heading", "header", `h${n}`], - onSelect: () => - editor.update(() => { - const selection = $getSelection(); - if ($isRangeSelection(selection)) { - $setBlocksType(selection, () => $createHeadingNode(`h${n}`)); - } - }), - }), - ), - // new ComponentPickerOption("Table", { - // icon: , - // keywords: ["table", "grid", "spreadsheet", "rows", "columns"], - // onSelect: () => - // showModal("Insert Table", (onClose) => ( - // - // )), - // }), - new ComponentPickerOption("Numbered List", { - icon: , - keywords: ["numbered list", "ordered list", "ol"], - onSelect: () => - editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0), - }), - new ComponentPickerOption("Bulleted List", { - icon: , - keywords: ["bulleted list", "unordered list", "ul"], - onSelect: () => - editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0), - }), - new ComponentPickerOption("Check List", { - icon: , - keywords: ["check list", "todo list"], - onSelect: () => editor.dispatchCommand(INSERT_CHECK_LIST_COMMAND, void 0), - }), - new ComponentPickerOption("Quote", { - icon: , - keywords: ["block quote"], - onSelect: () => - editor.update(() => { - const selection = $getSelection(); - if ($isRangeSelection(selection)) { - $setBlocksType(selection, () => $createQuoteNode()); - } - }), - }), - new ComponentPickerOption("Code", { - icon: , - keywords: ["javascript", "python", "js", "codeblock"], - onSelect: () => - editor.update(() => { - const selection = $getSelection(); - - if ($isRangeSelection(selection)) { - if (selection.isCollapsed()) { - $setBlocksType(selection, () => $createCodeNode()); - } else { - // Will this ever happen? - const textContent = selection.getTextContent(); - const codeNode = $createCodeNode(); - selection.insertNodes([codeNode]); - selection.insertRawText(textContent); - } - } - }), - }), - // new ComponentPickerOption("Divider", { - // icon: , - // keywords: ["horizontal rule", "divider", "hr"], - // onSelect: () => - // editor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined), - // }), - // new ComponentPickerOption("Page Break", { - // icon: , - // keywords: ["page break", "divider"], - // onSelect: () => editor.dispatchCommand(INSERT_PAGE_BREAK, undefined), - // }), - // new ComponentPickerOption("Excalidraw", { - // icon: , - // keywords: ["excalidraw", "diagram", "drawing"], - // onSelect: () => - // editor.dispatchCommand(INSERT_EXCALIDRAW_COMMAND, undefined), - // }), - // new ComponentPickerOption("Poll", { - // icon: , - // keywords: ["poll", "vote"], - // onSelect: () => - // showModal("Insert Poll", (onClose) => ( - // - // )), - // }), - // ...EmbedConfigs.map( - // (embedConfig) => - // new ComponentPickerOption(`Embed ${embedConfig.contentName}`, { - // icon: embedConfig.icon, - // keywords: [...embedConfig.keywords, "embed"], - // onSelect: () => - // editor.dispatchCommand(INSERT_EMBED_COMMAND, embedConfig.type), - // }), - // ), - // new ComponentPickerOption("Equation", { - // icon: , - // keywords: ["equation", "latex", "math"], - // onSelect: () => - // showModal("Insert Equation", (onClose) => ( - // - // )), - // }), - // new ComponentPickerOption("GIF", { - // icon: , - // keywords: ["gif", "animate", "image", "file"], - // onSelect: () => - // editor.dispatchCommand(INSERT_IMAGE_COMMAND, { - // altText: "Cat typing on a laptop", - // src: catTypingGif, - // }), - // }), - new ComponentPickerOption("Image", { - icon: , - keywords: ["image", "photo", "picture", "file"], - onSelect: () => - showModal({ - title: "Insert Image", - content: (onCancel) => ( - - ), - // onCancel: onClose - }), - }), - // new ComponentPickerOption("Collapsible", { - // icon: , - // keywords: ["collapse", "collapsible", "toggle"], - // onSelect: () => - // editor.dispatchCommand(INSERT_COLLAPSIBLE_COMMAND, undefined), - // }), - // new ComponentPickerOption("Columns Layout", { - // icon: , - // keywords: ["columns", "layout", "grid"], - // onSelect: () => - // showModal("Insert Columns Layout", (onClose) => ( - // - // )), - // }), - // ...(["left", "center", "right", "justify"] as const).map( - // (alignment) => - // new ComponentPickerOption(`Align ${alignment}`, { - // icon: , - // keywords: ["align", "justify", alignment], - // onSelect: () => - // editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, alignment), - // }), - // ), - ]; -} diff --git a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/types.ts b/@acme/ui/src/editor/plugins/ComponentPickerPlugin/types.ts deleted file mode 100644 index 96c6da0c..00000000 --- a/@acme/ui/src/editor/plugins/ComponentPickerPlugin/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { MenuOption } from "@lexical/react/LexicalTypeaheadMenuPlugin"; - -export class ComponentPickerOption extends MenuOption { - // What shows up in the editor - title: string; - // Icon for display - icon?: JSX.Element; - // For extra searching. - keywords: Array; - // TBD - keyboardShortcut?: string; - // What happens when you select this option? - onSelect: (queryString: string) => void; - - constructor( - title: string, - options: { - icon?: JSX.Element; - keywords?: Array; - keyboardShortcut?: string; - onSelect: (queryString: string) => void; - }, - ) { - super(title); - this.title = title; - this.keywords = options.keywords ?? []; - this.icon = options.icon; - this.keyboardShortcut = options.keyboardShortcut; - this.onSelect = options.onSelect.bind(this); - } -} diff --git a/@acme/ui/src/editor/plugins/DraggableBlockPlugin/index.tsx b/@acme/ui/src/editor/plugins/DraggableBlockPlugin/index.tsx deleted file mode 100644 index 8c278f91..00000000 --- a/@acme/ui/src/editor/plugins/DraggableBlockPlugin/index.tsx +++ /dev/null @@ -1,445 +0,0 @@ -"use client"; - -import type { LexicalEditor } from "lexical"; -import type { DragEvent as ReactDragEvent } from "react"; -import * as React from "react"; -import { useEffect, useRef, useState } from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { eventFiles } from "@lexical/rich-text"; -import { mergeRegister } from "@lexical/utils"; -import { - $getNearestNodeFromDOMNode, - $getNodeByKey, - $getRoot, - COMMAND_PRIORITY_HIGH, - COMMAND_PRIORITY_LOW, - DRAGOVER_COMMAND, - DROP_COMMAND, -} from "lexical"; -import { createPortal } from "react-dom"; - -import { clsm } from "@acme/ui"; - -import { Icon } from "../../../icons"; -import { isHTMLElement } from "../../utils/guard"; -import { Point } from "../../utils/point"; -import { Rect } from "../../utils/rect"; - -const SPACE = 4; -const TARGET_LINE_HALF_HEIGHT = 2; -const DRAGGABLE_BLOCK_MENU_CLASSNAME = "draggable-block-menu"; -const DRAG_DATA_FORMAT = "application/x-lexical-drag-block"; -const TEXT_BOX_HORIZONTAL_PADDING = 28; - -const Downward = 1; -const Upward = -1; -const Indeterminate = 0; - -let prevIndex = Infinity; - -function getCurrentIndex(keysLength: number): number { - if (keysLength === 0) { - return Infinity; - } - if (prevIndex >= 0 && prevIndex < keysLength) { - return prevIndex; - } - - return Math.floor(keysLength / 2); -} - -function getTopLevelNodeKeys(editor: LexicalEditor): string[] { - return editor.getEditorState().read(() => $getRoot().getChildrenKeys()); -} - -function getCollapsedMargins(element: HTMLElement): { - marginTop: number; - marginBottom: number; -} { - const getMargin = ( - element: Element | null, - margin: "marginTop" | "marginBottom", - ): number => - element ? Number.parseFloat(window.getComputedStyle(element)[margin]) : 0; - - const { marginTop, marginBottom } = window.getComputedStyle(element); - const prevElementSiblingMarginBottom = getMargin( - element.previousElementSibling, - "marginBottom", - ); - const nextElementSiblingMarginTop = getMargin( - element.nextElementSibling, - "marginTop", - ); - const collapsedTopMargin = Math.max( - Number.parseFloat(marginTop), - prevElementSiblingMarginBottom, - ); - const collapsedBottomMargin = Math.max( - Number.parseFloat(marginBottom), - nextElementSiblingMarginTop, - ); - - return { marginBottom: collapsedBottomMargin, marginTop: collapsedTopMargin }; -} - -function getBlockElement( - anchorElement: HTMLElement, - editor: LexicalEditor, - event: MouseEvent, - useEdgeAsDefault = false, -): HTMLElement | null { - const anchorElementRect = anchorElement.getBoundingClientRect(); - const topLevelNodeKeys = getTopLevelNodeKeys(editor); - - let blockElement: HTMLElement | null = null; - editor.getEditorState().read(() => { - if (useEdgeAsDefault) { - const [firstNode, lastNode] = [ - editor.getElementByKey(topLevelNodeKeys[0]!), - editor.getElementByKey(topLevelNodeKeys.at(-1)!), - ]; - - const [firstNodeRect, lastNodeRect] = [ - firstNode?.getBoundingClientRect(), - lastNode?.getBoundingClientRect(), - ]; - - if (firstNodeRect && lastNodeRect) { - if (event.y < firstNodeRect.top) { - blockElement = firstNode; - } else if (event.y > lastNodeRect.bottom) { - blockElement = lastNode; - } - - if (blockElement) { - return; - } - } - } - - let index = getCurrentIndex(topLevelNodeKeys.length); - let direction = Indeterminate; - - while (index >= 0 && index < topLevelNodeKeys.length) { - const key = topLevelNodeKeys[index]; - const element = editor.getElementByKey(key!); - if (element === null) { - break; - } - const point = new Point(event.x, event.y); - const domRect = Rect.fromDOM(element); - const { marginTop, marginBottom } = getCollapsedMargins(element); - - const rect = domRect.generateNewRect({ - bottom: domRect.bottom + marginBottom, - left: anchorElementRect.left, - right: anchorElementRect.right, - top: domRect.top - marginTop, - }); - - const { - result, - reason: { isOnTopSide, isOnBottomSide }, - } = rect.contains(point); - - if (result) { - blockElement = element; - prevIndex = index; - break; - } - - if (direction === Indeterminate) { - if (isOnTopSide) { - direction = Upward; - } else if (isOnBottomSide) { - direction = Downward; - } else { - // stop search block element - direction = Infinity; - } - } - - index += direction; - } - }); - - return blockElement; -} - -function isOnMenu(element: HTMLElement): boolean { - return !!element.closest(`.${DRAGGABLE_BLOCK_MENU_CLASSNAME}`); -} - -function setMenuPosition( - targetElement: HTMLElement | null, - floatingElement: HTMLElement, - anchorElement: HTMLElement, -) { - if (!targetElement) { - floatingElement.style.opacity = "0"; - floatingElement.style.transform = "translate(-10000px, -10000px)"; - return; - } - - const targetRect = targetElement.getBoundingClientRect(); - const targetStyle = window.getComputedStyle(targetElement); - const floatingElementRect = floatingElement.getBoundingClientRect(); - const anchorElementRect = anchorElement.getBoundingClientRect(); - - const top = - targetRect.top + - (Number.parseInt(targetStyle.lineHeight, 10) - floatingElementRect.height) / 2 - - anchorElementRect.top; - - const left = SPACE; - - floatingElement.style.opacity = "1"; - floatingElement.style.transform = `translate(${left}px, ${top}px)`; -} - -function setDragImage( - dataTransfer: DataTransfer, - draggableBlockElement: HTMLElement, -) { - const { transform } = draggableBlockElement.style; - - // Remove dragImage borders - draggableBlockElement.style.transform = "translateZ(0)"; - dataTransfer.setDragImage(draggableBlockElement, 0, 0); - - setTimeout(() => { - draggableBlockElement.style.transform = transform; - }); -} - -function setTargetLine( - targetLineElement: HTMLElement, - targetBlockElement: HTMLElement, - mouseY: number, - anchorElement: HTMLElement, -) { - const { top: targetBlockElementTop, height: targetBlockElementHeight } = - targetBlockElement.getBoundingClientRect(); - const { top: anchorTop, width: anchorWidth } = - anchorElement.getBoundingClientRect(); - - const { marginTop, marginBottom } = getCollapsedMargins(targetBlockElement); - let lineTop = targetBlockElementTop; - if (mouseY >= targetBlockElementTop) { - lineTop += targetBlockElementHeight + marginBottom / 2; - } else { - lineTop -= marginTop / 2; - } - - const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT; - const left = TEXT_BOX_HORIZONTAL_PADDING - SPACE; - - targetLineElement.style.transform = `translate(${left}px, ${top}px)`; - targetLineElement.style.width = `${ - anchorWidth - (TEXT_BOX_HORIZONTAL_PADDING - SPACE) * 2 - }px`; - targetLineElement.style.opacity = ".4"; -} - -function hideTargetLine(targetLineElement: HTMLElement | null) { - if (targetLineElement) { - targetLineElement.style.opacity = "0"; - targetLineElement.style.transform = "translate(-10000px, -10000px)"; - } -} - -function useDraggableBlockMenu( - editor: LexicalEditor, - anchorElement: HTMLElement, - isEditable: boolean, -): JSX.Element { - const scrollerElement = anchorElement.parentElement; - - const menuRef = useRef(null); - const targetLineRef = useRef(null); - const isDraggingBlockRef = useRef(false); - const [draggableBlockElement, setDraggableBlockElement] = - useState(null); - - useEffect(() => { - function onMouseMove(event: MouseEvent) { - const target = event.target; - if (!isHTMLElement(target)) { - setDraggableBlockElement(null); - return; - } - - if (isOnMenu(target)) { - return; - } - - const _draggableBlockElement = getBlockElement(anchorElement, editor, event); - - setDraggableBlockElement(_draggableBlockElement); - } - - function onMouseLeave() { - setDraggableBlockElement(null); - } - - scrollerElement?.addEventListener("mousemove", onMouseMove); - scrollerElement?.addEventListener("mouseleave", onMouseLeave); - - return () => { - scrollerElement?.removeEventListener("mousemove", onMouseMove); - scrollerElement?.removeEventListener("mouseleave", onMouseLeave); - }; - }, [scrollerElement, anchorElement, editor]); - - useEffect(() => { - if (menuRef.current) { - setMenuPosition(draggableBlockElement, menuRef.current, anchorElement); - } - }, [anchorElement, draggableBlockElement]); - - useEffect(() => { - function onDragover(event: DragEvent): boolean { - if (!isDraggingBlockRef.current) { - return false; - } - const [isFileTransfer] = eventFiles(event); - if (isFileTransfer) { - return false; - } - const { pageY, target } = event; - if (!isHTMLElement(target)) { - return false; - } - const targetBlockElement = getBlockElement(anchorElement, editor, event, true); - const targetLineElement = targetLineRef.current; - if (targetBlockElement === null || targetLineElement === null) { - return false; - } - setTargetLine(targetLineElement, targetBlockElement, pageY, anchorElement); - // Prevent default event to be able to trigger onDrop events - event.preventDefault(); - return true; - } - - function onDrop(event: DragEvent): boolean { - if (!isDraggingBlockRef.current) { - return false; - } - const [isFileTransfer] = eventFiles(event); - if (isFileTransfer) { - return false; - } - const { target, dataTransfer, pageY } = event; - const dragData = dataTransfer?.getData(DRAG_DATA_FORMAT) ?? ""; - const draggedNode = $getNodeByKey(dragData); - if (!draggedNode) { - return false; - } - if (!isHTMLElement(target)) { - return false; - } - const targetBlockElement = getBlockElement(anchorElement, editor, event, true); - if (!targetBlockElement) { - return false; - } - const targetNode = $getNearestNodeFromDOMNode(targetBlockElement); - if (!targetNode) { - return false; - } - if (targetNode === draggedNode) { - return true; - } - const targetBlockElementTop = targetBlockElement.getBoundingClientRect().top; - if (pageY >= targetBlockElementTop) { - targetNode.insertAfter(draggedNode); - } else { - targetNode.insertBefore(draggedNode); - } - setDraggableBlockElement(null); - - return true; - } - - return mergeRegister( - editor.registerCommand( - DRAGOVER_COMMAND, - (event) => { - return onDragover(event); - }, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - DROP_COMMAND, - (event) => { - return onDrop(event); - }, - COMMAND_PRIORITY_HIGH, - ), - ); - }, [anchorElement, editor]); - - function onDragStart(event: ReactDragEvent): void { - const dataTransfer = event.dataTransfer; - if (!draggableBlockElement) { - return; - } - setDragImage(dataTransfer, draggableBlockElement); - let nodeKey = ""; - editor.update(() => { - const node = $getNearestNodeFromDOMNode(draggableBlockElement); - if (node) { - nodeKey = node.getKey(); - } - }); - isDraggingBlockRef.current = true; - dataTransfer.setData(DRAG_DATA_FORMAT, nodeKey); - } - - function onDragEnd(): void { - isDraggingBlockRef.current = false; - hideTargetLine(targetLineRef.current); - } - - return createPortal( - <> -
    - {/*
    */} - {isEditable && ( - - )} -
    -
    - , - anchorElement, - ); -} - -export const DraggableBlockPlugin = ({ - anchorElem, -}: { - anchorElem: HTMLElement | null; -}): JSX.Element => { - const [editor] = useLexicalComposerContext(); - return useDraggableBlockMenu( - editor, - anchorElem ?? document.body, - editor._editable, - ); -}; diff --git a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableElement.tsx b/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableElement.tsx deleted file mode 100644 index 989b6227..00000000 --- a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableElement.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import type { DragEvent as ReactDragEvent } from "react"; -import { memo, useCallback } from "react"; - -import { clsm } from "@acme/ui"; - -import { useDraggableStore } from "../../stores/useDraggableStore"; - -const DraggableElement = () => { - const { draggable, resetState } = useDraggableStore(); - - const handleOnDragStart = useCallback( - ({ dataTransfer }: ReactDragEvent) => { - if (!draggable?.htmlElement) { - return; - } - // THIS WILL SET THE DRAGGABLE IMAGE - dataTransfer.setDragImage(draggable.htmlElement, 0, 0); - }, - [draggable?.htmlElement], - ); - - if (!draggable?.data) { - return null; - } - - const scrollOffset = document.body.getBoundingClientRect().top; - - return ( -
    - ); -}; - -const Memoized = memo(DraggableElement, () => true); - -export { Memoized as DraggableElement }; diff --git a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableLine.tsx b/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableLine.tsx deleted file mode 100644 index ffdc54ff..00000000 --- a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableLine.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; - -import { useDraggableLineStore } from "../../stores/useDraggableStore"; - -const DraggableLine: React.FC = () => { - const { line } = useDraggableLineStore(); - - if (!line?.data) { - return null; - } - - const scrollOffset = document.body.getBoundingClientRect().top; - - return ( -
    - ); -}; - -const Memoized = React.memo(DraggableLine, () => true); - -export { Memoized as DraggableLine }; diff --git a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableWrapper.tsx b/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableWrapper.tsx deleted file mode 100644 index b69b6db7..00000000 --- a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/DraggableWrapper.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { PropsWithChildren } from "react"; -import React, { useEffect, useRef } from "react"; - -import { draggableStore } from "../../stores/useDraggableStore"; - -export const DRAGGABLE_WRAPPER_ID = "lexical-draggable-wrapper-id"; - -/** - * Reset state on mouse leave - */ -export const DraggableWrapper: React.FC = ({ children }) => { - const ref = useRef(null); - - /** - * NOTE:
    { - const callback = () => { - draggableStore.getState().resetState(); - }; - - const current = ref.current; - - current?.addEventListener("mouseleave", callback); - - return () => { - current?.removeEventListener("mouseleave", callback); - }; - }, []); - - return ( -
    - {children} -
    - ); -}; diff --git a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/index.tsx b/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/index.tsx deleted file mode 100644 index 68e153ad..00000000 --- a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { createPortal } from "react-dom"; - -import { useDragListeners } from "../../hooks/useDragListeners"; -import { useOnDrop } from "../../hooks/useOnDrop"; -import { DraggableElement } from "./DraggableElement"; -import { DraggableLine } from "./DraggableLine"; -import { DRAGGABLE_WRAPPER_ID } from "./DraggableWrapper"; - -export const DraggableBlockPlugin: React.FC = () => { - const [editor] = useLexicalComposerContext(); - - useDragListeners(); - useOnDrop(); - - const wrapperHtmlElement = document.getElementById(DRAGGABLE_WRAPPER_ID); - - const isEditable = editor.isEditable(); - if (!isEditable || !wrapperHtmlElement) { - return null; - } - - return createPortal( - <> - - - , - wrapperHtmlElement, - ); -}; diff --git a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/temp/Editor.tsx_ b/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/temp/Editor.tsx_ deleted file mode 100644 index 0f16e01b..00000000 --- a/@acme/ui/src/editor/plugins/DraggableBlockPlugin_nemrosim/temp/Editor.tsx_ +++ /dev/null @@ -1,99 +0,0 @@ -"use client"; - -import type { InitialConfigType } from "@lexical/react/LexicalComposer"; -import { useMemo } from "react"; -import { LexicalComposer } from "@lexical/react/LexicalComposer"; -import { ContentEditable } from "@lexical/react/LexicalContentEditable"; -import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary"; -import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin"; - -import { ImageNode } from "./nodes/ImageNode"; -import { DraggableBlockPlugin } from "./plugins/DraggableBlockPlugin_nemrosim"; -import { DraggableWrapper } from "./plugins/DraggableBlockPlugin_nemrosim/DraggableWrapper"; -import { HistoryPlugin } from "./plugins/history"; -import { OnChangePlugin } from "./plugins/on-change"; - -export const Editor = () => { - const CustomContentEditable = useMemo(() => { - return ( - -
    - -
    -
    - ); - }, []); - const CustomPlaceholder = useMemo(() => { - return ( -
    - Enter some text... -
    - ); - }, []); - - // const [floatingAnchorElem, setFloatingAnchorElem] = - // useState(null); - // - // const onRef = (_floatingAnchorElem: HTMLDivElement) => { - // if (_floatingAnchorElem !== null) { - // setFloatingAnchorElem(_floatingAnchorElem); - // } - // }; - - const initialConfig: InitialConfigType = { - namespace: "Editor", - nodes: [ImageNode], - editable: true, - theme: { - text: { - bold: "text-bold", - italic: "text-italic", - underline: "text-underline", - code: "text-code", - highlight: "text-highlight", - strikethrough: "text-strikethrough", - subscript: "text-subscript", - superscript: "text-superscript", - }, - heading: { - // Flowbite examples: https://flowbite.com/docs/typography/headings/#heading-one-h1 - h1: "text-5xl font-extrabold dark:text-white", - h2: "text-4xl font-bold dark:text-white", - h3: "text-3xl font-bold dark:text-white", - h4: "text-2xl font-bold dark:text-white", - h5: "text-xl font-bold dark:text-white", - }, - }, - onError: (error, editor) => { - console.log("error", error, editor); - }, - }; - - return ( -
    - - - - {/* */} - - {/* {floatingAnchorElem ? ( */} - {/* <> */} - {/* */} - {/* */} - {/* ) : ( */} - {/* "" */} - {/* )} */} - -
    - ); -}; diff --git a/@acme/ui/src/editor/plugins/ImagesPlugin/ImagesPlugin.tsx b/@acme/ui/src/editor/plugins/ImagesPlugin/ImagesPlugin.tsx deleted file mode 100644 index be125a54..00000000 --- a/@acme/ui/src/editor/plugins/ImagesPlugin/ImagesPlugin.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import type { LexicalEditor } from "lexical"; -import { useEffect } from "react"; -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; -import { $wrapNodeInElement, mergeRegister } from "@lexical/utils"; -import { - $createParagraphNode, - $createRangeSelection, - $getSelection, - $insertNodes, - $isNodeSelection, - $isRootOrShadowRoot, - $setSelection, - COMMAND_PRIORITY_EDITOR, - COMMAND_PRIORITY_HIGH, - COMMAND_PRIORITY_LOW, - DRAGOVER_COMMAND, - DRAGSTART_COMMAND, - DROP_COMMAND, -} from "lexical"; - -import type { InsertImagePayload } from "./types"; -// import { CAN_USE_DOM } from "../../constants"; -import { ImageNode } from "../../nodes/ImageNode"; -import { - $createImageNode, - $isImageNode, -} from "../../nodes/ImageNode/ImageNode"; -import { INSERT_IMAGE_COMMAND } from "./constants"; - -// const getDOMSelection = (targetWindow: Window | null): Selection | null => -// CAN_USE_DOM ? (targetWindow ?? window).getSelection() : null; - -export const ImagesPlugin = ({ - captionsEnabled, -}: { - captionsEnabled?: boolean; -}): JSX.Element | null => { - const [editor] = useLexicalComposerContext(); - - useEffect(() => { - if (!editor.hasNodes([ImageNode])) { - throw new Error("ImagesPlugin: ImageNode not registered on editor"); - } - - return mergeRegister( - editor.registerCommand( - INSERT_IMAGE_COMMAND, - (payload) => { - const imageNode = $createImageNode(payload); - $insertNodes([imageNode]); - if ($isRootOrShadowRoot(imageNode.getParentOrThrow())) { - $wrapNodeInElement(imageNode, $createParagraphNode).selectEnd(); - } - - return true; - }, - COMMAND_PRIORITY_EDITOR, - ), - editor.registerCommand( - DRAGSTART_COMMAND, - (event) => { - return onDragStart(event); - }, - COMMAND_PRIORITY_HIGH, - ), - editor.registerCommand( - DRAGOVER_COMMAND, - (event) => { - return onDragover(event); - }, - COMMAND_PRIORITY_LOW, - ), - editor.registerCommand( - DROP_COMMAND, - (event) => { - return onDrop(event, editor); - }, - COMMAND_PRIORITY_HIGH, - ), - ); - }, [captionsEnabled, editor]); - - return null; -}; - -const TRANSPARENT_IMAGE = - "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; - -function onDragStart(event: DragEvent): boolean { - const img = (typeof document !== "undefined" && - document.createElement("img")) as unknown as HTMLImageElement; - img.src = TRANSPARENT_IMAGE; - const node = getImageNodeInSelection(); - if (!node) { - return false; - } - const dataTransfer = event.dataTransfer; - if (!dataTransfer) { - return false; - } - dataTransfer.setData("text/plain", "_"); - dataTransfer.setDragImage(img, 0, 0); - dataTransfer.setData( - "application/x-lexical-drag", - JSON.stringify({ - data: { - altText: node.__altText, - caption: node.__caption, - height: node.__height, - key: node.getKey(), - maxWidth: node.__maxWidth, - showCaption: node.__showCaption, - src: node.__src, - width: node.__width, - }, - type: "image", - }), - ); - - return true; -} - -function onDragover(event: DragEvent): boolean { - const node = getImageNodeInSelection(); - if (!node) { - return false; - } - if (!canDropImage(event)) { - event.preventDefault(); - } - return true; -} - -function onDrop(event: DragEvent, editor: LexicalEditor): boolean { - const node = getImageNodeInSelection(); - if (!node) { - return false; - } - const data = getDragImageData(event); - if (!data) { - return false; - } - event.preventDefault(); - if (canDropImage(event)) { - const range = getDragSelection(event); - node.remove(); - const rangeSelection = $createRangeSelection(); - if (range !== null && range !== undefined) { - rangeSelection.applyDOMRange(range); - } - $setSelection(rangeSelection); - editor.dispatchCommand(INSERT_IMAGE_COMMAND, data); - } - return true; -} - -function getImageNodeInSelection(): ImageNode | null { - const selection = $getSelection(); - if (!$isNodeSelection(selection)) { - return null; - } - const nodes = selection.getNodes(); - const node = nodes[0]; - return $isImageNode(node) ? node : null; -} - -function getDragImageData(event: DragEvent): null | InsertImagePayload { - const dragData = event.dataTransfer?.getData("application/x-lexical-drag"); - if (!dragData) { - return null; - } - - const { type, data } = JSON.parse(dragData); - if (type !== "image") { - return null; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return data; -} - -declare global { - interface DragEvent { - rangeOffset?: number; - rangeParent?: Node; - } -} - -function canDropImage(event: DragEvent): boolean { - const target = event.target; - return !!( - target && - target instanceof HTMLElement && - !target.closest("code, span.editor-image") && - target.parentElement?.closest("div.ContentEditable__root") - ); -} - -function getDragSelection(event: DragEvent): Range | null | undefined { - // let range; - // const target = event.target as null | Element | Document; - // const targetWindow = - // target == null - // ? null - // : target.nodeType === 9 - // ? (target as Document).defaultView - // : (target as Element).ownerDocument.defaultView; - // const domSelection = getDOMSelection(targetWindow); - // if (document.caretRangeFromPoint) { - // range = document.caretRangeFromPoint(event.clientX, event.clientY); - // } else if (event.rangeParent && domSelection !== null) { - // domSelection.collapse(event.rangeParent, event.rangeOffset ?? 0); - // range = domSelection.getRangeAt(0); - // } else { - // throw Error(`Cannot get the selection when dragging`); - // } - - const range = document.caretRangeFromPoint(event.clientX, event.clientY); - return range; -} diff --git a/@acme/ui/src/editor/plugins/ImagesPlugin/InsertImage.tsx b/@acme/ui/src/editor/plugins/ImagesPlugin/InsertImage.tsx deleted file mode 100644 index 0b72bb61..00000000 --- a/@acme/ui/src/editor/plugins/ImagesPlugin/InsertImage.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import type { LexicalEditor } from "lexical"; -import { useEffect, useRef } from "react"; - -import type { InsertImagePayload } from "./types"; -import { Tabs } from "../../../tabs"; -import { INSERT_IMAGE_COMMAND } from "./constants"; -import { Uploader } from "./Uploader"; - -export const InsertImage = ({ - activeEditor, - onCancel, -}: { - activeEditor: LexicalEditor; - onCancel: () => void; -}): JSX.Element => { - const hasModifier = useRef(false); - - useEffect(() => { - hasModifier.current = false; - const handler = (e: KeyboardEvent) => { - hasModifier.current = e.altKey; - }; - document.addEventListener("keydown", handler); - return () => { - document.removeEventListener("keydown", handler); - }; - }, [activeEditor]); - - const onChoiceImage = (payload: InsertImagePayload) => { - activeEditor.dispatchCommand(INSERT_IMAGE_COMMAND, payload); - onCancel(); - }; - - return ( - <> - , - }, - { - key: "embed", - label: "Embed Link", - children: "Embed Link", - }, - { - key: "unsplash", - label: "Unsplash", - children: "Unsplash Image", - }, - ]} - // onChange={(activeKey) => setMode(activeKey} - /> - - ); -}; diff --git a/@acme/ui/src/editor/plugins/ImagesPlugin/Uploader.tsx b/@acme/ui/src/editor/plugins/ImagesPlugin/Uploader.tsx deleted file mode 100644 index 5b12aa8e..00000000 --- a/@acme/ui/src/editor/plugins/ImagesPlugin/Uploader.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { useState } from "react"; - -import { clsm } from "@acme/ui"; - -import type { InsertImagePayload } from "./types"; -import { message } from "../../../toast"; -import { useEditor } from "../../store"; - -type UploadProps = { - onUploadSuccess: (payload: InsertImagePayload) => void; -}; -export const Uploader = ({ onUploadSuccess }: UploadProps) => { - const { uploadService } = useEditor(); - const [dragActive, setDragActive] = useState(false); - - const upload = async (file: File) => { - if (file.size / 1024 / 1024 > 50) { - message.error("File size too big (max 50MB)"); - } else { - const blob = await uploadService({ - file: file, - fileName: file.name, - }); - onUploadSuccess({ - src: blob.url, - altText: blob.fileName, - }); - // const reader = new FileReader(); - // reader.onload = async () => { - // console.log( - // "aaaa", - // file, - // typeof reader.result, - // reader.result instanceof ArrayBuffer, - // reader.result, - // ); - // // if (reader.result instanceof ArrayBuffer) { - // if (typeof reader.result === "string") { - // const blob = await apiUpload({ - // file: reader.result, - // fileName: file.name, - // }); - // onUploadSuccess({ - // src: blob.url, - // altText: blob.fileName, - // }); - // } - // }; - // reader.readAsBinaryString(file); - // reader.readAsArrayBuffer(file); - // reader.readAsDataURL(file); - - // setFile(file); - // const reader = new FileReader(); - // reader.onload = (e) => { - // setData((prev) => ({ - // ...prev, - // image: e.target?.result as string, - // })); - // }; - // reader.readAsDataURL(file); - } - }; - return ( -
    -
    -