From dabc124b33d2e4328d7c7892d4077d9e31ad91bc Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Thu, 2 Apr 2026 20:54:46 +0600 Subject: [PATCH 01/20] feat: enhance Select component with additional stories and features - Added new stories for Select component including WithGroups, WithLabel, SmallSize, DisabledItems, Controlled, Disabled, and Scrollable. - Updated Select component to support grouped items and labels. - Introduced AsyncCombobox component for dynamic searching with simulated API calls. - Created Command component with various subcomponents for command palette functionality. - Added Command stories demonstrating usage of Command, CommandDialog, and CommandGroups. - Improved Popover styling in PopoverContent for better visual consistency. --- package-lock.json | 669 ++++++++++++++++++-- package.json | 1 + src/components/ui/AsyncCombobox.stories.tsx | 17 + src/components/ui/Combobox.stories.tsx | 595 +++++++++++------ src/components/ui/Command.stories.tsx | 286 +++++++++ src/components/ui/Select.stories.tsx | 212 ++++++- src/components/ui/async-combobox.tsx | 105 +++ src/components/ui/command.tsx | 181 ++++++ src/components/ui/index.ts | 16 + src/components/ui/popover.tsx | 2 +- 10 files changed, 1817 insertions(+), 267 deletions(-) create mode 100644 src/components/ui/AsyncCombobox.stories.tsx create mode 100644 src/components/ui/Command.stories.tsx create mode 100644 src/components/ui/async-combobox.tsx create mode 100644 src/components/ui/command.tsx diff --git a/package-lock.json b/package-lock.json index 9742b22..22485d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@wordpress/hooks": "^4.39.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "input-otp": "^1.4.2", "lucide-react": "^0.563.0", "recharts": "^2.15.4", @@ -159,7 +160,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2189,7 +2189,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2252,7 +2251,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -2293,7 +2291,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2383,7 +2380,6 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", - "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -2605,7 +2601,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -4049,7 +4044,6 @@ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -4073,7 +4067,6 @@ "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" }, @@ -4087,7 +4080,6 @@ "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, @@ -4570,7 +4562,6 @@ "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" @@ -4598,7 +4589,6 @@ "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", @@ -4627,7 +4617,6 @@ "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" } @@ -5138,6 +5127,447 @@ "node": ">=10" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -5793,7 +6223,6 @@ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -6210,6 +6639,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -6230,6 +6660,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -6240,6 +6671,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -6253,6 +6685,7 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -6263,6 +6696,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -6277,7 +6711,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", @@ -6363,7 +6798,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -6546,7 +6982,6 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -6751,7 +7186,6 @@ "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -6826,7 +7260,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -7039,7 +7472,6 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -7839,7 +8271,6 @@ "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.25.7", @@ -9075,7 +9506,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -9169,7 +9599,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -9371,6 +9800,18 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -10342,7 +10783,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -10975,6 +11415,22 @@ "node": ">=6" } }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -12218,6 +12674,7 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -12260,13 +12717,18 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devtools-protocol": { "version": "0.0.1507524", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1507524.tgz", "integrity": "sha512-OjaNE7qpk6GRTXtqQjAE5bGx6+c4F1zZH0YXtpZQLM92HNXx4zMAaqlKhP4T52DosG6hDW8gPMNhGOF8xbwk/w==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/diff-sequences": { "version": "29.6.3", @@ -12322,7 +12784,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dom-converter": { "version": "0.2.0", @@ -12876,7 +13339,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -12981,7 +13443,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -13038,7 +13499,6 @@ "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -13162,7 +13622,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -14912,6 +15371,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -16871,7 +17339,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -17972,8 +18439,7 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz", "integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/ws": { "version": "8.19.0", @@ -18513,6 +18979,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -19345,7 +19812,6 @@ "integrity": "sha512-cuXAJJB1Rdqz0UO6w524matlBqDBjcNt7Ru+RDIu4y6RI1gVqiWBnylrK8sPRk81gGBA0X8hJbDXolVOoTc+sA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^6.12.6", "ajv-errors": "^1.0.1", @@ -20137,6 +20603,7 @@ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright-core": "1.58.2" }, @@ -20156,6 +20623,7 @@ "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -20174,6 +20642,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -20224,7 +20693,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -21193,7 +21661,6 @@ "integrity": "sha512-X4UlrxDTH8oom9qXlcjnydsjAOD2BmB6yFmvS4Z2zdTzqqpRWb+fbqrH412+l+OUXmbzJlSXjlMFYPgYG12IAA==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -21621,7 +22088,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -21697,7 +22163,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -21718,11 +22183,57 @@ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -21738,6 +22249,28 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -21999,8 +22532,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", @@ -22547,7 +23079,6 @@ "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -22652,7 +23183,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -23514,7 +24044,6 @@ "integrity": "sha512-885uSIn8NQw2ZG7vy84K45lHCOSyz1DVsDV8pHiHQj3J0riCuWLNeO50lK9z98zE8kjhgTtxAAkMTy5nkmNRKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", @@ -23966,7 +24495,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.19", @@ -24296,7 +24824,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -24966,7 +25493,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -25248,7 +25774,6 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=10" }, @@ -25361,7 +25886,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -25514,7 +26038,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -25660,6 +26183,27 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-memo-one": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", @@ -25669,6 +26213,28 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -25891,7 +26457,6 @@ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -26000,7 +26565,6 @@ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -26086,7 +26650,6 @@ "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", diff --git a/package.json b/package.json index df149b7..56aa851 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "@wordpress/hooks": "^4.39.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "input-otp": "^1.4.2", "lucide-react": "^0.563.0", "recharts": "^2.15.4", diff --git a/src/components/ui/AsyncCombobox.stories.tsx b/src/components/ui/AsyncCombobox.stories.tsx new file mode 100644 index 0000000..ba9bbe9 --- /dev/null +++ b/src/components/ui/AsyncCombobox.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import AsyncCombobox from "./async-combobox"; + +const meta = { + title: "UI/AsyncCombobox", + component: AsyncCombobox, + parameters: { layout: "centered" }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => , +}; diff --git a/src/components/ui/Combobox.stories.tsx b/src/components/ui/Combobox.stories.tsx index ea880cb..d11a5bf 100644 --- a/src/components/ui/Combobox.stories.tsx +++ b/src/components/ui/Combobox.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react"; -import React, { useState } from "react"; +import { useState } from "react"; import { Combobox, ComboboxInput, @@ -14,10 +14,49 @@ import { ComboboxChip, ComboboxChipsInput, useComboboxAnchor, -} from "./combobox"; -import { CheckIcon, ChevronDownIcon } from "lucide-react"; + Button, + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + Popover, + PopoverContent, + PopoverTrigger, + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "./index"; +import { + CheckIcon, + ChevronsUpDownIcon, + MoreHorizontalIcon, +} from "lucide-react"; +import { cn } from "@/lib/utils"; + +const frameworks = [ + "Next.js", + "SvelteKit", + "Nuxt.js", + "Remix", + "Astro", +] as const; -const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"] as const; +const frameworkOptions = [ + { value: "next.js", label: "Next.js" }, + { value: "sveltekit", label: "SvelteKit" }, + { value: "nuxt.js", label: "Nuxt.js" }, + { value: "remix", label: "Remix" }, + { value: "astro", label: "Astro" }, +]; const meta = { title: "UI/Combobox", @@ -30,94 +69,107 @@ export default meta; type Story = StoryObj; -/** - * Basic searchable combobox with a list of items. - * Users can type to filter and select a single item. - */ +// ─── Base UI Combobox (Default) ──────────────────────── + +function ComboboxDefaultDemo() { + const [value, setValue] = useState(null); + + return ( + + + + + {frameworks.map((item) => ( + + {item} + + ))} + + No results found. + + + ); +} + export const Default: Story = { - render: function DefaultStory() { - const [value, setValue] = useState(null); - - return ( - - - - - {frameworks.map((item) => ( - {item} - ))} - - No results found. - - - ); - }, + render: () => , }; -/** - * Combobox with clear button to reset selection. - * Useful when users need to easily clear their selection. - */ +// ─── With Clear Button ──────────────────────── + +function ComboboxWithClearDemo() { + const [value, setValue] = useState(null); + + return ( + + + + + {frameworks.map((item) => ( + + {item} + + ))} + + No results found. + + + ); +} + export const WithClearButton: Story = { - render: function WithClearButtonStory() { - const [value, setValue] = useState(null); - - return ( - - - - - {frameworks.map((item) => ( - {item} - ))} - - No results found. - - - ); - }, + render: () => , }; -const groupedItems = ["apple", "banana", "orange", "carrot", "broccoli", "spinach"]; +// ─── With Groups ──────────────────────── + +const groupedItems = [ + "apple", + "banana", + "orange", + "carrot", + "broccoli", + "spinach", +]; + +function ComboboxWithGroupsDemo() { + const [value, setValue] = useState(null); + + return ( + + + + + + Fruits + Apple + Banana + Orange + + + + Vegetables + Carrot + Broccoli + Spinach + + + No results found. + + + ); +} -/** - * Combobox with items organized into labeled groups with separators. - * Useful for categorizing options in larger lists. - */ export const WithGroups: Story = { - render: function WithGroupsStory() { - const [value, setValue] = useState(null); - - return ( - - - - - - Fruits - Apple - Banana - Orange - - - - Vegetables - Carrot - Broccoli - Spinach - - - No results found. - - - ); - }, + render: () => , }; +// ─── Multi Select ──────────────────────── + type MultiSelectItem = { value: string; label: string }; const multiSelectItems: MultiSelectItem[] = [ @@ -129,148 +181,271 @@ const multiSelectItems: MultiSelectItem[] = [ { value: "preact", label: "Preact" }, ]; -/** - * Multi-select combobox with tag chips and optional search. - * Selected items are displayed as removable tags. Supports controlled - * and uncontrolled modes, max tag count, and searchable filtering. - */ -export const MultiSelect: Story = { - render: function MultiSelectStory() { - const [selectedValues, setSelectedValues] = useState(["react", "vue"]); - const anchorRef = useComboboxAnchor(); - - const selectedItems = multiSelectItems.filter((item) => - selectedValues.includes(item.value) - ); - - const handleValueChange = ( - value: MultiSelectItem[] | MultiSelectItem | null - ) => { - const next = Array.isArray(value) ? value : value ? [value] : []; - setSelectedValues(next.map((i) => i.value)); - }; - - return ( - item.label} - itemToStringValue={(item) => item.value} - > - - {selectedItems.map((item) => ( - +function ComboboxMultiSelectDemo() { + const [selectedValues, setSelectedValues] = useState([ + "react", + "vue", + ]); + const anchorRef = useComboboxAnchor(); + + const selectedItems = multiSelectItems.filter((item) => + selectedValues.includes(item.value) + ); + + const handleValueChange = ( + value: MultiSelectItem[] | MultiSelectItem | null + ) => { + const next = Array.isArray(value) ? value : value ? [value] : []; + setSelectedValues(next.map((i) => i.value)); + }; + + return ( + item.label} + itemToStringValue={(item) => item.value} + > + + {selectedItems.map((item) => ( + {item.label} + ))} + + + + + {multiSelectItems.map((item) => ( + {item.label} - + ))} - - - - - {multiSelectItems.map((item) => ( - - {item.label} - - ))} - - No frameworks found. - - - ); - }, + + No frameworks found. + + + ); +} + +export const MultiSelect: Story = { + render: () => , }; -/** - * Multi-select with trigger button showing selected tags as pills. - * Alternative presentation for multi-select where selections are - * displayed in a compact trigger button with overflow indicator. - */ -export const MultiSelectWithTrigger: Story = { - render: function MultiSelectTriggerStory() { - const [selectedValues, setSelectedValues] = useState(["react"]); - - const selectedItems = multiSelectItems.filter((item) => - selectedValues.includes(item.value) - ); - - const handleValueChange = ( - value: MultiSelectItem[] | MultiSelectItem | null - ) => { - const next = Array.isArray(value) ? value : value ? [value] : []; - setSelectedValues(next.map((i) => i.value)); - }; - - const maxTagCount = 2; - const visibleTags = selectedItems.slice(0, maxTagCount); - const remainingCount = Math.max(selectedItems.length - maxTagCount, 0); - - return ( - item.label} - itemToStringValue={(item) => item.value} +// ─── Command + Popover Pattern (ShadCN Combobox Demo) ──────────────────────── + +function ComboboxCommandDemo() { + const [open, setOpen] = useState(false); + const [value, setValue] = useState(""); + + return ( + + + } > - - - - - {multiSelectItems.map((item) => ( - - - {selectedValues.includes(item.value) && ( - - )} - - {item.label} - - ))} - - No frameworks found. - - - ); - }, + + + + Actions + + Assign to... + Set due date... + + + Apply label + + + + + No label found. + + {labels.map((l) => ( + { + setLabel(val); + setOpen(false); + }} + > + {l} + + ))} + + + + + + + + Delete + + + + + + ); +} + +export const DropdownMenuWithCommand: Story = { + render: () => , }; -/** - * Disabled combobox state. Input is not interactive. - */ +// ─── Disabled ──────────────────────── + export const Disabled: Story = { render: () => ( - + {frameworks.map((item) => ( - {item} + + {item} + ))} diff --git a/src/components/ui/Command.stories.tsx b/src/components/ui/Command.stories.tsx new file mode 100644 index 0000000..d8e0010 --- /dev/null +++ b/src/components/ui/Command.stories.tsx @@ -0,0 +1,286 @@ +import { useState, useEffect } from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { + Command, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, + CommandShortcut, + Button, +} from "./index"; +import { + CalendarIcon, + SmileIcon, + CalculatorIcon, + UserIcon, + CreditCardIcon, + SettingsIcon, +} from "lucide-react"; + +function CommandDemo() { + return ( + + + + No results found. + + + + Calendar + + + + Search Emoji + + + + Calculator + + + + + + + Profile + ⌘P + + + + Billing + ⌘B + + + + Settings + ⌘S + + + + + ); +} + +function CommandDialogDemo() { + const [open, setOpen] = useState(false); + + useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === "j" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setOpen((prev) => !prev); + } + }; + document.addEventListener("keydown", down); + return () => document.removeEventListener("keydown", down); + }, []); + + return ( + <> +

+ Press{" "} + + J + {" "} + or click the button below. +

+ + + + + No results found. + + + + Calendar + + + + Search Emoji + + + + Calculator + + + + + + + Profile + ⌘P + + + + Billing + ⌘B + + + + Settings + ⌘S + + + + + + ); +} + +function CommandShortcutsDemo() { + return ( + + + + No results found. + + + New File + ⌘N + + + Open File + ⌘O + + + Save + ⌘S + + + Save As... + ⇧⌘S + + + + + + Undo + ⌘Z + + + Redo + ⇧⌘Z + + + Find + ⌘F + + + Replace + ⌘H + + + + + ); +} + +function CommandGroupsDemo() { + return ( + + + + No results found. + + + + John Doe + + + + Jane Smith + + + + Bob Wilson + + + + + + + Schedule Meeting + + + + Make Payment + + + + Open Settings + + + + + + + Calculator + + + + Emoji Picker + + + + + ); +} + +function CommandScrollableDemo() { + const items = Array.from({ length: 30 }, (_, i) => ({ + label: `Item ${i + 1}`, + value: `item-${i + 1}`, + })); + + return ( + + + + No results found. + + {items.map((item) => ( + + {item.label} + + ))} + + + + ); +} + +const meta = { + title: "UI/Command", + component: Command, + parameters: { layout: "centered" }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => , +}; + +export const DialogVariant: Story = { + render: () => , +}; + +export const Shortcuts: Story = { + render: () => , +}; + +export const Groups: Story = { + render: () => , +}; + +export const Scrollable: Story = { + render: () => , +}; diff --git a/src/components/ui/Select.stories.tsx b/src/components/ui/Select.stories.tsx index 4281eb5..c003b9c 100644 --- a/src/components/ui/Select.stories.tsx +++ b/src/components/ui/Select.stories.tsx @@ -1,11 +1,17 @@ import type { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; import { Select, SelectContent, + SelectGroup, SelectItem, + SelectLabel, + SelectSeparator, SelectTrigger, SelectValue, -} from "./select"; + Label, + Button, +} from "./index"; const meta = { title: "UI/Select", @@ -18,16 +24,216 @@ export default meta; type Story = StoryObj; +// ─── Default ──────────────────────── + export const Default: Story = { render: () => ( + ), +}; + +// ─── With Groups ──────────────────────── + +export const WithGroups: Story = { + render: () => ( + + ), +}; + +// ─── With Label ──────────────────────── + +export const WithLabel: Story = { + render: () => ( +
+ + +
+ ), +}; + +// ─── Small Size ──────────────────────── + +export const SmallSize: Story = { + render: () => ( + + ), +}; + +// ─── Disabled Items ──────────────────────── + +export const DisabledItems: Story = { + render: () => ( + + ), +}; + +// ─── Controlled ──────────────────────── + +function SelectControlledDemo() { + const [value, setValue] = useState("banana"); + + return ( +
+ +

+ Selected: {value} +

+ +
+ ); +} + +export const Controlled: Story = { + render: () => , +}; + +// ─── Disabled ──────────────────────── + +export const Disabled: Story = { + render: () => ( + + ), +}; + +// ─── Scrollable (many items) ──────────────────────── + +export const Scrollable: Story = { + render: () => ( + ), diff --git a/src/components/ui/async-combobox.tsx b/src/components/ui/async-combobox.tsx new file mode 100644 index 0000000..7ec4746 --- /dev/null +++ b/src/components/ui/async-combobox.tsx @@ -0,0 +1,105 @@ +"use client" + +import { Check, ChevronsUpDown, Loader2 } from "lucide-react" +import { useEffect, useState } from "react" +import { Button } from "@/components/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command" +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" +import { cn } from "@/lib/utils" + +const AsyncCombobox = () => { + const [open, setOpen] = useState(false) + const [value, setValue] = useState("") + const [search, setSearch] = useState("") + const [isSearching, setIsSearching] = useState(false) + const [results, setResults] = useState([]) + + useEffect(() => { + if (!search) { + setResults([]) + return + } + + setIsSearching(true) + const timer = setTimeout(() => { + // Simulate API call + setResults([`${search} - Result 1`, `${search} - Result 2`, `${search} - Result 3`]) + setIsSearching(false) + }, 500) + + return () => clearTimeout(timer) + }, [search]) + + return ( + + + } + > + {value || "Search dynamically..."} + + + + + + + {isSearching ? ( +
+ + Searching... +
+ ) : ( + <> + {!search && ( +
+ Start typing to search +
+ )} + {search && results.length === 0 && !isSearching && ( + No results found. + )} + {results.length > 0 && ( + + {results.map(result => ( + { + setValue(currentValue === value ? "" : currentValue) + setOpen(false) + }} + value={result} + > + + {result} + + ))} + + )} + + )} +
+
+
+
+ ) +} + +export default AsyncCombobox diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx new file mode 100644 index 0000000..4544808 --- /dev/null +++ b/src/components/ui/command.tsx @@ -0,0 +1,181 @@ +"use client" + +import * as React from "react" +import { Command as CommandPrimitive } from "cmdk" +import { SearchIcon } from "lucide-react" + +import { cn } from "@/lib/utils" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" + +function Command({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +function CommandDialog({ + title = "Command Palette", + description = "Search for a command to run...", + children, + showCloseButton = true, + ...props +}: Omit, "children"> & { + title?: string + description?: string + showCloseButton?: boolean + children?: React.ReactNode +}) { + return ( + + + + {title} + {description} + + + {children} + + + + ) +} + +function CommandInput({ + className, + ...props +}: React.ComponentProps) { + return ( +
+ + +
+ ) +} + +function CommandList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandEmpty({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +export { + Command, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, + CommandShortcut, +} diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index 722eb83..e93ef40 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -96,6 +96,22 @@ export { SelectValue, } from "./select"; +// AsyncCombobox component +export { default as AsyncCombobox } from "./async-combobox"; + +// Command component (cmdk) +export { + Command, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, + CommandShortcut, +} from "./command"; + // Combobox component export { CombineInput, type CombineInputProps } from "./combine-input"; export { diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index e1f107f..cedaace 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -43,7 +43,7 @@ function PopoverContent({ Date: Thu, 2 Apr 2026 21:20:01 +0600 Subject: [PATCH 02/20] feat: update ComboboxDropdownMenuDemo to improve dropdown menu structure and accessibility --- src/components/ui/Combobox.stories.tsx | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/ui/Combobox.stories.tsx b/src/components/ui/Combobox.stories.tsx index d11a5bf..9334100 100644 --- a/src/components/ui/Combobox.stories.tsx +++ b/src/components/ui/Combobox.stories.tsx @@ -385,14 +385,17 @@ function ComboboxDropdownMenuDemo() { - Actions + Actions Assign to... Set due date... - - - Apply label - + + + + Apply label + + {/* Stop keyboard event propagation to prevent base-ui Menu from intercepting input */} +
e.stopPropagation()}> - - - - - Delete - - +
+
+
+ + + Delete +
From 44af39682bc4ac59d5e008e97d1e5e68aae87db9 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Thu, 2 Apr 2026 21:47:22 +0600 Subject: [PATCH 03/20] feat: enhance Combobox component with dropdown menu and dialog for label selection --- src/components/ui/Combobox.stories.tsx | 125 ++++++++++++------------- src/components/ui/combobox.tsx | 14 +-- 2 files changed, 70 insertions(+), 69 deletions(-) diff --git a/src/components/ui/Combobox.stories.tsx b/src/components/ui/Combobox.stories.tsx index 9334100..3f5e551 100644 --- a/src/components/ui/Combobox.stories.tsx +++ b/src/components/ui/Combobox.stories.tsx @@ -30,15 +30,14 @@ import { DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, DropdownMenuTrigger, + CommandDialog, } from "./index"; import { CheckIcon, ChevronsUpDownIcon, MoreHorizontalIcon, + TagIcon, } from "lucide-react"; import { cn } from "@/lib/utils"; @@ -368,68 +367,68 @@ const labels = [ function ComboboxDropdownMenuDemo() { const [label, setLabel] = useState("feature"); - const [open, setOpen] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + const [labelDialogOpen, setLabelDialogOpen] = useState(false); return ( -
-

- - {label} - - Create a new project -

- - } - > - - - - - Actions - Assign to... - Set due date... - - - - Apply label - - {/* Stop keyboard event propagation to prevent base-ui Menu from intercepting input */} -
e.stopPropagation()}> - - - - No label found. - - {labels.map((l) => ( - { - setLabel(val); - setOpen(false); - }} - > - {l} - - ))} - - - -
-
-
- - - Delete - -
-
-
+ <> +
+

+ + {label} + + Create a new project +

+ + } + > + + + + + Actions + Assign to... + Set due date... + + + { + setMenuOpen(false); + setTimeout(() => setLabelDialogOpen(true), 100); + }} + > + + Apply label + + + + Delete + + + +
+ + + + No label found. + + {labels.map((l) => ( + { + setLabel(val); + setLabelDialogOpen(false); + }} + > + {l} + + ))} + + + + ); } diff --git a/src/components/ui/combobox.tsx b/src/components/ui/combobox.tsx index b3b72e7..b505ddd 100644 --- a/src/components/ui/combobox.tsx +++ b/src/components/ui/combobox.tsx @@ -237,13 +237,14 @@ function ComboboxSeparator({ ); } -function ComboboxChips({ - className, - ...props -}: React.ComponentPropsWithRef & - ComboboxPrimitive.Chips.Props) { +const ComboboxChips = React.forwardRef< + HTMLDivElement, + React.ComponentPropsWithRef & + ComboboxPrimitive.Chips.Props +>(({ className, ...props }, ref) => { return ( ); -} +}); +ComboboxChips.displayName = "ComboboxChips"; function ComboboxChip({ className, From 224541683d2b047246505e1ebcbb849405a12c2d Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Thu, 2 Apr 2026 22:16:32 +0600 Subject: [PATCH 04/20] feat: enhance ComboboxInput component with aria-invalid styles and trigger button handling --- src/components/ui/Combobox.stories.tsx | 223 +++++++++++++++++++++++++ src/components/ui/combobox.tsx | 6 +- 2 files changed, 226 insertions(+), 3 deletions(-) diff --git a/src/components/ui/Combobox.stories.tsx b/src/components/ui/Combobox.stories.tsx index 3f5e551..6e49db9 100644 --- a/src/components/ui/Combobox.stories.tsx +++ b/src/components/ui/Combobox.stories.tsx @@ -13,8 +13,11 @@ import { ComboboxChips, ComboboxChip, ComboboxChipsInput, + ComboboxTrigger, + ComboboxValue, useComboboxAnchor, Button, + InputGroupAddon, Command, CommandEmpty, CommandGroup, @@ -36,6 +39,7 @@ import { import { CheckIcon, ChevronsUpDownIcon, + GlobeIcon, MoreHorizontalIcon, TagIcon, } from "lucide-react"; @@ -436,6 +440,87 @@ export const DropdownMenuWithCommand: Story = { render: () => , }; +// ─── Custom Items ──────────────────────── + +type Framework = { + value: string; + label: string; + description: string; +}; + +const customFrameworks: Framework[] = [ + { value: "next.js", label: "Next.js", description: "The React Framework" }, + { value: "sveltekit", label: "SvelteKit", description: "Web development, streamlined" }, + { value: "nuxt.js", label: "Nuxt.js", description: "The Intuitive Vue Framework" }, + { value: "remix", label: "Remix", description: "Build better websites" }, + { value: "astro", label: "Astro", description: "The web framework for content" }, +]; + +function ComboboxCustomItemsDemo() { + const [value, setValue] = useState(null); + + return ( + item.label} + itemToStringValue={(item) => item.value} + > + + + + {customFrameworks.map((item) => ( + +
+ {item.label} + + {item.description} + +
+
+ ))} +
+ No frameworks found. +
+
+ ); +} + +export const CustomItems: Story = { + render: () => , +}; + +// ─── Invalid ──────────────────────── + +function ComboboxInvalidDemo() { + const [value, setValue] = useState(null); + + return ( + + + + + {frameworks.map((item) => ( + + {item} + + ))} + + No results found. + + + ); +} + +export const Invalid: Story = { + render: () => , +}; + // ─── Disabled ──────────────────────── export const Disabled: Story = { @@ -454,3 +539,141 @@ export const Disabled: Story = {
), }; + +// ─── Auto Highlight ──────────────────────── + +function ComboboxAutoHighlightDemo() { + const [value, setValue] = useState(null); + + return ( + + + + + {frameworks.map((item) => ( + + {item} + + ))} + + No results found. + + + ); +} + +export const AutoHighlight: Story = { + render: () => , +}; + +// ─── Popup (Trigger Button) ──────────────────────── + +function ComboboxPopupDemo() { + const [value, setValue] = useState(null); + + return ( + + + } + > + + + + + + + {frameworks.map((item) => ( + + {item} + + ))} + + No results found. + + + ); +} + +export const Popup: Story = { + render: () => , +}; + +// ─── Input Group (with addon) ──────────────────────── + +function ComboboxInputGroupDemo() { + const [value, setValue] = useState(null); + + return ( + + + + + + + + + {frameworks.map((item) => ( + + {item} + + ))} + + No results found. + + + ); +} + +export const InputGroup: Story = { + render: () => , +}; + +// ─── RTL ──────────────────────── + +const arabicItems = [ + "Next.js - إطار عمل ريأكت", + "SvelteKit - إطار عمل سفيلت", + "Nuxt.js - إطار عمل فيو", + "Remix - إطار عمل ريأكت", + "Astro - إطار عمل المحتوى", +]; + +function ComboboxRtlDemo() { + const [value, setValue] = useState(null); + + return ( +
+ + + + + {arabicItems.map((item) => ( + + {item} + + ))} + + لا توجد نتائج. + + +
+ ); +} + +export const RTL: Story = { + render: () => , +}; diff --git a/src/components/ui/combobox.tsx b/src/components/ui/combobox.tsx index b505ddd..7d86eca 100644 --- a/src/components/ui/combobox.tsx +++ b/src/components/ui/combobox.tsx @@ -79,7 +79,7 @@ const ComboboxInput = React.forwardRef<
} {...props} /> - {showTrigger && } + {showTrigger && } {showClear && } {children}
From f2bc6b1775daa5530990a19703819cbafe40562b Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Thu, 2 Apr 2026 22:29:39 +0600 Subject: [PATCH 05/20] feat: enhance AsyncCombobox component with improved search functionality and option grouping --- src/components/ui/AsyncCombobox.stories.tsx | 354 +++++++++++++++++++- src/components/ui/async-combobox.tsx | 325 ++++++++++++++---- src/components/ui/index.ts | 6 +- 3 files changed, 614 insertions(+), 71 deletions(-) diff --git a/src/components/ui/AsyncCombobox.stories.tsx b/src/components/ui/AsyncCombobox.stories.tsx index ba9bbe9..8fc5423 100644 --- a/src/components/ui/AsyncCombobox.stories.tsx +++ b/src/components/ui/AsyncCombobox.stories.tsx @@ -1,5 +1,51 @@ +import { useState, useCallback } from "react"; import type { Meta, StoryObj } from "@storybook/react"; -import AsyncCombobox from "./async-combobox"; +import { + AsyncCombobox, + type AsyncComboboxOption, +} from "./async-combobox"; + +const allFrameworks: AsyncComboboxOption[] = [ + { value: "next.js", label: "Next.js" }, + { value: "remix", label: "Remix" }, + { value: "astro", label: "Astro" }, + { value: "nuxt", label: "Nuxt" }, + { value: "sveltekit", label: "SvelteKit" }, + { value: "gatsby", label: "Gatsby" }, + { value: "angular", label: "Angular" }, + { value: "vue", label: "Vue" }, + { value: "react", label: "React" }, + { value: "solid-start", label: "SolidStart" }, +]; + +function useAsyncSearch( + data: AsyncComboboxOption[], + delay = 500 +) { + const [options, setOptions] = useState([]); + const [loading, setLoading] = useState(false); + + const handleSearch = useCallback( + (query: string) => { + if (!query) { + setOptions([]); + return; + } + setLoading(true); + setTimeout(() => { + setOptions( + data.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + setLoading(false); + }, delay); + }, + [data, delay] + ); + + return { options, loading, handleSearch }; +} const meta = { title: "UI/AsyncCombobox", @@ -12,6 +58,308 @@ export default meta; type Story = StoryObj; -export const Default: Story = { - render: () => , +// ─── Basic ──────────────────────── + +function AsyncComboboxBasicDemo() { + const [value, setValue] = useState(""); + const { options, loading, handleSearch } = useAsyncSearch(allFrameworks); + + return ( + + ); +} + +export const Basic: Story = { + render: () => , +}; + +// ─── Clear Button ──────────────────────── + +function AsyncComboboxClearDemo() { + const [value, setValue] = useState(""); + const { options, loading, handleSearch } = useAsyncSearch(allFrameworks); + + return ( + + ); +} + +export const ClearButton: Story = { + render: () => , +}; + +// ─── Custom Items (with description) ──────────────────────── + +const frameworksWithDesc: AsyncComboboxOption[] = [ + { value: "next.js", label: "Next.js", description: "The React Framework" }, + { value: "remix", label: "Remix", description: "Build better websites" }, + { value: "astro", label: "Astro", description: "The web framework for content" }, + { value: "nuxt", label: "Nuxt", description: "The Intuitive Vue Framework" }, + { value: "sveltekit", label: "SvelteKit", description: "Web development, streamlined" }, +]; + +function AsyncComboboxCustomItemsDemo() { + const [value, setValue] = useState(""); + const { options, loading, handleSearch } = useAsyncSearch(frameworksWithDesc); + + return ( + + ); +} + +export const CustomItems: Story = { + render: () => , +}; + +// ─── Custom Render ──────────────────────── + +function AsyncComboboxCustomRenderDemo() { + const [value, setValue] = useState(""); + const { options, loading, handleSearch } = useAsyncSearch(frameworksWithDesc); + + return ( + ( +
+
+ + {option.label.charAt(0)} + +
+ {option.label} + {option.description && ( + + {option.description} + + )} +
+
+ {isSelected && ( + Selected + )} +
+ )} + /> + ); +} + +export const CustomRender: Story = { + render: () => , +}; + +// ─── Groups ──────────────────────── + +const groupedFrameworks: AsyncComboboxOption[] = [ + { value: "next.js", label: "Next.js", group: "React" }, + { value: "remix", label: "Remix", group: "React" }, + { value: "gatsby", label: "Gatsby", group: "React" }, + { value: "nuxt", label: "Nuxt", group: "Vue" }, + { value: "sveltekit", label: "SvelteKit", group: "Svelte" }, + { value: "astro", label: "Astro", group: "Multi-framework" }, + { value: "angular", label: "Angular", group: "Standalone" }, +]; + +function AsyncComboboxGroupsDemo() { + const [value, setValue] = useState(""); + const { options, loading, handleSearch } = useAsyncSearch(groupedFrameworks); + + return ( + + ); +} + +export const Groups: Story = { + render: () => , +}; + +// ─── Custom Messages ──────────────────────── + +function AsyncComboboxCustomMessagesDemo() { + const [value, setValue] = useState(""); + const { options, loading, handleSearch } = useAsyncSearch(allFrameworks, 800); + + return ( + + ); +} + +export const CustomMessages: Story = { + render: () => , +}; + +// ─── User Search (with disabled items) ──────────────────────── + +function AsyncComboboxUsersDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState([]); + const [loading, setLoading] = useState(false); + + const handleSearch = useCallback((query: string) => { + if (!query) { + setOptions([]); + return; + } + setLoading(true); + setTimeout(() => { + setOptions([ + { value: "user-1", label: `${query} Smith` }, + { value: "user-2", label: `${query} Johnson` }, + { value: "user-3", label: `${query} Williams` }, + { + value: "user-4", + label: `${query} Brown (inactive)`, + disabled: true, + }, + ]); + setLoading(false); + }, 600); + }, []); + + return ( + + ); +} + +export const UserSearch: Story = { + render: () => , +}; + +// ─── Invalid ──────────────────────── + +function AsyncComboboxInvalidDemo() { + const [value, setValue] = useState(""); + const { options, loading, handleSearch } = useAsyncSearch(allFrameworks); + + return ( + + ); +} + +export const Invalid: Story = { + render: () => , +}; + +// ─── Disabled ──────────────────────── + +export const Disabled: Story = { + render: () => ( + {}} + options={[]} + disabled + placeholder="Disabled combobox" + /> + ), +}; + +// ─── Preselected ──────────────────────── + +function AsyncComboboxPreselectedDemo() { + const [value, setValue] = useState("react"); + const [options, setOptions] = useState(allFrameworks); + const [loading, setLoading] = useState(false); + + const handleSearch = useCallback((query: string) => { + if (!query) { + setOptions(allFrameworks); + return; + } + setLoading(true); + setTimeout(() => { + setOptions( + allFrameworks.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + setLoading(false); + }, 300); + }, []); + + return ( + + ); +} + +export const Preselected: Story = { + render: () => , }; diff --git a/src/components/ui/async-combobox.tsx b/src/components/ui/async-combobox.tsx index 7ec4746..db7847a 100644 --- a/src/components/ui/async-combobox.tsx +++ b/src/components/ui/async-combobox.tsx @@ -1,7 +1,7 @@ "use client" -import { Check, ChevronsUpDown, Loader2 } from "lucide-react" -import { useEffect, useState } from "react" +import * as React from "react" +import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, XIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Command, @@ -10,90 +10,280 @@ import { CommandInput, CommandItem, CommandList, + CommandSeparator, } from "@/components/ui/command" -import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" import { cn } from "@/lib/utils" -const AsyncCombobox = () => { - const [open, setOpen] = useState(false) - const [value, setValue] = useState("") - const [search, setSearch] = useState("") - const [isSearching, setIsSearching] = useState(false) - const [results, setResults] = useState([]) - - useEffect(() => { - if (!search) { - setResults([]) - return +export interface AsyncComboboxOption { + /** Unique value for the option */ + value: string + /** Display label */ + label: string + /** Optional description shown below label */ + description?: string + /** Whether the option is disabled */ + disabled?: boolean + /** Optional group name for grouping options */ + group?: string +} + +export interface AsyncComboboxProps { + /** Async callback triggered on search input change (after debounce) */ + onSearch: (query: string) => void | Promise + /** Options to display in the dropdown */ + options: AsyncComboboxOption[] + /** Currently selected value */ + value?: string + /** Callback when selection changes */ + onValueChange?: (value: string) => void + /** Whether results are currently loading */ + loading?: boolean + /** Placeholder text for the trigger button */ + placeholder?: string + /** Placeholder text for the search input */ + searchPlaceholder?: string + /** Message shown when no results are found */ + emptyMessage?: string + /** Message shown before the user starts typing */ + idleMessage?: string + /** Whether to show a clear button when a value is selected */ + showClear?: boolean + /** Whether the combobox is disabled */ + disabled?: boolean + /** Mark combobox as invalid */ + invalid?: boolean + /** Debounce delay in milliseconds @default 300 */ + debounceMs?: number + /** Additional class for the trigger button */ + className?: string + /** Width class for the popover content @default "w-[250px]" */ + contentClassName?: string + /** Custom render function for each option */ + renderOption?: (option: AsyncComboboxOption, isSelected: boolean) => React.ReactNode +} + +function AsyncCombobox({ + onSearch, + options, + value = "", + onValueChange, + loading = false, + placeholder = "Select an option...", + searchPlaceholder = "Type to search...", + emptyMessage = "No results found.", + idleMessage = "Start typing to search", + showClear = false, + disabled = false, + invalid = false, + debounceMs = 300, + className, + contentClassName, + renderOption, +}: AsyncComboboxProps) { + const [open, setOpen] = React.useState(false) + const [search, setSearch] = React.useState("") + const timerRef = React.useRef | null>(null) + + const selectedOption = React.useMemo( + () => options.find((opt) => opt.value === value), + [options, value] + ) + + const handleSearchChange = React.useCallback( + (query: string) => { + setSearch(query) + + if (timerRef.current) { + clearTimeout(timerRef.current) + } + + timerRef.current = setTimeout(() => { + onSearch(query) + }, debounceMs) + }, + [onSearch, debounceMs] + ) + + React.useEffect(() => { + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current) + } + } + }, []) + + React.useEffect(() => { + if (!open) { + setSearch("") } + }, [open]) + + // Group options by group name + const groupedOptions = React.useMemo(() => { + const groups = new Map() + const ungrouped: AsyncComboboxOption[] = [] - setIsSearching(true) - const timer = setTimeout(() => { - // Simulate API call - setResults([`${search} - Result 1`, `${search} - Result 2`, `${search} - Result 3`]) - setIsSearching(false) - }, 500) + for (const opt of options) { + if (opt.group) { + const group = groups.get(opt.group) || [] + group.push(opt) + groups.set(opt.group, group) + } else { + ungrouped.push(opt) + } + } - return () => clearTimeout(timer) - }, [search]) + return { groups, ungrouped } + }, [options]) + + const handleClear = React.useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + onValueChange?.("") + }, + [onValueChange] + ) + + const renderItems = (items: AsyncComboboxOption[]) => + items.map((option) => { + const isSelected = value === option.value + return ( + { + onValueChange?.(currentValue === value ? "" : currentValue) + setOpen(false) + }} + > + {renderOption ? ( + renderOption(option, isSelected) + ) : ( + <> + {option.description ? ( +
+ {option.label} + + {option.description} + +
+ ) : ( + option.label + )} + + + )} +
+ ) + }) + + const renderGroupedContent = () => { + const { groups, ungrouped } = groupedOptions + const entries = Array.from(groups.entries()) + + return ( + <> + {ungrouped.length > 0 && ( + {renderItems(ungrouped)} + )} + {entries.map(([groupName, items], index) => ( + + {(index > 0 || ungrouped.length > 0) && } + + {renderItems(items)} + + + ))} + + ) + } return ( - + } > - {value || "Search dynamically..."} - + + {selectedOption?.label || value || placeholder} + +
+ {showClear && value && ( + { + if (e.key === "Enter" || e.key === " ") handleClear(e as unknown as React.MouseEvent) + }} + > + + Clear + + )} + +
- + - + - {isSearching ? ( -
- - Searching... + {loading ? ( +
+ + + Searching... +
+ ) : !search ? ( +
+ {idleMessage} +
+ ) : options.length === 0 ? ( + {emptyMessage} + ) : groupedOptions.groups.size > 0 ? ( + renderGroupedContent() ) : ( - <> - {!search && ( -
- Start typing to search -
- )} - {search && results.length === 0 && !isSearching && ( - No results found. - )} - {results.length > 0 && ( - - {results.map(result => ( - { - setValue(currentValue === value ? "" : currentValue) - setOpen(false) - }} - value={result} - > - - {result} - - ))} - - )} - + {renderItems(options)} )} @@ -102,4 +292,5 @@ const AsyncCombobox = () => { ) } -export default AsyncCombobox +export { AsyncCombobox } +export type { AsyncComboboxOption as AsyncComboboxOptionType } diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index e93ef40..ab24062 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -97,7 +97,11 @@ export { } from "./select"; // AsyncCombobox component -export { default as AsyncCombobox } from "./async-combobox"; +export { + AsyncCombobox, + type AsyncComboboxOption, + type AsyncComboboxProps, +} from "./async-combobox"; // Command component (cmdk) export { From 29dc014ceee54483decc272ba2b785f91b136c33 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Thu, 2 Apr 2026 23:45:14 +0600 Subject: [PATCH 06/20] feat: add AsyncMultiCombobox component with various demo stories --- .../ui/AsyncMultiCombobox.stories.tsx | 312 +++++++++++++ src/components/ui/async-multi-combobox.tsx | 433 ++++++++++++++++++ src/components/ui/index.ts | 8 + 3 files changed, 753 insertions(+) create mode 100644 src/components/ui/AsyncMultiCombobox.stories.tsx create mode 100644 src/components/ui/async-multi-combobox.tsx diff --git a/src/components/ui/AsyncMultiCombobox.stories.tsx b/src/components/ui/AsyncMultiCombobox.stories.tsx new file mode 100644 index 0000000..78040df --- /dev/null +++ b/src/components/ui/AsyncMultiCombobox.stories.tsx @@ -0,0 +1,312 @@ +import { useState, useCallback } from "react"; +import type { Meta, StoryObj } from "@storybook/react"; +import { + AsyncMultiCombobox, + type AsyncMultiComboboxOption, +} from "./async-multi-combobox"; + +const allFrameworks: AsyncMultiComboboxOption[] = [ + { value: "next.js", label: "Next.js" }, + { value: "remix", label: "Remix" }, + { value: "astro", label: "Astro" }, + { value: "nuxt", label: "Nuxt" }, + { value: "sveltekit", label: "SvelteKit" }, + { value: "gatsby", label: "Gatsby" }, + { value: "angular", label: "Angular" }, + { value: "vue", label: "Vue" }, + { value: "react", label: "React" }, + { value: "solid-start", label: "SolidStart" }, +]; + +const meta = { + title: "UI/AsyncMultiCombobox", + component: AsyncMultiCombobox, + parameters: { layout: "centered" }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +// ─── Basic (sync mode) ──────────────────────── + +function BasicDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const Basic: Story = { + render: () => , +}; + +// ─── With Default Values ──────────────────────── + +function DefaultValueDemo() { + const [value, setValue] = useState(["react", "vue", "next.js"]); + + return ( + + ); +} + +export const WithDefaultValues: Story = { + render: () => , +}; + +// ─── Max Count ──────────────────────── + +function MaxCountDemo() { + const [value, setValue] = useState([ + "react", + "vue", + "next.js", + "remix", + "astro", + ]); + + return ( + + ); +} + +export const MaxCount: Story = { + render: () => , +}; + +// ─── Hide Select All ──────────────────────── + +function HideSelectAllDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const HideSelectAll: Story = { + render: () => , +}; + +// ─── Custom Label Function ──────────────────────── + +function CustomLabelDemo() { + const [value, setValue] = useState([]); + + return ( + ( +
+ + {option.label.charAt(0)} + + {option.label} + {isSelected && ( + + selected + + )} +
+ )} + /> + ); +} + +export const CustomLabel: Story = { + render: () => , +}; + +// ─── Async Search ──────────────────────── + +function AsyncSearchDemo() { + const [value, setValue] = useState([]); + const [options, setOptions] = useState([]); + const [loading, setLoading] = useState(false); + + const handleSearch = useCallback((query: string) => { + if (!query) { + setOptions([]); + return; + } + setLoading(true); + setTimeout(() => { + setOptions( + allFrameworks.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + setLoading(false); + }, 500); + }, []); + + return ( + + ); +} + +export const AsyncSearch: Story = { + render: () => , +}; + +// ─── Async with Error ──────────────────────── + +function AsyncErrorDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const AsyncError: Story = { + render: () => , +}; + +// ─── Async with Preselected ──────────────────────── + +function AsyncPreselectedDemo() { + const [value, setValue] = useState(["react", "vue"]); + const [options, setOptions] = useState( + allFrameworks + ); + const [loading, setLoading] = useState(false); + + const handleSearch = useCallback((query: string) => { + if (!query) { + setOptions(allFrameworks); + return; + } + setLoading(true); + setTimeout(() => { + setOptions( + allFrameworks.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + setLoading(false); + }, 400); + }, []); + + return ( + + ); +} + +export const AsyncPreselected: Story = { + render: () => , +}; + +// ─── Invalid ──────────────────────── + +function InvalidDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const Invalid: Story = { + render: () => , +}; + +// ─── Disabled ──────────────────────── + +export const Disabled: Story = { + render: () => ( + {}} + placeholder="Disabled combobox" + disabled + /> + ), +}; + +// ─── Custom Texts ──────────────────────── + +function CustomTextsDemo() { + const [value, setValue] = useState(["react"]); + + return ( + + ); +} + +export const CustomTexts: Story = { + render: () => , +}; diff --git a/src/components/ui/async-multi-combobox.tsx b/src/components/ui/async-multi-combobox.tsx new file mode 100644 index 0000000..41f7654 --- /dev/null +++ b/src/components/ui/async-multi-combobox.tsx @@ -0,0 +1,433 @@ +"use client" + +import * as React from "react" +import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, XIcon } from "lucide-react" +import { Badge } from "@/components/ui/badge" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import { cn } from "@/lib/utils" + +export interface AsyncMultiComboboxOption { + /** The display text for the option */ + label: string + /** The unique identifier for the option. Should be unique and not empty */ + value: string +} + +export interface AsyncMultiComboboxProps { + /** An array of options to display */ + options: AsyncMultiComboboxOption[] + /** Whether the select is async. If true, filtering is handled externally */ + async?: boolean + /** Whether options are currently loading. Works only when async is true */ + loading?: boolean + /** Error object. If set, the error message is shown. Works only when async is true */ + error?: Error | null + /** The default selected values when the component mounts */ + defaultValue?: string[] + /** The selected values (controlled) */ + value?: string[] + /** Placeholder text when no values are selected */ + placeholder?: string + /** Placeholder text for the search input */ + searchPlaceholder?: string + /** Maximum number of badge items to display. Extra items are summarized */ + maxCount?: number + /** Additional class names for the trigger */ + className?: string + /** Additional class names for the popover content */ + contentClassName?: string + /** Text for the clear button */ + clearText?: string + /** Text for the close button */ + closeText?: string + /** Whether to hide the select all option */ + hideSelectAll?: boolean + /** Whether to clear search input when popover closes */ + clearSearchOnClose?: boolean + /** Controlled search value */ + searchValue?: string + /** Whether the combobox is disabled */ + disabled?: boolean + /** Mark combobox as invalid */ + invalid?: boolean + /** Custom label function for rendering each option */ + labelFunc?: ( + option: AsyncMultiComboboxOption, + isSelected: boolean, + index: number + ) => React.ReactNode + /** Callback when selected values change */ + onValueChange: (value: string[]) => void + /** Callback when search input changes */ + onSearch?: (value: string) => void +} + +export interface AsyncMultiComboboxRef { + /** Programmatically control the popover open/close state */ + setIsPopoverOpen: (open: boolean) => void + /** Programmatically set the search input value */ + setSearchValue: (value: string) => void +} + +const AsyncMultiCombobox = React.forwardRef< + AsyncMultiComboboxRef, + AsyncMultiComboboxProps +>( + ( + { + options, + async = false, + loading = false, + error = null, + defaultValue = [], + value, + placeholder = "Select...", + searchPlaceholder = "Search...", + maxCount = 3, + className, + contentClassName, + clearText = "Clear", + closeText = "Close", + hideSelectAll = false, + clearSearchOnClose = false, + searchValue, + disabled = false, + invalid = false, + labelFunc, + onValueChange, + onSearch, + }, + ref + ) => { + const [selectedValues, setSelectedValues] = + React.useState(defaultValue) + const [isPopoverOpen, setIsPopoverOpen] = React.useState(false) + const [searchValueState, setSearchValueState] = React.useState( + searchValue || "" + ) + // Cache options for async mode so selected labels remain visible after search changes + const [reserveOptions, setReserveOptions] = React.useState< + Record + >({}) + const optionsRef = React.useRef>({}) + const isInit = React.useRef(false) + + const handleInputKeyDown = ( + event: React.KeyboardEvent + ) => { + if (event.key === "Enter") { + setIsPopoverOpen(true) + } else if (event.key === "Backspace" && !event.currentTarget.value) { + const newSelectedValues = [...selectedValues] + newSelectedValues.pop() + setSelectedValues(newSelectedValues) + onValueChange(newSelectedValues) + } + } + + const toggleOption = (optionValue: string) => { + const isSelected = selectedValues.includes(optionValue) + const newSelectedValues = isSelected + ? selectedValues.filter((v) => v !== optionValue) + : [...selectedValues, optionValue] + setSelectedValues(newSelectedValues) + onValueChange(newSelectedValues) + } + + const handleClear = () => { + setSelectedValues([]) + onValueChange([]) + } + + const clearExtraOptions = () => { + const newSelectedValues = selectedValues.slice(0, maxCount) + setSelectedValues(newSelectedValues) + onValueChange(newSelectedValues) + } + + const toggleAll = () => { + if (selectedValues.length === options.length) { + handleClear() + } else { + const allValues = options.map((option) => option.value) + setSelectedValues(allValues) + onValueChange(allValues) + } + } + + // Cache options for async mode so selected item labels persist across searches + React.useEffect(() => { + const temp = options.reduce( + (acc, option) => { + acc[option.value] = option + return acc + }, + {} as Record + ) + if (async) { + if (!isInit.current) { + optionsRef.current = temp + setReserveOptions(temp) + isInit.current = true + } else { + const selectedCache = selectedValues.reduce( + (acc, val) => { + const option = optionsRef.current[val] + if (option) { + acc[option.value] = option + } + return acc + }, + {} as Record + ) + optionsRef.current = { ...temp, ...selectedCache } + setReserveOptions({ ...temp, ...selectedCache }) + } + } + }, [async, options, selectedValues]) + + React.useEffect(() => { + if (value) { + setSelectedValues(value) + } + }, [value]) + + React.useEffect(() => { + if (searchValue !== undefined) { + setSearchValueState(searchValue) + } + }, [searchValue]) + + React.useImperativeHandle(ref, () => ({ + setIsPopoverOpen, + setSearchValue: setSearchValueState, + })) + + const getOptionLabel = (val: string) => { + if (async) { + return reserveOptions[val]?.label ?? val + } + return options.find((o) => o.value === val)?.label ?? val + } + + return ( + { + setIsPopoverOpen(open) + if (!open && clearSearchOnClose) { + setSearchValueState("") + onSearch?.("") + } + }} + > + + } + > + {selectedValues.length > 0 ? ( +
+
+ {selectedValues.slice(0, maxCount).map((val) => ( + + {getOptionLabel(val)} + e.stopPropagation()} + onClick={(e) => { + e.stopPropagation() + e.preventDefault() + toggleOption(val) + }} + /> + + ))} + {selectedValues.length > maxCount && ( + + {`+${selectedValues.length - maxCount}`} + e.stopPropagation()} + onClick={(e) => { + e.stopPropagation() + e.preventDefault() + clearExtraOptions() + }} + /> + + )} +
+
+ e.stopPropagation()} + onClick={(e) => { + e.stopPropagation() + e.preventDefault() + handleClear() + }} + /> +
+ +
+
+ ) : ( +
+ + {placeholder} + + +
+ )} + + + + { + setSearchValueState(val) + onSearch?.(val) + }} + onKeyDown={handleInputKeyDown} + /> + + {async && error && ( +
+ {error.message} +
+ )} + {async && loading && options.length === 0 && ( +
+ + + Searching... + +
+ )} + {async ? ( + !loading && + !error && + options.length === 0 && ( +
+ No results found. +
+ ) + ) : ( + No results found. + )} + + {!async && !hideSelectAll && ( + +
+ +
+ Select all +
+ )} + {options.map((option, index) => { + const isSelected = selectedValues.includes(option.value) + return ( + toggleOption(option.value)} + className="cursor-pointer" + > +
+ +
+ {labelFunc + ? labelFunc(option, isSelected, index) + : option.label} +
+ ) + })} +
+ + +
+ {selectedValues.length > 0 && ( + <> + + {clearText} + +
+ + )} + setIsPopoverOpen(false)} + className="flex-1 justify-center cursor-pointer max-w-full" + > + {closeText} + +
+ + + + + + ) + } +) + +AsyncMultiCombobox.displayName = "AsyncMultiCombobox" + +export { AsyncMultiCombobox } diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index ab24062..450e08f 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -96,6 +96,14 @@ export { SelectValue, } from "./select"; +// AsyncMultiCombobox component +export { + AsyncMultiCombobox, + type AsyncMultiComboboxOption, + type AsyncMultiComboboxProps, + type AsyncMultiComboboxRef, +} from "./async-multi-combobox"; + // AsyncCombobox component export { AsyncCombobox, From c793a5abcba37f02f99ccb3ac601aa56ddd65807 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Thu, 2 Apr 2026 23:46:45 +0600 Subject: [PATCH 07/20] feat: update AsyncMultiCombobox badge styles for improved appearance --- src/components/ui/async-multi-combobox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ui/async-multi-combobox.tsx b/src/components/ui/async-multi-combobox.tsx index 41f7654..1a181bd 100644 --- a/src/components/ui/async-multi-combobox.tsx +++ b/src/components/ui/async-multi-combobox.tsx @@ -258,7 +258,7 @@ const AsyncMultiCombobox = React.forwardRef< {getOptionLabel(val)} maxCount && ( {`+${selectedValues.length - maxCount}`} Date: Thu, 2 Apr 2026 23:50:02 +0600 Subject: [PATCH 08/20] feat: improve AsyncMultiCombobox accessibility and interaction for disabled state --- src/components/ui/async-multi-combobox.tsx | 46 +++++++++++++--------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/components/ui/async-multi-combobox.tsx b/src/components/ui/async-multi-combobox.tsx index 1a181bd..62f41b9 100644 --- a/src/components/ui/async-multi-combobox.tsx +++ b/src/components/ui/async-multi-combobox.tsx @@ -227,6 +227,7 @@ const AsyncMultiCombobox = React.forwardRef< { + if (disabled) return setIsPopoverOpen(open) if (!open && clearSearchOnClose) { setSearchValueState("") @@ -245,7 +246,7 @@ const AsyncMultiCombobox = React.forwardRef< "cursor-pointer flex h-auto min-h-9 w-[250px] items-center justify-between rounded-md border border-input bg-transparent px-2 py-1 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]", invalid && "border-destructive ring-destructive/20 ring-[3px] dark:ring-destructive/40", - disabled && "cursor-not-allowed opacity-50", + disabled && "cursor-not-allowed opacity-50 pointer-events-none", className )} /> @@ -253,7 +254,7 @@ const AsyncMultiCombobox = React.forwardRef< > {selectedValues.length > 0 ? (
-
+
{selectedValues.slice(0, maxCount).map((val) => ( {getOptionLabel(val)} - e.stopPropagation()} - onClick={(e) => { + { e.stopPropagation() e.preventDefault() toggleOption(val) }} - /> + > + + ))} {selectedValues.length > maxCount && ( @@ -278,28 +282,34 @@ const AsyncMultiCombobox = React.forwardRef< className="h-6 gap-1 rounded-md px-2 text-xs font-normal" > {`+${selectedValues.length - maxCount}`} - e.stopPropagation()} - onClick={(e) => { + { e.stopPropagation() e.preventDefault() clearExtraOptions() }} - /> + > + + )}
-
- e.stopPropagation()} - onClick={(e) => { +
+ { e.stopPropagation() e.preventDefault() handleClear() }} - /> + > + +
From 725f193edcc222cfbb8d6a42f86c53ca2c76bb0c Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Thu, 2 Apr 2026 23:55:25 +0600 Subject: [PATCH 09/20] feat: add static options demos for AsyncCombobox and AsyncMultiCombobox components --- src/components/ui/AsyncCombobox.stories.tsx | 62 +++++++++++++++++++ .../ui/AsyncMultiCombobox.stories.tsx | 48 ++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/src/components/ui/AsyncCombobox.stories.tsx b/src/components/ui/AsyncCombobox.stories.tsx index 8fc5423..0e6d509 100644 --- a/src/components/ui/AsyncCombobox.stories.tsx +++ b/src/components/ui/AsyncCombobox.stories.tsx @@ -363,3 +363,65 @@ function AsyncComboboxPreselectedDemo() { export const Preselected: Story = { render: () => , }; + +// ─── Static (no server request) ──────────────────────── + +const staticOptions: AsyncComboboxOption[] = [ + { value: "active", label: "Active" }, + { value: "inactive", label: "Inactive" }, +]; + +function AsyncComboboxStaticDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState(allFrameworks); + + return ( + { + if (!query) { + setOptions(allFrameworks); + return; + } + setOptions( + allFrameworks.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + }} + options={options} + value={value} + onValueChange={setValue} + placeholder="Select framework..." + idleMessage="All frameworks" + showClear + /> + ); +} + +export const StaticOptions: Story = { + render: () => , +}; + +// ─── Static with few options ──────────────────────── + +function AsyncComboboxFewOptionsDemo() { + const [value, setValue] = useState(""); + + return ( + {}} + options={staticOptions} + value={value} + onValueChange={setValue} + placeholder="Select status..." + idleMessage="Choose a status" + showClear + className="w-[180px]" + contentClassName="w-[180px]" + /> + ); +} + +export const FewStaticOptions: Story = { + render: () => , +}; diff --git a/src/components/ui/AsyncMultiCombobox.stories.tsx b/src/components/ui/AsyncMultiCombobox.stories.tsx index 78040df..cea8c36 100644 --- a/src/components/ui/AsyncMultiCombobox.stories.tsx +++ b/src/components/ui/AsyncMultiCombobox.stories.tsx @@ -310,3 +310,51 @@ function CustomTextsDemo() { export const CustomTexts: Story = { render: () => , }; + +// ─── Static (no server request) ──────────────────────── + +function StaticOptionsDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const StaticOptions: Story = { + render: () => , +}; + +// ─── Static with few options ──────────────────────── + +const statusOptions: AsyncMultiComboboxOption[] = [ + { value: "active", label: "Active" }, + { value: "inactive", label: "Inactive" }, + { value: "pending", label: "Pending" }, +]; + +function FewStaticOptionsDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const FewStaticOptions: Story = { + render: () => , +}; From c171100a267d4fb81fd6e70e371af047264ff064 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:03:56 +0600 Subject: [PATCH 10/20] feat: add SmartMultiSelect and SmartSelect components with comprehensive stories - Introduced SmartMultiSelect component for multi-selection with async capabilities and custom rendering options. - Added SmartSelect component for single selection with search functionality and grouping options. - Created detailed Storybook stories for both components showcasing various use cases including async search, custom labels, and error handling. - Updated index.ts to export new components and their types. --- ...ories.tsx => SmartMultiSelect.stories.tsx} | 46 ++++----- ...ox.stories.tsx => SmartSelect.stories.tsx} | 96 +++++++++---------- src/components/ui/index.ts | 22 ++--- ...ti-combobox.tsx => smart-multi-select.tsx} | 32 +++---- .../{async-combobox.tsx => smart-select.tsx} | 26 ++--- 5 files changed, 111 insertions(+), 111 deletions(-) rename src/components/ui/{AsyncMultiCombobox.stories.tsx => SmartMultiSelect.stories.tsx} (91%) rename src/components/ui/{AsyncCombobox.stories.tsx => SmartSelect.stories.tsx} (84%) rename src/components/ui/{async-multi-combobox.tsx => smart-multi-select.tsx} (95%) rename src/components/ui/{async-combobox.tsx => smart-select.tsx} (93%) diff --git a/src/components/ui/AsyncMultiCombobox.stories.tsx b/src/components/ui/SmartMultiSelect.stories.tsx similarity index 91% rename from src/components/ui/AsyncMultiCombobox.stories.tsx rename to src/components/ui/SmartMultiSelect.stories.tsx index cea8c36..8047ce0 100644 --- a/src/components/ui/AsyncMultiCombobox.stories.tsx +++ b/src/components/ui/SmartMultiSelect.stories.tsx @@ -1,11 +1,11 @@ import { useState, useCallback } from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { - AsyncMultiCombobox, - type AsyncMultiComboboxOption, -} from "./async-multi-combobox"; + SmartMultiSelect, + type SmartMultiSelectOption, +} from "./smart-multi-select"; -const allFrameworks: AsyncMultiComboboxOption[] = [ +const allFrameworks: SmartMultiSelectOption[] = [ { value: "next.js", label: "Next.js" }, { value: "remix", label: "Remix" }, { value: "astro", label: "Astro" }, @@ -19,11 +19,11 @@ const allFrameworks: AsyncMultiComboboxOption[] = [ ]; const meta = { - title: "UI/AsyncMultiCombobox", - component: AsyncMultiCombobox, + title: "UI/SmartMultiSelect", + component: SmartMultiSelect, parameters: { layout: "centered" }, tags: ["autodocs"], -} satisfies Meta; +} satisfies Meta; export default meta; @@ -35,7 +35,7 @@ function BasicDemo() { const [value, setValue] = useState([]); return ( - (["react", "vue", "next.js"]); return ( - ([]); return ( - ([]); return ( - ([]); - const [options, setOptions] = useState([]); + const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); const handleSearch = useCallback((query: string) => { @@ -173,7 +173,7 @@ function AsyncSearchDemo() { }, []); return ( - ([]); return ( - (["react", "vue"]); - const [options, setOptions] = useState( + const [options, setOptions] = useState( allFrameworks ); const [loading, setLoading] = useState(false); @@ -238,7 +238,7 @@ function AsyncPreselectedDemo() { }, []); return ( - ([]); return ( - ( - {}} @@ -295,7 +295,7 @@ function CustomTextsDemo() { const [value, setValue] = useState(["react"]); return ( - ([]); return ( - ([]); return ( - ([]); + const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); const handleSearch = useCallback( @@ -48,11 +48,11 @@ function useAsyncSearch( } const meta = { - title: "UI/AsyncCombobox", - component: AsyncCombobox, + title: "UI/SmartSelect", + component: SmartSelect, parameters: { layout: "centered" }, tags: ["autodocs"], -} satisfies Meta; +} satisfies Meta; export default meta; @@ -60,12 +60,12 @@ type Story = StoryObj; // ─── Basic ──────────────────────── -function AsyncComboboxBasicDemo() { +function SmartSelectBasicDemo() { const [value, setValue] = useState(""); const { options, loading, handleSearch } = useAsyncSearch(allFrameworks); return ( - , + render: () => , }; // ─── Clear Button ──────────────────────── -function AsyncComboboxClearDemo() { +function SmartSelectClearDemo() { const [value, setValue] = useState(""); const { options, loading, handleSearch } = useAsyncSearch(allFrameworks); return ( - , + render: () => , }; // ─── Custom Items (with description) ──────────────────────── -const frameworksWithDesc: AsyncComboboxOption[] = [ +const frameworksWithDesc: SmartSelectOption[] = [ { value: "next.js", label: "Next.js", description: "The React Framework" }, { value: "remix", label: "Remix", description: "Build better websites" }, { value: "astro", label: "Astro", description: "The web framework for content" }, @@ -114,12 +114,12 @@ const frameworksWithDesc: AsyncComboboxOption[] = [ { value: "sveltekit", label: "SvelteKit", description: "Web development, streamlined" }, ]; -function AsyncComboboxCustomItemsDemo() { +function SmartSelectCustomItemsDemo() { const [value, setValue] = useState(""); const { options, loading, handleSearch } = useAsyncSearch(frameworksWithDesc); return ( - , + render: () => , }; // ─── Custom Render ──────────────────────── -function AsyncComboboxCustomRenderDemo() { +function SmartSelectCustomRenderDemo() { const [value, setValue] = useState(""); const { options, loading, handleSearch } = useAsyncSearch(frameworksWithDesc); return ( - , + render: () => , }; // ─── Groups ──────────────────────── -const groupedFrameworks: AsyncComboboxOption[] = [ +const groupedFrameworks: SmartSelectOption[] = [ { value: "next.js", label: "Next.js", group: "React" }, { value: "remix", label: "Remix", group: "React" }, { value: "gatsby", label: "Gatsby", group: "React" }, @@ -192,12 +192,12 @@ const groupedFrameworks: AsyncComboboxOption[] = [ { value: "angular", label: "Angular", group: "Standalone" }, ]; -function AsyncComboboxGroupsDemo() { +function SmartSelectGroupsDemo() { const [value, setValue] = useState(""); const { options, loading, handleSearch } = useAsyncSearch(groupedFrameworks); return ( - , + render: () => , }; // ─── Custom Messages ──────────────────────── -function AsyncComboboxCustomMessagesDemo() { +function SmartSelectCustomMessagesDemo() { const [value, setValue] = useState(""); const { options, loading, handleSearch } = useAsyncSearch(allFrameworks, 800); return ( - , + render: () => , }; // ─── User Search (with disabled items) ──────────────────────── -function AsyncComboboxUsersDemo() { +function SmartSelectUsersDemo() { const [value, setValue] = useState(""); - const [options, setOptions] = useState([]); + const [options, setOptions] = useState([]); const [loading, setLoading] = useState(false); const handleSearch = useCallback((query: string) => { @@ -266,7 +266,7 @@ function AsyncComboboxUsersDemo() { }, []); return ( - , + render: () => , }; // ─── Invalid ──────────────────────── -function AsyncComboboxInvalidDemo() { +function SmartSelectInvalidDemo() { const [value, setValue] = useState(""); const { options, loading, handleSearch } = useAsyncSearch(allFrameworks); return ( - , + render: () => , }; // ─── Disabled ──────────────────────── export const Disabled: Story = { render: () => ( - {}} options={[]} disabled @@ -325,9 +325,9 @@ export const Disabled: Story = { // ─── Preselected ──────────────────────── -function AsyncComboboxPreselectedDemo() { +function SmartSelectPreselectedDemo() { const [value, setValue] = useState("react"); - const [options, setOptions] = useState(allFrameworks); + const [options, setOptions] = useState(allFrameworks); const [loading, setLoading] = useState(false); const handleSearch = useCallback((query: string) => { @@ -347,7 +347,7 @@ function AsyncComboboxPreselectedDemo() { }, []); return ( - , + render: () => , }; // ─── Static (no server request) ──────────────────────── -const staticOptions: AsyncComboboxOption[] = [ +const staticOptions: SmartSelectOption[] = [ { value: "active", label: "Active" }, { value: "inactive", label: "Inactive" }, ]; -function AsyncComboboxStaticDemo() { +function SmartSelectStaticDemo() { const [value, setValue] = useState(""); const [options, setOptions] = useState(allFrameworks); return ( - { if (!query) { setOptions(allFrameworks); @@ -399,16 +399,16 @@ function AsyncComboboxStaticDemo() { } export const StaticOptions: Story = { - render: () => , + render: () => , }; // ─── Static with few options ──────────────────────── -function AsyncComboboxFewOptionsDemo() { +function SmartSelectFewOptionsDemo() { const [value, setValue] = useState(""); return ( - {}} options={staticOptions} value={value} @@ -423,5 +423,5 @@ function AsyncComboboxFewOptionsDemo() { } export const FewStaticOptions: Story = { - render: () => , + render: () => , }; diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index 450e08f..236d757 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -96,20 +96,20 @@ export { SelectValue, } from "./select"; -// AsyncMultiCombobox component +// SmartMultiSelect component export { - AsyncMultiCombobox, - type AsyncMultiComboboxOption, - type AsyncMultiComboboxProps, - type AsyncMultiComboboxRef, -} from "./async-multi-combobox"; + SmartMultiSelect, + type SmartMultiSelectOption, + type SmartMultiSelectProps, + type SmartMultiSelectRef, +} from "./smart-multi-select"; -// AsyncCombobox component +// SmartSelect component export { - AsyncCombobox, - type AsyncComboboxOption, - type AsyncComboboxProps, -} from "./async-combobox"; + SmartSelect, + type SmartSelectOption, + type SmartSelectProps, +} from "./smart-select"; // Command component (cmdk) export { diff --git a/src/components/ui/async-multi-combobox.tsx b/src/components/ui/smart-multi-select.tsx similarity index 95% rename from src/components/ui/async-multi-combobox.tsx rename to src/components/ui/smart-multi-select.tsx index 62f41b9..a9786d9 100644 --- a/src/components/ui/async-multi-combobox.tsx +++ b/src/components/ui/smart-multi-select.tsx @@ -19,16 +19,16 @@ import { } from "@/components/ui/popover" import { cn } from "@/lib/utils" -export interface AsyncMultiComboboxOption { +export interface SmartMultiSelectOption { /** The display text for the option */ label: string /** The unique identifier for the option. Should be unique and not empty */ value: string } -export interface AsyncMultiComboboxProps { +export interface SmartMultiSelectProps { /** An array of options to display */ - options: AsyncMultiComboboxOption[] + options: SmartMultiSelectOption[] /** Whether the select is async. If true, filtering is handled externally */ async?: boolean /** Whether options are currently loading. Works only when async is true */ @@ -65,7 +65,7 @@ export interface AsyncMultiComboboxProps { invalid?: boolean /** Custom label function for rendering each option */ labelFunc?: ( - option: AsyncMultiComboboxOption, + option: SmartMultiSelectOption, isSelected: boolean, index: number ) => React.ReactNode @@ -75,16 +75,16 @@ export interface AsyncMultiComboboxProps { onSearch?: (value: string) => void } -export interface AsyncMultiComboboxRef { +export interface SmartMultiSelectRef { /** Programmatically control the popover open/close state */ setIsPopoverOpen: (open: boolean) => void /** Programmatically set the search input value */ setSearchValue: (value: string) => void } -const AsyncMultiCombobox = React.forwardRef< - AsyncMultiComboboxRef, - AsyncMultiComboboxProps +const SmartMultiSelect = React.forwardRef< + SmartMultiSelectRef, + SmartMultiSelectProps >( ( { @@ -120,9 +120,9 @@ const AsyncMultiCombobox = React.forwardRef< ) // Cache options for async mode so selected labels remain visible after search changes const [reserveOptions, setReserveOptions] = React.useState< - Record + Record >({}) - const optionsRef = React.useRef>({}) + const optionsRef = React.useRef>({}) const isInit = React.useRef(false) const handleInputKeyDown = ( @@ -175,7 +175,7 @@ const AsyncMultiCombobox = React.forwardRef< acc[option.value] = option return acc }, - {} as Record + {} as Record ) if (async) { if (!isInit.current) { @@ -191,7 +191,7 @@ const AsyncMultiCombobox = React.forwardRef< } return acc }, - {} as Record + {} as Record ) optionsRef.current = { ...temp, ...selectedCache } setReserveOptions({ ...temp, ...selectedCache }) @@ -241,7 +241,7 @@ const AsyncMultiCombobox = React.forwardRef< role="combobox" aria-expanded={isPopoverOpen} aria-invalid={invalid || undefined} - data-slot="async-multi-combobox-trigger" + data-slot="smart-multi-select-trigger" className={cn( "cursor-pointer flex h-auto min-h-9 w-[250px] items-center justify-between rounded-md border border-input bg-transparent px-2 py-1 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]", invalid && @@ -324,7 +324,7 @@ const AsyncMultiCombobox = React.forwardRef< )} @@ -438,6 +438,6 @@ const AsyncMultiCombobox = React.forwardRef< } ) -AsyncMultiCombobox.displayName = "AsyncMultiCombobox" +SmartMultiSelect.displayName = "SmartMultiSelect" -export { AsyncMultiCombobox } +export { SmartMultiSelect } diff --git a/src/components/ui/async-combobox.tsx b/src/components/ui/smart-select.tsx similarity index 93% rename from src/components/ui/async-combobox.tsx rename to src/components/ui/smart-select.tsx index db7847a..db56b52 100644 --- a/src/components/ui/async-combobox.tsx +++ b/src/components/ui/smart-select.tsx @@ -19,7 +19,7 @@ import { } from "@/components/ui/popover" import { cn } from "@/lib/utils" -export interface AsyncComboboxOption { +export interface SmartSelectOption { /** Unique value for the option */ value: string /** Display label */ @@ -32,11 +32,11 @@ export interface AsyncComboboxOption { group?: string } -export interface AsyncComboboxProps { +export interface SmartSelectProps { /** Async callback triggered on search input change (after debounce) */ onSearch: (query: string) => void | Promise /** Options to display in the dropdown */ - options: AsyncComboboxOption[] + options: SmartSelectOption[] /** Currently selected value */ value?: string /** Callback when selection changes */ @@ -64,10 +64,10 @@ export interface AsyncComboboxProps { /** Width class for the popover content @default "w-[250px]" */ contentClassName?: string /** Custom render function for each option */ - renderOption?: (option: AsyncComboboxOption, isSelected: boolean) => React.ReactNode + renderOption?: (option: SmartSelectOption, isSelected: boolean) => React.ReactNode } -function AsyncCombobox({ +function SmartSelect({ onSearch, options, value = "", @@ -84,7 +84,7 @@ function AsyncCombobox({ className, contentClassName, renderOption, -}: AsyncComboboxProps) { +}: SmartSelectProps) { const [open, setOpen] = React.useState(false) const [search, setSearch] = React.useState("") const timerRef = React.useRef | null>(null) @@ -125,8 +125,8 @@ function AsyncCombobox({ // Group options by group name const groupedOptions = React.useMemo(() => { - const groups = new Map() - const ungrouped: AsyncComboboxOption[] = [] + const groups = new Map() + const ungrouped: SmartSelectOption[] = [] for (const opt of options) { if (opt.group) { @@ -149,7 +149,7 @@ function AsyncCombobox({ [onValueChange] ) - const renderItems = (items: AsyncComboboxOption[]) => + const renderItems = (items: SmartSelectOption[]) => items.map((option) => { const isSelected = value === option.value return ( @@ -219,7 +219,7 @@ function AsyncCombobox({ aria-expanded={open} aria-invalid={invalid || undefined} disabled={disabled} - data-slot="async-combobox-trigger" + data-slot="smart-select-trigger" className={cn( "w-[250px] justify-between border-input text-foreground font-normal", invalid && @@ -256,7 +256,7 @@ function AsyncCombobox({
@@ -292,5 +292,5 @@ function AsyncCombobox({ ) } -export { AsyncCombobox } -export type { AsyncComboboxOption as AsyncComboboxOptionType } +export { SmartSelect } +export type { SmartSelectOption as SmartSelectOptionType } From cd7e13fa9a0ff6bd60dd409b433d7e630e0bdb43 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:35:03 +0600 Subject: [PATCH 11/20] feat: enhance SmartSelect and SmartMultiSelect components with create functionality and improved styling --- .../ui/SmartMultiSelect.stories.tsx | 6 +- src/components/ui/SmartSelect.stories.tsx | 275 +++++++++++++++++- src/components/ui/index.ts | 1 + src/components/ui/smart-multi-select.tsx | 2 +- src/components/ui/smart-select.tsx | 171 +++++++++-- 5 files changed, 422 insertions(+), 33 deletions(-) diff --git a/src/components/ui/SmartMultiSelect.stories.tsx b/src/components/ui/SmartMultiSelect.stories.tsx index 8047ce0..f119df9 100644 --- a/src/components/ui/SmartMultiSelect.stories.tsx +++ b/src/components/ui/SmartMultiSelect.stories.tsx @@ -86,7 +86,7 @@ function MaxCountDemo() { placeholder="Select frameworks..." maxCount={2} className="w-[300px]" - contentClassName="w-[300px]" + /> ); } @@ -127,7 +127,7 @@ function CustomLabelDemo() { onValueChange={setValue} placeholder="Select frameworks..." className="w-[300px]" - contentClassName="w-[300px]" + labelFunc={(option, isSelected) => (
@@ -350,7 +350,7 @@ function FewStaticOptionsDemo() { placeholder="Select status..." hideSelectAll className="w-[200px]" - contentClassName="w-[200px]" + /> ); } diff --git a/src/components/ui/SmartSelect.stories.tsx b/src/components/ui/SmartSelect.stories.tsx index 44f5100..fec9aa0 100644 --- a/src/components/ui/SmartSelect.stories.tsx +++ b/src/components/ui/SmartSelect.stories.tsx @@ -3,7 +3,9 @@ import type { Meta, StoryObj } from "@storybook/react"; import { SmartSelect, type SmartSelectOption, + type SmartSelectCreateContext, } from "./smart-select"; +import { Input, Label, Button } from "./index"; const allFrameworks: SmartSelectOption[] = [ { value: "next.js", label: "Next.js" }, @@ -127,7 +129,7 @@ function SmartSelectCustomItemsDemo() { loading={loading} placeholder="Search frameworks..." className="w-[300px]" - contentClassName="w-[300px]" + /> ); } @@ -151,7 +153,7 @@ function SmartSelectCustomRenderDemo() { loading={loading} placeholder="Search frameworks..." className="w-[300px]" - contentClassName="w-[300px]" + renderOption={(option, isSelected) => (
@@ -278,7 +280,7 @@ function SmartSelectUsersDemo() { idleMessage="Search by name or email" showClear className="w-[300px]" - contentClassName="w-[300px]" + /> ); } @@ -417,7 +419,7 @@ function SmartSelectFewOptionsDemo() { idleMessage="Choose a status" showClear className="w-[180px]" - contentClassName="w-[180px]" + /> ); } @@ -425,3 +427,268 @@ function SmartSelectFewOptionsDemo() { export const FewStaticOptions: Story = { render: () => , }; + +// ─── Disabled Search ──────────────────────── + +function SmartSelectNoSearchDemo() { + const [value, setValue] = useState(""); + + return ( + {}} + options={[...staticOptions]} + value={value} + onValueChange={setValue} + placeholder="Select status..." + idleMessage="Choose a status" + disableSearch + className="w-[180px]" + + /> + ); +} + +export const DisabledSearch: Story = { + render: () => , +}; + +// ─── Create with Default Form ──────────────────────── + +function SmartSelectCreateDefaultDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState([...allFrameworks]); + const [filteredOptions, setFilteredOptions] = useState([ + ...allFrameworks, + ]); + + return ( + { + if (!query) { + setFilteredOptions(options); + return; + } + setFilteredOptions( + options.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + }} + options={filteredOptions} + value={value} + onValueChange={setValue} + placeholder="Select or create..." + idleMessage="All frameworks" + showClear + className="w-[280px]" + + onCreate={(name, done) => { + const newOption = { value: name.toLowerCase(), label: name }; + setOptions((prev) => [...prev, newOption]); + setFilteredOptions((prev) => [...prev, newOption]); + done(newOption.value); + }} + selectOnCreate + /> + ); +} + +export const CreateDefault: Story = { + render: () => , +}; + +// ─── Create with Custom Form (simple) ──────────────────────── + +function SimpleCreateForm({ + ctx, + onCreated, +}: { + ctx: SmartSelectCreateContext; + onCreated: (option: SmartSelectOption) => void; +}) { + const [name, setName] = useState(ctx.searchValue); + return ( +
+

Create new framework

+ setName(e.target.value)} + placeholder="Framework name..." + autoFocus + onKeyDown={(e) => e.stopPropagation()} + /> +
+ + +
+
+ ); +} + +function SmartSelectCreateCustomSimpleDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState([...allFrameworks]); + const [filteredOptions, setFilteredOptions] = useState([ + ...allFrameworks, + ]); + + const addOption = (opt: SmartSelectOption) => { + setOptions((prev) => [...prev, opt]); + setFilteredOptions((prev) => [...prev, opt]); + setValue(opt.value); + }; + + return ( + { + if (!query) { + setFilteredOptions(options); + return; + } + setFilteredOptions( + options.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + }} + options={filteredOptions} + value={value} + onValueChange={setValue} + placeholder="Select or create..." + idleMessage="All frameworks" + showClear + className="w-[300px]" + + renderCreateForm={(ctx) => ( + + )} + /> + ); +} + +export const CreateCustomSimple: Story = { + render: () => , +}; + +// ─── Create with Custom Form (complex) ──────────────────────── + +function ComplexCreateForm({ + ctx, + onCreated, +}: { + ctx: SmartSelectCreateContext; + onCreated: (option: SmartSelectOption) => void; +}) { + const [name, setName] = useState(ctx.searchValue); + const [description, setDescription] = useState(""); + const [group, setGroup] = useState(""); + return ( +
+

Create new framework

+
+ + setName(e.target.value)} + placeholder="Framework name..." + autoFocus + onKeyDown={(e) => e.stopPropagation()} + /> +
+
+ + setDescription(e.target.value)} + placeholder="Short description..." + onKeyDown={(e) => e.stopPropagation()} + /> +
+
+ + setGroup(e.target.value)} + placeholder="e.g. React, Vue..." + onKeyDown={(e) => e.stopPropagation()} + /> +
+
+ + +
+
+ ); +} + +function SmartSelectCreateCustomComplexDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState([...allFrameworks]); + const [filteredOptions, setFilteredOptions] = useState([ + ...allFrameworks, + ]); + + const addOption = (opt: SmartSelectOption) => { + setOptions((prev) => [...prev, opt]); + setFilteredOptions((prev) => [...prev, opt]); + setValue(opt.value); + }; + + return ( + { + if (!query) { + setFilteredOptions(options); + return; + } + setFilteredOptions( + options.filter((f) => + f.label.toLowerCase().includes(query.toLowerCase()) + ) + ); + }} + options={filteredOptions} + value={value} + onValueChange={setValue} + placeholder="Select or create..." + idleMessage="All frameworks" + showClear + className="w-[350px]" + + renderCreateForm={(ctx) => ( + + )} + /> + ); +} + +export const CreateCustomComplex: Story = { + render: () => , +}; diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index 236d757..ad49472 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -109,6 +109,7 @@ export { SmartSelect, type SmartSelectOption, type SmartSelectProps, + type SmartSelectCreateContext, } from "./smart-select"; // Command component (cmdk) diff --git a/src/components/ui/smart-multi-select.tsx b/src/components/ui/smart-multi-select.tsx index a9786d9..d28e1a6 100644 --- a/src/components/ui/smart-multi-select.tsx +++ b/src/components/ui/smart-multi-select.tsx @@ -325,7 +325,7 @@ const SmartMultiSelect = React.forwardRef< diff --git a/src/components/ui/smart-select.tsx b/src/components/ui/smart-select.tsx index db56b52..4ea86bc 100644 --- a/src/components/ui/smart-select.tsx +++ b/src/components/ui/smart-select.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, XIcon } from "lucide-react" +import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, PlusIcon, XIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Command, @@ -32,6 +32,15 @@ export interface SmartSelectOption { group?: string } +export interface SmartSelectCreateContext { + /** Current search/input value */ + searchValue: string + /** Clear the search input and close the create form */ + clearSearch: () => void + /** Close the popover */ + close: () => void +} + export interface SmartSelectProps { /** Async callback triggered on search input change (after debounce) */ onSearch: (query: string) => void | Promise @@ -57,6 +66,8 @@ export interface SmartSelectProps { disabled?: boolean /** Mark combobox as invalid */ invalid?: boolean + /** Whether to disable the search input */ + disableSearch?: boolean /** Debounce delay in milliseconds @default 300 */ debounceMs?: number /** Additional class for the trigger button */ @@ -65,6 +76,23 @@ export interface SmartSelectProps { contentClassName?: string /** Custom render function for each option */ renderOption?: (option: SmartSelectOption, isSelected: boolean) => React.ReactNode + /** + * Render function for the create form. Receives a context with searchValue, + * clearSearch, and close helpers. Return any React node — a simple input or + * a complex form. The form is shown when no results match or when the user + * clicks the create button. + */ + renderCreateForm?: (ctx: SmartSelectCreateContext) => React.ReactNode + /** Whether to auto-select the newly created item @default false */ + selectOnCreate?: boolean + /** + * Callback fired after a new item is created via the default create form. + * If you use `renderCreateForm`, you handle creation yourself. + * Receives the name and a done callback to call when finished. + */ + onCreate?: (name: string, done: (createdValue?: string) => void) => void + /** Text for the default create button @default "Create" */ + createButtonText?: string } function SmartSelect({ @@ -80,13 +108,19 @@ function SmartSelect({ showClear = false, disabled = false, invalid = false, + disableSearch = false, debounceMs = 300, className, contentClassName, renderOption, + renderCreateForm, + selectOnCreate = false, + onCreate, + createButtonText = "Create", }: SmartSelectProps) { const [open, setOpen] = React.useState(false) const [search, setSearch] = React.useState("") + const [showCreate, setShowCreate] = React.useState(false) const timerRef = React.useRef | null>(null) const selectedOption = React.useMemo( @@ -97,6 +131,7 @@ function SmartSelect({ const handleSearchChange = React.useCallback( (query: string) => { setSearch(query) + setShowCreate(false) if (timerRef.current) { clearTimeout(timerRef.current) @@ -120,10 +155,10 @@ function SmartSelect({ React.useEffect(() => { if (!open) { setSearch("") + setShowCreate(false) } }, [open]) - // Group options by group name const groupedOptions = React.useMemo(() => { const groups = new Map() const ungrouped: SmartSelectOption[] = [] @@ -149,6 +184,37 @@ function SmartSelect({ [onValueChange] ) + const clearSearch = React.useCallback(() => { + setSearch("") + setShowCreate(false) + onSearch("") + }, [onSearch]) + + const createCtx: SmartSelectCreateContext = React.useMemo( + () => ({ + searchValue: search, + clearSearch, + close: () => setOpen(false), + }), + [search, clearSearch] + ) + + const handleDefaultCreate = React.useCallback(() => { + if (!onCreate || !search.trim()) return + onCreate(search.trim(), (createdValue?: string) => { + if (selectOnCreate && createdValue) { + onValueChange?.(createdValue) + } + clearSearch() + setShowCreate(false) + }) + }, [onCreate, search, selectOnCreate, onValueChange, clearSearch]) + + const hasCreateCapability = !!(renderCreateForm || onCreate) + const showCreateForm = + hasCreateCapability && + (showCreate || (search && !loading && options.length === 0)) + const renderItems = (items: SmartSelectOption[]) => items.map((option) => { const isSelected = value === option.value @@ -209,6 +275,34 @@ function SmartSelect({ ) } + const renderDefaultCreateForm = () => ( +
+
+ setSearch(e.target.value)} + placeholder="Enter name..." + autoFocus + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault() + handleDefaultCreate() + } + e.stopPropagation() + }} + /> + +
+
+ ) + return ( - + {!disableSearch && ( +
+ + {hasCreateCapability && ( + + )} +
+ )} - {loading ? ( -
- - - Searching... - -
- ) : !search ? ( -
- {idleMessage} -
- ) : options.length === 0 ? ( - {emptyMessage} - ) : groupedOptions.groups.size > 0 ? ( - renderGroupedContent() - ) : ( - {renderItems(options)} + {!showCreateForm && ( + <> + {loading ? ( +
+ + + Searching... + +
+ ) : options.length > 0 ? ( + groupedOptions.groups.size > 0 + ? renderGroupedContent() + : {renderItems(options)} + ) : !search ? ( +
+ {idleMessage} +
+ ) : !hasCreateCapability ? ( + {emptyMessage} + ) : null} + )}
+ {showCreateForm && ( + renderCreateForm + ? renderCreateForm(createCtx) + : renderDefaultCreateForm() + )}
From d426bb03e5254cb965ae75679e77e79cb75301e8 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:43:41 +0600 Subject: [PATCH 12/20] feat: optimize option handling in SmartSelect create functionality --- src/components/ui/SmartSelect.stories.tsx | 7 +++++-- src/components/ui/smart-select.tsx | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/ui/SmartSelect.stories.tsx b/src/components/ui/SmartSelect.stories.tsx index fec9aa0..2a0ad7e 100644 --- a/src/components/ui/SmartSelect.stories.tsx +++ b/src/components/ui/SmartSelect.stories.tsx @@ -484,8 +484,11 @@ function SmartSelectCreateDefaultDemo() { onCreate={(name, done) => { const newOption = { value: name.toLowerCase(), label: name }; - setOptions((prev) => [...prev, newOption]); - setFilteredOptions((prev) => [...prev, newOption]); + setOptions((prev) => { + const updated = [...prev, newOption]; + setFilteredOptions(updated); + return updated; + }); done(newOption.value); }} selectOnCreate diff --git a/src/components/ui/smart-select.tsx b/src/components/ui/smart-select.tsx index 4ea86bc..3647c0f 100644 --- a/src/components/ui/smart-select.tsx +++ b/src/components/ui/smart-select.tsx @@ -187,8 +187,7 @@ function SmartSelect({ const clearSearch = React.useCallback(() => { setSearch("") setShowCreate(false) - onSearch("") - }, [onSearch]) + }, []) const createCtx: SmartSelectCreateContext = React.useMemo( () => ({ From 0bf9d646e94886848bd420dd47c90dd912d5e02e Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:46:05 +0600 Subject: [PATCH 13/20] feat: refactor addOption function to update filteredOptions in a single step --- src/components/ui/SmartSelect.stories.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/ui/SmartSelect.stories.tsx b/src/components/ui/SmartSelect.stories.tsx index 2a0ad7e..ba10a7c 100644 --- a/src/components/ui/SmartSelect.stories.tsx +++ b/src/components/ui/SmartSelect.stories.tsx @@ -547,8 +547,11 @@ function SmartSelectCreateCustomSimpleDemo() { ]); const addOption = (opt: SmartSelectOption) => { - setOptions((prev) => [...prev, opt]); - setFilteredOptions((prev) => [...prev, opt]); + setOptions((prev) => { + const updated = [...prev, opt]; + setFilteredOptions(updated); + return updated; + }); setValue(opt.value); }; @@ -659,8 +662,11 @@ function SmartSelectCreateCustomComplexDemo() { ]); const addOption = (opt: SmartSelectOption) => { - setOptions((prev) => [...prev, opt]); - setFilteredOptions((prev) => [...prev, opt]); + setOptions((prev) => { + const updated = [...prev, opt]; + setFilteredOptions(updated); + return updated; + }); setValue(opt.value); }; From 2a83360e75fe6b5bcdc34e050a05035abdf80685 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 00:51:20 +0600 Subject: [PATCH 14/20] feat: add create functionality and custom forms to SmartMultiSelect component --- .../ui/SmartMultiSelect.stories.tsx | 202 +++++++++++ src/components/ui/index.ts | 1 + src/components/ui/smart-multi-select.tsx | 325 ++++++++++++------ 3 files changed, 427 insertions(+), 101 deletions(-) diff --git a/src/components/ui/SmartMultiSelect.stories.tsx b/src/components/ui/SmartMultiSelect.stories.tsx index f119df9..198cb89 100644 --- a/src/components/ui/SmartMultiSelect.stories.tsx +++ b/src/components/ui/SmartMultiSelect.stories.tsx @@ -3,7 +3,9 @@ import type { Meta, StoryObj } from "@storybook/react"; import { SmartMultiSelect, type SmartMultiSelectOption, + type SmartMultiSelectCreateContext, } from "./smart-multi-select"; +import { Input, Label, Button } from "./index"; const allFrameworks: SmartMultiSelectOption[] = [ { value: "next.js", label: "Next.js" }, @@ -358,3 +360,203 @@ function FewStaticOptionsDemo() { export const FewStaticOptions: Story = { render: () => , }; + +// ─── Disabled Search ──────────────────────── + +function DisabledSearchDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const DisabledSearch: Story = { + render: () => , +}; + +// ─── Create with Default Form ──────────────────────── + +function CreateDefaultDemo() { + const [value, setValue] = useState([]); + const [options, setOptions] = useState([ + ...allFrameworks, + ]); + + return ( + { + const newOption = { value: name.toLowerCase(), label: name }; + setOptions((prev) => [...prev, newOption]); + done(newOption.value); + }} + selectOnCreate + /> + ); +} + +export const CreateDefault: Story = { + render: () => , +}; + +// ─── Create with Custom Form ──────────────────────── + +function MultiCreateForm({ + ctx, + onCreated, +}: { + ctx: SmartMultiSelectCreateContext; + onCreated: (option: SmartMultiSelectOption) => void; +}) { + const [name, setName] = useState(ctx.searchValue); + return ( +
+

Create new item

+ setName(e.target.value)} + placeholder="Item name..." + autoFocus + onKeyDown={(e) => e.stopPropagation()} + /> +
+ + +
+
+ ); +} + +function CreateCustomFormDemo() { + const [value, setValue] = useState([]); + const [options, setOptions] = useState([ + ...allFrameworks, + ]); + + const addOption = (opt: SmartMultiSelectOption) => { + setOptions((prev) => [...prev, opt]); + setValue((prev) => [...prev, opt.value]); + }; + + return ( + ( + + )} + /> + ); +} + +export const CreateCustomForm: Story = { + render: () => , +}; + +// ─── Create with Complex Form ──────────────────────── + +function MultiComplexCreateForm({ + ctx, + onCreated, +}: { + ctx: SmartMultiSelectCreateContext; + onCreated: (option: SmartMultiSelectOption) => void; +}) { + const [name, setName] = useState(ctx.searchValue); + const [description, setDescription] = useState(""); + return ( +
+

Create new framework

+
+ + setName(e.target.value)} + placeholder="Framework name..." + autoFocus + onKeyDown={(e) => e.stopPropagation()} + /> +
+
+ + setDescription(e.target.value)} + placeholder="Short description..." + onKeyDown={(e) => e.stopPropagation()} + /> +
+
+ + +
+
+ ); +} + +function CreateComplexFormDemo() { + const [value, setValue] = useState([]); + const [options, setOptions] = useState([ + ...allFrameworks, + ]); + + const addOption = (opt: SmartMultiSelectOption) => { + setOptions((prev) => [...prev, opt]); + setValue((prev) => [...prev, opt.value]); + }; + + return ( + ( + + )} + /> + ); +} + +export const CreateComplexForm: Story = { + render: () => , +}; diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index ad49472..cee164b 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -102,6 +102,7 @@ export { type SmartMultiSelectOption, type SmartMultiSelectProps, type SmartMultiSelectRef, + type SmartMultiSelectCreateContext, } from "./smart-multi-select"; // SmartSelect component diff --git a/src/components/ui/smart-multi-select.tsx b/src/components/ui/smart-multi-select.tsx index d28e1a6..c24199f 100644 --- a/src/components/ui/smart-multi-select.tsx +++ b/src/components/ui/smart-multi-select.tsx @@ -1,8 +1,9 @@ "use client" import * as React from "react" -import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, XIcon } from "lucide-react" +import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, PlusIcon, XIcon } from "lucide-react" import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" import { Command, CommandEmpty, @@ -26,6 +27,15 @@ export interface SmartMultiSelectOption { value: string } +export interface SmartMultiSelectCreateContext { + /** Current search/input value */ + searchValue: string + /** Clear the search input and close the create form */ + clearSearch: () => void + /** Close the popover */ + close: () => void +} + export interface SmartMultiSelectProps { /** An array of options to display */ options: SmartMultiSelectOption[] @@ -63,6 +73,8 @@ export interface SmartMultiSelectProps { disabled?: boolean /** Mark combobox as invalid */ invalid?: boolean + /** Whether to disable the search input */ + disableSearch?: boolean /** Custom label function for rendering each option */ labelFunc?: ( option: SmartMultiSelectOption, @@ -73,6 +85,20 @@ export interface SmartMultiSelectProps { onValueChange: (value: string[]) => void /** Callback when search input changes */ onSearch?: (value: string) => void + /** + * Render function for the create form. Receives a context with searchValue, + * clearSearch, and close helpers. Return any React node. + */ + renderCreateForm?: (ctx: SmartMultiSelectCreateContext) => React.ReactNode + /** Whether to auto-select the newly created item @default false */ + selectOnCreate?: boolean + /** + * Callback fired after a new item is created via the default create form. + * Receives the name and a done callback to call when finished. + */ + onCreate?: (name: string, done: (createdValue?: string) => void) => void + /** Text for the default create button @default "Create" */ + createButtonText?: string } export interface SmartMultiSelectRef { @@ -106,9 +132,14 @@ const SmartMultiSelect = React.forwardRef< searchValue, disabled = false, invalid = false, + disableSearch = false, labelFunc, onValueChange, onSearch, + renderCreateForm, + selectOnCreate = false, + onCreate, + createButtonText = "Create", }, ref ) => { @@ -118,13 +149,15 @@ const SmartMultiSelect = React.forwardRef< const [searchValueState, setSearchValueState] = React.useState( searchValue || "" ) - // Cache options for async mode so selected labels remain visible after search changes + const [showCreate, setShowCreate] = React.useState(false) const [reserveOptions, setReserveOptions] = React.useState< Record >({}) const optionsRef = React.useRef>({}) const isInit = React.useRef(false) + const hasCreateCapability = !!(renderCreateForm || onCreate) + const handleInputKeyDown = ( event: React.KeyboardEvent ) => { @@ -168,7 +201,37 @@ const SmartMultiSelect = React.forwardRef< } } - // Cache options for async mode so selected item labels persist across searches + const clearSearch = React.useCallback(() => { + setSearchValueState("") + setShowCreate(false) + }, []) + + const createCtx: SmartMultiSelectCreateContext = React.useMemo( + () => ({ + searchValue: searchValueState, + clearSearch, + close: () => setIsPopoverOpen(false), + }), + [searchValueState, clearSearch] + ) + + const handleDefaultCreate = React.useCallback(() => { + if (!onCreate || !searchValueState.trim()) return + onCreate(searchValueState.trim(), (createdValue?: string) => { + if (selectOnCreate && createdValue) { + const newSelected = [...selectedValues, createdValue] + setSelectedValues(newSelected) + onValueChange(newSelected) + } + clearSearch() + }) + }, [onCreate, searchValueState, selectOnCreate, selectedValues, onValueChange, clearSearch]) + + const showCreateForm = + hasCreateCapability && + (showCreate || (searchValueState && !loading && options.length === 0)) + + // Cache options for async mode React.useEffect(() => { const temp = options.reduce( (acc, option) => { @@ -223,15 +286,46 @@ const SmartMultiSelect = React.forwardRef< return options.find((o) => o.value === val)?.label ?? val } + const renderDefaultCreateForm = () => ( +
+
+ setSearchValueState(e.target.value)} + placeholder="Enter name..." + autoFocus + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault() + handleDefaultCreate() + } + e.stopPropagation() + }} + /> + +
+
+ ) + return ( { if (disabled) return setIsPopoverOpen(open) - if (!open && clearSearchOnClose) { - setSearchValueState("") - onSearch?.("") + if (!open) { + setShowCreate(false) + if (clearSearchOnClose) { + setSearchValueState("") + onSearch?.("") + } } }} > @@ -329,108 +423,137 @@ const SmartMultiSelect = React.forwardRef< align="start" > - { - setSearchValueState(val) - onSearch?.(val) - }} - onKeyDown={handleInputKeyDown} - /> - - {async && error && ( -
- {error.message} -
- )} - {async && loading && options.length === 0 && ( -
- - - Searching... - -
- )} - {async ? ( - !loading && - !error && - options.length === 0 && ( -
- No results found. -
- ) - ) : ( - No results found. - )} - - {!async && !hideSelectAll && ( - + { + setSearchValueState(val) + setShowCreate(false) + onSearch?.(val) + }} + onKeyDown={handleInputKeyDown} + /> + {hasCreateCapability && ( + )} - {options.map((option, index) => { - const isSelected = selectedValues.includes(option.value) - return ( - toggleOption(option.value)} - className="cursor-pointer" - > -
- +
+ )} + + {!showCreateForm && ( + <> + {async && error && ( +
+ {error.message} +
+ )} + {async && loading && options.length === 0 && ( +
+ + + Searching... + +
+ )} + {async ? ( + !loading && + !error && + options.length === 0 && + !hasCreateCapability && ( +
+ No results found.
- {labelFunc - ? labelFunc(option, isSelected, index) - : option.label} -
- ) - })} -
- - -
- {selectedValues.length > 0 && ( - <> + ) + ) : ( + No results found. + )} + + {!async && !hideSelectAll && options.length > 0 && ( - {clearText} +
+ +
+ Select all
-
- - )} - setIsPopoverOpen(false)} - className="flex-1 justify-center cursor-pointer max-w-full" - > - {closeText} - -
-
+ )} + {options.map((option, index) => { + const isSelected = selectedValues.includes(option.value) + return ( + toggleOption(option.value)} + className="cursor-pointer" + > +
+ +
+ {labelFunc + ? labelFunc(option, isSelected, index) + : option.label} +
+ ) + })} + + {options.length > 0 && } + +
+ {selectedValues.length > 0 && ( + <> + + {clearText} + +
+ + )} + setIsPopoverOpen(false)} + className="flex-1 justify-center cursor-pointer max-w-full" + > + {closeText} + +
+ + + )} + {showCreateForm && ( + renderCreateForm + ? renderCreateForm(createCtx) + : renderDefaultCreateForm() + )} From a76b96f94081c56c0da3c90f01ecb8d10c992165 Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:09:35 +0600 Subject: [PATCH 15/20] feat: replace ChevronsUpDownIcon with ChevronDownIcon in SmartMultiSelect and SmartSelect components --- .../ui/SmartMultiSelect.stories.tsx | 62 ++++++++++ src/components/ui/SmartSelect.stories.tsx | 109 ++++++++++++++++++ src/components/ui/smart-multi-select.tsx | 35 +++++- src/components/ui/smart-select.tsx | 27 ++++- 4 files changed, 222 insertions(+), 11 deletions(-) diff --git a/src/components/ui/SmartMultiSelect.stories.tsx b/src/components/ui/SmartMultiSelect.stories.tsx index 198cb89..010889f 100644 --- a/src/components/ui/SmartMultiSelect.stories.tsx +++ b/src/components/ui/SmartMultiSelect.stories.tsx @@ -6,6 +6,7 @@ import { type SmartMultiSelectCreateContext, } from "./smart-multi-select"; import { Input, Label, Button } from "./index"; +import { UserIcon } from "lucide-react"; const allFrameworks: SmartMultiSelectOption[] = [ { value: "next.js", label: "Next.js" }, @@ -560,3 +561,64 @@ function CreateComplexFormDemo() { export const CreateComplexForm: Story = { render: () => , }; + +// ─── With Start Icon ──────────────────────── + +function WithStartIconDemo() { + const [value, setValue] = useState([]); + + return ( + } + /> + ); +} + +export const WithStartIcon: Story = { + render: () => , +}; + +// ─── No Chevron ──────────────────────── + +function NoChevronDemo() { + const [value, setValue] = useState([]); + + return ( + + ); +} + +export const NoChevron: Story = { + render: () => , +}; + +// ─── Start Icon + No End Icon ──────────────────────── + +function StartIconNoEndDemo() { + const [value, setValue] = useState([]); + + return ( + } + endIcon={null} + /> + ); +} + +export const StartIconNoEndIcon: Story = { + render: () => , +}; diff --git a/src/components/ui/SmartSelect.stories.tsx b/src/components/ui/SmartSelect.stories.tsx index ba10a7c..2209568 100644 --- a/src/components/ui/SmartSelect.stories.tsx +++ b/src/components/ui/SmartSelect.stories.tsx @@ -6,6 +6,7 @@ import { type SmartSelectCreateContext, } from "./smart-select"; import { Input, Label, Button } from "./index"; +import { SearchIcon, UserIcon } from "lucide-react"; const allFrameworks: SmartSelectOption[] = [ { value: "next.js", label: "Next.js" }, @@ -701,3 +702,111 @@ function SmartSelectCreateCustomComplexDemo() { export const CreateCustomComplex: Story = { render: () => , }; + +// ─── With Start Icon ──────────────────────── + +function SmartSelectStartIconDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState(allFrameworks); + + return ( + { + if (!query) { setOptions(allFrameworks); return; } + setOptions(allFrameworks.filter((f) => f.label.toLowerCase().includes(query.toLowerCase()))); + }} + options={options} + value={value} + onValueChange={setValue} + placeholder="Guest Customer" + idleMessage="All customers" + startIcon={} + showClear + /> + ); +} + +export const WithStartIcon: Story = { + render: () => , +}; + +// ─── Custom End Icon ──────────────────────── + +function SmartSelectCustomEndIconDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState(allFrameworks); + + return ( + { + if (!query) { setOptions(allFrameworks); return; } + setOptions(allFrameworks.filter((f) => f.label.toLowerCase().includes(query.toLowerCase()))); + }} + options={options} + value={value} + onValueChange={setValue} + placeholder="Search..." + idleMessage="All frameworks" + endIcon={} + /> + ); +} + +export const CustomEndIcon: Story = { + render: () => , +}; + +// ─── No Chevron ──────────────────────── + +function SmartSelectNoChevronDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState(allFrameworks); + + return ( + { + if (!query) { setOptions(allFrameworks); return; } + setOptions(allFrameworks.filter((f) => f.label.toLowerCase().includes(query.toLowerCase()))); + }} + options={options} + value={value} + onValueChange={setValue} + placeholder="Select framework..." + idleMessage="All frameworks" + showChevron={false} + showClear + /> + ); +} + +export const NoChevron: Story = { + render: () => , +}; + +// ─── Start Icon + No End Icon ──────────────────────── + +function SmartSelectStartIconNoEndDemo() { + const [value, setValue] = useState(""); + const [options, setOptions] = useState(allFrameworks); + + return ( + { + if (!query) { setOptions(allFrameworks); return; } + setOptions(allFrameworks.filter((f) => f.label.toLowerCase().includes(query.toLowerCase()))); + }} + options={options} + value={value} + onValueChange={setValue} + placeholder="Guest Customer" + idleMessage="All customers" + startIcon={} + endIcon={null} + showClear + /> + ); +} + +export const StartIconNoEndIcon: Story = { + render: () => , +}; diff --git a/src/components/ui/smart-multi-select.tsx b/src/components/ui/smart-multi-select.tsx index c24199f..7be7789 100644 --- a/src/components/ui/smart-multi-select.tsx +++ b/src/components/ui/smart-multi-select.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, PlusIcon, XIcon } from "lucide-react" +import { CheckIcon, ChevronDownIcon, Loader2Icon, PlusIcon, XIcon } from "lucide-react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -99,6 +99,12 @@ export interface SmartMultiSelectProps { onCreate?: (name: string, done: (createdValue?: string) => void) => void /** Text for the default create button @default "Create" */ createButtonText?: string + /** Icon to show at the start of the trigger (e.g. a person icon) */ + startIcon?: React.ReactNode + /** Custom icon to replace the default chevron. Set to `null` to hide */ + endIcon?: React.ReactNode + /** Whether to show the chevron arrow @default true */ + showChevron?: boolean } export interface SmartMultiSelectRef { @@ -140,6 +146,9 @@ const SmartMultiSelect = React.forwardRef< selectOnCreate = false, onCreate, createButtonText = "Create", + startIcon, + endIcon, + showChevron = true, }, ref ) => { @@ -348,6 +357,9 @@ const SmartMultiSelect = React.forwardRef< > {selectedValues.length > 0 ? (
+ {startIcon && ( + {startIcon} + )}
{selectedValues.slice(0, maxCount).map((val) => ( -
- + {(showChevron || endIcon !== undefined) && ( + <> +
+ {endIcon !== undefined + ? endIcon && {endIcon} + : showChevron && + } + + )}
) : (
- + {startIcon && ( + {startIcon} + )} + {placeholder} - + {endIcon !== undefined + ? endIcon && {endIcon} + : showChevron && + }
)} diff --git a/src/components/ui/smart-select.tsx b/src/components/ui/smart-select.tsx index 3647c0f..cfeaeb9 100644 --- a/src/components/ui/smart-select.tsx +++ b/src/components/ui/smart-select.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { CheckIcon, ChevronsUpDownIcon, Loader2Icon, PlusIcon, XIcon } from "lucide-react" +import { CheckIcon, ChevronDownIcon, Loader2Icon, PlusIcon, XIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Command, @@ -42,8 +42,8 @@ export interface SmartSelectCreateContext { } export interface SmartSelectProps { - /** Async callback triggered on search input change (after debounce) */ - onSearch: (query: string) => void | Promise + /** Callback triggered on search input change (after debounce). Optional for static options */ + onSearch?: (query: string) => void | Promise /** Options to display in the dropdown */ options: SmartSelectOption[] /** Currently selected value */ @@ -93,6 +93,12 @@ export interface SmartSelectProps { onCreate?: (name: string, done: (createdValue?: string) => void) => void /** Text for the default create button @default "Create" */ createButtonText?: string + /** Icon to show at the start of the trigger (e.g. a person icon) */ + startIcon?: React.ReactNode + /** Custom icon to replace the default chevron. Set to `null` to hide */ + endIcon?: React.ReactNode + /** Whether to show the chevron arrow @default true */ + showChevron?: boolean } function SmartSelect({ @@ -117,6 +123,9 @@ function SmartSelect({ selectOnCreate = false, onCreate, createButtonText = "Create", + startIcon, + endIcon, + showChevron = true, }: SmartSelectProps) { const [open, setOpen] = React.useState(false) const [search, setSearch] = React.useState("") @@ -138,7 +147,7 @@ function SmartSelect({ } timerRef.current = setTimeout(() => { - onSearch(query) + onSearch?.(query) }, debounceMs) }, [onSearch, debounceMs] @@ -322,9 +331,12 @@ function SmartSelect({ /> } > + {startIcon && ( + {startIcon} + )} @@ -345,7 +357,10 @@ function SmartSelect({ Clear )} - + {endIcon !== undefined + ? endIcon && {endIcon} + : showChevron && + }
Date: Fri, 3 Apr 2026 01:28:02 +0600 Subject: [PATCH 16/20] feat: add Babel plugin to inject source code of wrapper functions into Storybook story exports --- .storybook/babel-plugin-story-source.js | 132 ++++++++++++++++++++++++ .storybook/main.ts | 3 + 2 files changed, 135 insertions(+) create mode 100644 .storybook/babel-plugin-story-source.js diff --git a/.storybook/babel-plugin-story-source.js b/.storybook/babel-plugin-story-source.js new file mode 100644 index 0000000..55fad25 --- /dev/null +++ b/.storybook/babel-plugin-story-source.js @@ -0,0 +1,132 @@ +/** + * Babel plugin that extracts wrapper/demo function source code and injects it + * as `parameters.docs.source.code` into Storybook story exports. + * + * Problem: Stories that use `render: () => ` cause Storybook's + * "Show code" panel to display `` instead of the actual + * component usage inside the wrapper. + * + * Solution: At compile time, this plugin: + * 1. Collects all top-level non-exported function declarations (the wrappers). + * 2. For each story export whose render body is ``, it copies + * the wrapper function's original source into + * `parameters.docs.source.code` so Storybook displays it verbatim. + */ +module.exports = function storySourcePlugin({ types: t }) { + return { + name: 'story-source-injector', + visitor: { + Program: { + enter(programPath, state) { + const fileSource = state.file.code; + if (!fileSource) return; + + // ── Step 1: collect wrapper functions ──────────────────────── + const wrapperFunctions = new Map(); + + for (const stmt of programPath.get('body')) { + // Skip any exported declaration + if ( + stmt.isExportNamedDeclaration() || + stmt.isExportDefaultDeclaration() + ) { + continue; + } + + if (stmt.isFunctionDeclaration()) { + const name = stmt.node.id?.name; + if (!name) continue; + + const { start, end } = stmt.node; + if (start == null || end == null) continue; + + wrapperFunctions.set(name, fileSource.slice(start, end)); + } + } + + if (wrapperFunctions.size === 0) return; + + // ── Step 2: inject source into matching story exports ──────── + for (const stmt of programPath.get('body')) { + if (!stmt.isExportNamedDeclaration()) continue; + + const decl = stmt.get('declaration'); + if (!decl.isVariableDeclaration()) continue; + + for (const declarator of decl.get('declarations')) { + let init = declarator.get('init'); + + // Unwrap `{ ... } satisfies Type` or `{ ... } as Type` + if ( + init.isTSSatisfiesExpression?.() || + init.isTSAsExpression?.() + ) { + init = init.get('expression'); + } + + if (!init.isObjectExpression()) continue; + + const properties = init.get('properties'); + + // Find `render` property + const renderProp = properties.find( + (p) => + p.isObjectProperty() && + p.get('key').isIdentifier({ name: 'render' }) + ); + if (!renderProp) continue; + + const renderVal = renderProp.get('value'); + if (!renderVal.isArrowFunctionExpression()) continue; + + const renderBody = renderVal.get('body'); + if (!renderBody.isJSXElement()) continue; + + // Must be a self-closing element: + const opening = renderBody.get('openingElement'); + const nameNode = opening.get('name'); + if (!nameNode.isJSXIdentifier()) continue; + + const wrapperName = nameNode.node.name; + if (!wrapperFunctions.has(wrapperName)) continue; + + // Skip stories that already define their own `parameters` + const hasParams = properties.some( + (p) => + p.isObjectProperty() && + p.get('key').isIdentifier({ name: 'parameters' }) + ); + if (hasParams) continue; + + // Build: parameters: { docs: { source: { code, language } } } + const sourceCode = wrapperFunctions.get(wrapperName); + + const sourceObj = t.objectExpression([ + t.objectProperty( + t.identifier('code'), + t.stringLiteral(sourceCode) + ), + t.objectProperty( + t.identifier('language'), + t.stringLiteral('tsx') + ), + ]); + + const docsObj = t.objectExpression([ + t.objectProperty(t.identifier('source'), sourceObj), + ]); + + const paramsObj = t.objectExpression([ + t.objectProperty(t.identifier('docs'), docsObj), + ]); + + init.node.properties.push( + t.objectProperty(t.identifier('parameters'), paramsObj) + ); + } + } + }, + }, + }, + }; +}; diff --git a/.storybook/main.ts b/.storybook/main.ts index 6728e7e..f3cd9b2 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -91,6 +91,9 @@ const config: StorybookConfig = { const babelLoaderForStories = { loader: require.resolve("babel-loader"), options: { + plugins: [ + require.resolve("./babel-plugin-story-source"), + ], presets: [ [require.resolve("@babel/preset-react"), { runtime: "automatic" }], [require.resolve("@babel/preset-typescript"), { allowDeclareFields: true }], From e43b9af9dc32f619315ae1021c649c35e25c60cf Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:35:34 +0600 Subject: [PATCH 17/20] feat: add Claude theme and dark theme to theme options --- .storybook/preview.js | 2 + src/themes/index.ts | 100 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/.storybook/preview.js b/.storybook/preview.js index 2533481..43b50f5 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -63,6 +63,7 @@ export const globalTypes = { { value: 'cyberpunk', title: 'Cyberpunk' }, { value: 'twitter', title: 'Twitter' }, { value: 'slate', title: 'Slate' }, + { value: 'claude', title: 'Claude' }, ], showName: true, }, @@ -91,6 +92,7 @@ export const decorators = [ 'cyberpunk': { tokens: Themes.cyberpunkTheme, darkTokens: Themes.cyberpunkDarkTheme }, 'twitter': { tokens: Themes.twitterTheme, darkTokens: Themes.twitterDarkTheme }, slate: { tokens: Themes.slateTheme, darkTokens: Themes.slateDarkTheme }, + claude: { tokens: Themes.claudeTheme, darkTokens: Themes.claudeDarkTheme }, }; const activeBrand = themeMap[brand] || themeMap.default; diff --git a/src/themes/index.ts b/src/themes/index.ts index d32239f..62d4f22 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -672,6 +672,106 @@ export const twitterDarkTheme: ThemeTokens = { "shadow-2xl": "0px 2px 0px 0px hsl(202.8169 89.1213% 53.1373% / 0.00)", }; +/** + * Claude theme - Warm terracotta primary with cream backgrounds + */ +export const claudeTheme: ThemeTokens = { + background: "oklch(0.9818 0.0054 95.0986)", + foreground: "oklch(0.3438 0.0269 95.7226)", + card: "oklch(0.9818 0.0054 95.0986)", + cardForeground: "oklch(0.1908 0.0020 106.5859)", + popover: "oklch(1.0000 0 0)", + popoverForeground: "oklch(0.2671 0.0196 98.9390)", + primary: "oklch(0.6171 0.1375 39.0427)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9245 0.0138 92.9892)", + secondaryForeground: "oklch(0.4334 0.0177 98.6048)", + muted: "oklch(0.9341 0.0153 90.2390)", + mutedForeground: "oklch(0.6059 0.0075 97.4233)", + accent: "oklch(0.9245 0.0138 92.9892)", + accentForeground: "oklch(0.2671 0.0196 98.9390)", + destructive: "oklch(0.1908 0.0020 106.5859)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.8847 0.0069 97.3627)", + input: "oklch(0.7621 0.0156 98.3528)", + ring: "oklch(0.6171 0.1375 39.0427)", + chart1: "oklch(0.5583 0.1276 42.9956)", + chart2: "oklch(0.6898 0.1581 290.4107)", + chart3: "oklch(0.8816 0.0276 93.1280)", + chart4: "oklch(0.8822 0.0403 298.1792)", + chart5: "oklch(0.5608 0.1348 42.0584)", + sidebar: "oklch(0.9663 0.0080 98.8792)", + sidebarForeground: "oklch(0.3590 0.0051 106.6524)", + sidebarPrimary: "oklch(0.6171 0.1375 39.0427)", + sidebarPrimaryForeground: "oklch(0.9881 0 0)", + sidebarAccent: "oklch(0.9245 0.0138 92.9892)", + sidebarAccentForeground: "oklch(0.3250 0 0)", + sidebarBorder: "oklch(0.9401 0 0)", + sidebarRing: "oklch(0.7731 0 0)", + fontSans: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'", + fontSerif: "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + fontMono: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace", + radius: "0.5rem", + "shadow-2xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + shadow: "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + "shadow-md": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0 1px 3px 0px hsl(0 0% 0% / 0.25)", +}; + +/** + * Claude dark theme + */ +export const claudeDarkTheme: ThemeTokens = { + background: "oklch(0.2679 0.0036 106.6427)", + foreground: "oklch(0.8074 0.0142 93.0137)", + card: "oklch(0.2679 0.0036 106.6427)", + cardForeground: "oklch(0.9818 0.0054 95.0986)", + popover: "oklch(0.3085 0.0035 106.6039)", + popoverForeground: "oklch(0.9211 0.0040 106.4781)", + primary: "oklch(0.6724 0.1308 38.7559)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9818 0.0054 95.0986)", + secondaryForeground: "oklch(0.3085 0.0035 106.6039)", + muted: "oklch(0.2213 0.0038 106.7070)", + mutedForeground: "oklch(0.7713 0.0169 99.0657)", + accent: "oklch(0.2130 0.0078 95.4245)", + accentForeground: "oklch(0.9663 0.0080 98.8792)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.3618 0.0101 106.8928)", + input: "oklch(0.4336 0.0113 100.2195)", + ring: "oklch(0.6724 0.1308 38.7559)", + chart1: "oklch(0.5583 0.1276 42.9956)", + chart2: "oklch(0.6898 0.1581 290.4107)", + chart3: "oklch(0.2130 0.0078 95.4245)", + chart4: "oklch(0.3074 0.0516 289.3230)", + chart5: "oklch(0.5608 0.1348 42.0584)", + sidebar: "oklch(0.2357 0.0024 67.7077)", + sidebarForeground: "oklch(0.8074 0.0142 93.0137)", + sidebarPrimary: "oklch(0.3250 0 0)", + sidebarPrimaryForeground: "oklch(0.9881 0 0)", + sidebarAccent: "oklch(0.1680 0.0020 106.6177)", + sidebarAccentForeground: "oklch(0.8074 0.0142 93.0137)", + sidebarBorder: "oklch(0.9401 0 0)", + sidebarRing: "oklch(0.7731 0 0)", + fontSans: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'", + fontSerif: "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + fontMono: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace", + radius: "0.5rem", + "shadow-2xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + shadow: "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + "shadow-md": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0 1px 3px 0px hsl(0 0% 0% / 0.25)", +}; + /** * Helper to create a custom theme with defaults filled in */ From 7ca9cf5226745086d40f52396ff85b6d83b08cef Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 01:52:18 +0600 Subject: [PATCH 18/20] Refactor code structure for improved readability and maintainability --- .storybook/preview.js | 20 + src/themes/index.ts | 1000 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1020 insertions(+) diff --git a/.storybook/preview.js b/.storybook/preview.js index 43b50f5..7d7ffac 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -64,6 +64,16 @@ export const globalTypes = { { value: 'twitter', title: 'Twitter' }, { value: 'slate', title: 'Slate' }, { value: 'claude', title: 'Claude' }, + { value: 'claymorphism', title: 'Claymorphism' }, + { value: 'clean-slate', title: 'Clean Slate' }, + { value: 'modern-minimal', title: 'Modern Minimal' }, + { value: 'nature', title: 'Nature' }, + { value: 'neo-brutalism', title: 'Neo Brutalism' }, + { value: 'notebook', title: 'Notebook' }, + { value: 'ocean-breeze', title: 'Ocean Breeze' }, + { value: 'supabase', title: 'Supabase' }, + { value: 'terminal', title: 'Terminal' }, + { value: 'whatsapp', title: 'WhatsApp' }, ], showName: true, }, @@ -93,6 +103,16 @@ export const decorators = [ 'twitter': { tokens: Themes.twitterTheme, darkTokens: Themes.twitterDarkTheme }, slate: { tokens: Themes.slateTheme, darkTokens: Themes.slateDarkTheme }, claude: { tokens: Themes.claudeTheme, darkTokens: Themes.claudeDarkTheme }, + claymorphism: { tokens: Themes.claymorphismTheme, darkTokens: Themes.claymorphismDarkTheme }, + 'clean-slate': { tokens: Themes.cleanSlateTheme, darkTokens: Themes.cleanSlateDarkTheme }, + 'modern-minimal': { tokens: Themes.modernMinimalTheme, darkTokens: Themes.modernMinimalDarkTheme }, + nature: { tokens: Themes.natureTheme, darkTokens: Themes.natureDarkTheme }, + 'neo-brutalism': { tokens: Themes.neoBrutalismTheme, darkTokens: Themes.neoBrutalismDarkTheme }, + notebook: { tokens: Themes.notebookTheme, darkTokens: Themes.notebookDarkTheme }, + 'ocean-breeze': { tokens: Themes.oceanBreezeTheme, darkTokens: Themes.oceanBreezeDarkTheme }, + supabase: { tokens: Themes.supabaseTheme, darkTokens: Themes.supabaseDarkTheme }, + terminal: { tokens: Themes.terminalTheme, darkTokens: Themes.terminalDarkTheme }, + whatsapp: { tokens: Themes.whatsappTheme, darkTokens: Themes.whatsappDarkTheme }, }; const activeBrand = themeMap[brand] || themeMap.default; diff --git a/src/themes/index.ts b/src/themes/index.ts index 62d4f22..648bb1d 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -772,6 +772,1006 @@ export const claudeDarkTheme: ThemeTokens = { "shadow-2xl": "0 1px 3px 0px hsl(0 0% 0% / 0.25)", }; +/** + * Claymorphism theme - Soft clay-like surfaces with purple primary and warm tones + */ +export const claymorphismTheme: ThemeTokens = { + background: "oklch(0.9232 0.0026 48.7171)", + foreground: "oklch(0.2795 0.0368 260.0310)", + card: "oklch(0.9699 0.0013 106.4238)", + cardForeground: "oklch(0.2795 0.0368 260.0310)", + popover: "oklch(0.9699 0.0013 106.4238)", + popoverForeground: "oklch(0.2795 0.0368 260.0310)", + primary: "oklch(0.5854 0.2041 277.1173)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.8687 0.0043 56.3660)", + secondaryForeground: "oklch(0.4461 0.0263 256.8018)", + muted: "oklch(0.9232 0.0026 48.7171)", + mutedForeground: "oklch(0.5510 0.0234 264.3637)", + accent: "oklch(0.9376 0.0260 321.9388)", + accentForeground: "oklch(0.3729 0.0306 259.7328)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.8687 0.0043 56.3660)", + input: "oklch(0.8687 0.0043 56.3660)", + ring: "oklch(0.5854 0.2041 277.1173)", + chart1: "oklch(0.5854 0.2041 277.1173)", + chart2: "oklch(0.5106 0.2301 276.9656)", + chart3: "oklch(0.4568 0.2146 277.0229)", + chart4: "oklch(0.3984 0.1773 277.3662)", + chart5: "oklch(0.3588 0.1354 278.6973)", + sidebar: "oklch(0.8687 0.0043 56.3660)", + sidebarForeground: "oklch(0.2795 0.0368 260.0310)", + sidebarPrimary: "oklch(0.5854 0.2041 277.1173)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.9376 0.0260 321.9388)", + sidebarAccentForeground: "oklch(0.3729 0.0306 259.7328)", + sidebarBorder: "oklch(0.8687 0.0043 56.3660)", + sidebarRing: "oklch(0.5854 0.2041 277.1173)", + fontSans: "Plus Jakarta Sans, sans-serif", + fontSerif: "Lora, serif", + fontMono: "Roboto Mono, monospace", + radius: "1.25rem", + "shadow-2xs": "2px 2px 10px 4px hsl(240 4% 60% / 0.09)", + "shadow-xs": "2px 2px 10px 4px hsl(240 4% 60% / 0.09)", + "shadow-sm": "2px 2px 10px 4px hsl(240 4% 60% / 0.18), 2px 1px 2px 3px hsl(240 4% 60% / 0.18)", + shadow: "2px 2px 10px 4px hsl(240 4% 60% / 0.18), 2px 1px 2px 3px hsl(240 4% 60% / 0.18)", + "shadow-md": "2px 2px 10px 4px hsl(240 4% 60% / 0.18), 2px 2px 4px 3px hsl(240 4% 60% / 0.18)", + "shadow-lg": "2px 2px 10px 4px hsl(240 4% 60% / 0.18), 2px 4px 6px 3px hsl(240 4% 60% / 0.18)", + "shadow-xl": "2px 2px 10px 4px hsl(240 4% 60% / 0.18), 2px 8px 10px 3px hsl(240 4% 60% / 0.18)", + "shadow-2xl": "2px 2px 10px 4px hsl(240 4% 60% / 0.45)", +}; + +/** + * Claymorphism dark theme + */ +export const claymorphismDarkTheme: ThemeTokens = { + background: "oklch(0.2244 0.0074 67.4370)", + foreground: "oklch(0.9288 0.0126 255.5078)", + card: "oklch(0.2801 0.0080 59.3379)", + cardForeground: "oklch(0.9288 0.0126 255.5078)", + popover: "oklch(0.2801 0.0080 59.3379)", + popoverForeground: "oklch(0.9288 0.0126 255.5078)", + primary: "oklch(0.6801 0.1583 276.9349)", + primaryForeground: "oklch(0.2244 0.0074 67.4370)", + secondary: "oklch(0.3359 0.0077 59.4197)", + secondaryForeground: "oklch(0.8717 0.0093 258.3382)", + muted: "oklch(0.2287 0.0074 67.4469)", + mutedForeground: "oklch(0.7137 0.0192 261.3246)", + accent: "oklch(0.3896 0.0074 59.4734)", + accentForeground: "oklch(0.8717 0.0093 258.3382)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(0.2244 0.0074 67.4370)", + border: "oklch(0.3359 0.0077 59.4197)", + input: "oklch(0.3359 0.0077 59.4197)", + ring: "oklch(0.6801 0.1583 276.9349)", + chart1: "oklch(0.6801 0.1583 276.9349)", + chart2: "oklch(0.5854 0.2041 277.1173)", + chart3: "oklch(0.5106 0.2301 276.9656)", + chart4: "oklch(0.4568 0.2146 277.0229)", + chart5: "oklch(0.3984 0.1773 277.3662)", + sidebar: "oklch(0.3359 0.0077 59.4197)", + sidebarForeground: "oklch(0.9288 0.0126 255.5078)", + sidebarPrimary: "oklch(0.6801 0.1583 276.9349)", + sidebarPrimaryForeground: "oklch(0.2244 0.0074 67.4370)", + sidebarAccent: "oklch(0.3896 0.0074 59.4734)", + sidebarAccentForeground: "oklch(0.8717 0.0093 258.3382)", + sidebarBorder: "oklch(0.3359 0.0077 59.4197)", + sidebarRing: "oklch(0.6801 0.1583 276.9349)", + fontSans: "Plus Jakarta Sans, sans-serif", + fontSerif: "Lora, serif", + fontMono: "Roboto Mono, monospace", + radius: "1.25rem", + "shadow-2xs": "2px 2px 10px 4px hsl(0 0% 0% / 0.09)", + "shadow-xs": "2px 2px 10px 4px hsl(0 0% 0% / 0.09)", + "shadow-sm": "2px 2px 10px 4px hsl(0 0% 0% / 0.18), 2px 1px 2px 3px hsl(0 0% 0% / 0.18)", + shadow: "2px 2px 10px 4px hsl(0 0% 0% / 0.18), 2px 1px 2px 3px hsl(0 0% 0% / 0.18)", + "shadow-md": "2px 2px 10px 4px hsl(0 0% 0% / 0.18), 2px 2px 4px 3px hsl(0 0% 0% / 0.18)", + "shadow-lg": "2px 2px 10px 4px hsl(0 0% 0% / 0.18), 2px 4px 6px 3px hsl(0 0% 0% / 0.18)", + "shadow-xl": "2px 2px 10px 4px hsl(0 0% 0% / 0.18), 2px 8px 10px 3px hsl(0 0% 0% / 0.18)", + "shadow-2xl": "2px 2px 10px 4px hsl(0 0% 0% / 0.45)", +}; + +/** + * Clean Slate theme - Crisp purple-violet primary on cool blue-grey surfaces + */ +export const cleanSlateTheme: ThemeTokens = { + background: "oklch(0.9842 0.0034 247.8575)", + foreground: "oklch(0.2795 0.0368 260.0310)", + card: "oklch(1.0000 0 0)", + cardForeground: "oklch(0.2795 0.0368 260.0310)", + popover: "oklch(1.0000 0 0)", + popoverForeground: "oklch(0.2795 0.0368 260.0310)", + primary: "oklch(0.5854 0.2041 277.1173)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9276 0.0058 264.5313)", + secondaryForeground: "oklch(0.3729 0.0306 259.7328)", + muted: "oklch(0.9670 0.0029 264.5419)", + mutedForeground: "oklch(0.5510 0.0234 264.3637)", + accent: "oklch(0.9299 0.0334 272.7879)", + accentForeground: "oklch(0.3729 0.0306 259.7328)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.8717 0.0093 258.3382)", + input: "oklch(0.8717 0.0093 258.3382)", + ring: "oklch(0.5854 0.2041 277.1173)", + chart1: "oklch(0.5854 0.2041 277.1173)", + chart2: "oklch(0.5106 0.2301 276.9656)", + chart3: "oklch(0.4568 0.2146 277.0229)", + chart4: "oklch(0.3984 0.1773 277.3662)", + chart5: "oklch(0.3588 0.1354 278.6973)", + sidebar: "oklch(0.9670 0.0029 264.5419)", + sidebarForeground: "oklch(0.2795 0.0368 260.0310)", + sidebarPrimary: "oklch(0.5854 0.2041 277.1173)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.9299 0.0334 272.7879)", + sidebarAccentForeground: "oklch(0.3729 0.0306 259.7328)", + sidebarBorder: "oklch(0.8717 0.0093 258.3382)", + sidebarRing: "oklch(0.5854 0.2041 277.1173)", + fontSans: "Inter, sans-serif", + fontSerif: "Merriweather, serif", + fontMono: "JetBrains Mono, monospace", + radius: "0.5rem", + "shadow-2xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + shadow: "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + "shadow-md": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.25)", +}; + +/** + * Clean Slate dark theme + */ +export const cleanSlateDarkTheme: ThemeTokens = { + background: "oklch(0.2077 0.0398 265.7549)", + foreground: "oklch(0.9288 0.0126 255.5078)", + card: "oklch(0.2795 0.0368 260.0310)", + cardForeground: "oklch(0.9288 0.0126 255.5078)", + popover: "oklch(0.2795 0.0368 260.0310)", + popoverForeground: "oklch(0.9288 0.0126 255.5078)", + primary: "oklch(0.6801 0.1583 276.9349)", + primaryForeground: "oklch(0.2077 0.0398 265.7549)", + secondary: "oklch(0.3351 0.0331 260.9120)", + secondaryForeground: "oklch(0.8717 0.0093 258.3382)", + muted: "oklch(0.2427 0.0381 259.9437)", + mutedForeground: "oklch(0.7137 0.0192 261.3246)", + accent: "oklch(0.3729 0.0306 259.7328)", + accentForeground: "oklch(0.8717 0.0093 258.3382)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(0.2077 0.0398 265.7549)", + border: "oklch(0.4461 0.0263 256.8018)", + input: "oklch(0.4461 0.0263 256.8018)", + ring: "oklch(0.6801 0.1583 276.9349)", + chart1: "oklch(0.6801 0.1583 276.9349)", + chart2: "oklch(0.5854 0.2041 277.1173)", + chart3: "oklch(0.5106 0.2301 276.9656)", + chart4: "oklch(0.4568 0.2146 277.0229)", + chart5: "oklch(0.3984 0.1773 277.3662)", + sidebar: "oklch(0.2795 0.0368 260.0310)", + sidebarForeground: "oklch(0.9288 0.0126 255.5078)", + sidebarPrimary: "oklch(0.6801 0.1583 276.9349)", + sidebarPrimaryForeground: "oklch(0.2077 0.0398 265.7549)", + sidebarAccent: "oklch(0.3729 0.0306 259.7328)", + sidebarAccentForeground: "oklch(0.8717 0.0093 258.3382)", + sidebarBorder: "oklch(0.4461 0.0263 256.8018)", + sidebarRing: "oklch(0.6801 0.1583 276.9349)", + fontSans: "Inter, sans-serif", + fontSerif: "Merriweather, serif", + fontMono: "JetBrains Mono, monospace", + radius: "0.5rem", + "shadow-2xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + shadow: "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + "shadow-md": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.25)", +}; + +/** + * Modern Minimal theme - Clean blue-violet primary on pure white with tight radius + */ +export const modernMinimalTheme: ThemeTokens = { + background: "oklch(1.0000 0 0)", + foreground: "oklch(0.3211 0 0)", + card: "oklch(1.0000 0 0)", + cardForeground: "oklch(0.3211 0 0)", + popover: "oklch(1.0000 0 0)", + popoverForeground: "oklch(0.3211 0 0)", + primary: "oklch(0.6231 0.1880 259.8145)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9670 0.0029 264.5419)", + secondaryForeground: "oklch(0.4461 0.0263 256.8018)", + muted: "oklch(0.9846 0.0017 247.8389)", + mutedForeground: "oklch(0.5510 0.0234 264.3637)", + accent: "oklch(0.9514 0.0250 236.8242)", + accentForeground: "oklch(0.3791 0.1378 265.5222)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.9276 0.0058 264.5313)", + input: "oklch(0.9276 0.0058 264.5313)", + ring: "oklch(0.6231 0.1880 259.8145)", + chart1: "oklch(0.6231 0.1880 259.8145)", + chart2: "oklch(0.5461 0.2152 262.8809)", + chart3: "oklch(0.4882 0.2172 264.3763)", + chart4: "oklch(0.4244 0.1809 265.6377)", + chart5: "oklch(0.3791 0.1378 265.5222)", + sidebar: "oklch(0.9846 0.0017 247.8389)", + sidebarForeground: "oklch(0.3211 0 0)", + sidebarPrimary: "oklch(0.6231 0.1880 259.8145)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.9514 0.0250 236.8242)", + sidebarAccentForeground: "oklch(0.3791 0.1378 265.5222)", + sidebarBorder: "oklch(0.9276 0.0058 264.5313)", + sidebarRing: "oklch(0.6231 0.1880 259.8145)", + fontSans: "Inter, sans-serif", + fontSerif: "Source Serif 4, serif", + fontMono: "JetBrains Mono, monospace", + radius: "0.375rem", + "shadow-2xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + shadow: "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + "shadow-md": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0 1px 3px 0px hsl(0 0% 0% / 0.25)", +}; + +/** + * Modern Minimal dark theme + */ +export const modernMinimalDarkTheme: ThemeTokens = { + background: "oklch(0.2046 0 0)", + foreground: "oklch(0.9219 0 0)", + card: "oklch(0.2686 0 0)", + cardForeground: "oklch(0.9219 0 0)", + popover: "oklch(0.2686 0 0)", + popoverForeground: "oklch(0.9219 0 0)", + primary: "oklch(0.6231 0.1880 259.8145)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.2686 0 0)", + secondaryForeground: "oklch(0.9219 0 0)", + muted: "oklch(0.2393 0 0)", + mutedForeground: "oklch(0.7155 0 0)", + accent: "oklch(0.3791 0.1378 265.5222)", + accentForeground: "oklch(0.8823 0.0571 254.1284)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.3715 0 0)", + input: "oklch(0.3715 0 0)", + ring: "oklch(0.6231 0.1880 259.8145)", + chart1: "oklch(0.7137 0.1434 254.6240)", + chart2: "oklch(0.6231 0.1880 259.8145)", + chart3: "oklch(0.5461 0.2152 262.8809)", + chart4: "oklch(0.4882 0.2172 264.3763)", + chart5: "oklch(0.4244 0.1809 265.6377)", + sidebar: "oklch(0.2046 0 0)", + sidebarForeground: "oklch(0.9219 0 0)", + sidebarPrimary: "oklch(0.6231 0.1880 259.8145)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.3791 0.1378 265.5222)", + sidebarAccentForeground: "oklch(0.8823 0.0571 254.1284)", + sidebarBorder: "oklch(0.3715 0 0)", + sidebarRing: "oklch(0.6231 0.1880 259.8145)", + fontSans: "Inter, sans-serif", + fontSerif: "Source Serif 4, serif", + fontMono: "JetBrains Mono, monospace", + radius: "0.375rem", + "shadow-2xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + shadow: "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + "shadow-md": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0 1px 3px 0px hsl(0 0% 0% / 0.25)", +}; + +/** + * Nature theme - Earthy green primary on warm parchment backgrounds + */ +export const natureTheme: ThemeTokens = { + background: "oklch(0.9711 0.0074 80.7211)", + foreground: "oklch(0.3000 0.0358 30.2042)", + card: "oklch(0.9711 0.0074 80.7211)", + cardForeground: "oklch(0.3000 0.0358 30.2042)", + popover: "oklch(0.9711 0.0074 80.7211)", + popoverForeground: "oklch(0.3000 0.0358 30.2042)", + primary: "oklch(0.5234 0.1347 144.1672)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9571 0.0210 147.6360)", + secondaryForeground: "oklch(0.4254 0.1159 144.3078)", + muted: "oklch(0.9370 0.0142 74.4218)", + mutedForeground: "oklch(0.4495 0.0486 39.2110)", + accent: "oklch(0.8952 0.0504 146.0366)", + accentForeground: "oklch(0.4254 0.1159 144.3078)", + destructive: "oklch(0.5386 0.1937 26.7249)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.8805 0.0208 74.6428)", + input: "oklch(0.8805 0.0208 74.6428)", + ring: "oklch(0.5234 0.1347 144.1672)", + chart1: "oklch(0.6731 0.1624 144.2083)", + chart2: "oklch(0.5752 0.1446 144.1813)", + chart3: "oklch(0.5234 0.1347 144.1672)", + chart4: "oklch(0.4254 0.1159 144.3078)", + chart5: "oklch(0.2157 0.0453 145.7256)", + sidebar: "oklch(0.9370 0.0142 74.4218)", + sidebarForeground: "oklch(0.3000 0.0358 30.2042)", + sidebarPrimary: "oklch(0.5234 0.1347 144.1672)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.8952 0.0504 146.0366)", + sidebarAccentForeground: "oklch(0.4254 0.1159 144.3078)", + sidebarBorder: "oklch(0.8805 0.0208 74.6428)", + sidebarRing: "oklch(0.5234 0.1347 144.1672)", + fontSans: "Montserrat, sans-serif", + fontSerif: "Merriweather, serif", + fontMono: "Source Code Pro, monospace", + radius: "0.5rem", + "shadow-2xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + shadow: "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + "shadow-md": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0 1px 3px 0px hsl(0 0% 0% / 0.25)", +}; + +/** + * Nature dark theme + */ +export const natureDarkTheme: ThemeTokens = { + background: "oklch(0.2683 0.0279 150.7681)", + foreground: "oklch(0.9423 0.0097 72.6595)", + card: "oklch(0.3327 0.0271 146.9867)", + cardForeground: "oklch(0.9423 0.0097 72.6595)", + popover: "oklch(0.3327 0.0271 146.9867)", + popoverForeground: "oklch(0.9423 0.0097 72.6595)", + primary: "oklch(0.6731 0.1624 144.2083)", + primaryForeground: "oklch(0.2157 0.0453 145.7256)", + secondary: "oklch(0.3942 0.0265 142.9926)", + secondaryForeground: "oklch(0.8970 0.0166 142.5518)", + muted: "oklch(0.2926 0.0212 147.7496)", + mutedForeground: "oklch(0.8579 0.0174 76.0955)", + accent: "oklch(0.5752 0.1446 144.1813)", + accentForeground: "oklch(0.9423 0.0097 72.6595)", + destructive: "oklch(0.5386 0.1937 26.7249)", + destructiveForeground: "oklch(0.9423 0.0097 72.6595)", + border: "oklch(0.3942 0.0265 142.9926)", + input: "oklch(0.3942 0.0265 142.9926)", + ring: "oklch(0.6731 0.1624 144.2083)", + chart1: "oklch(0.7660 0.1179 145.2950)", + chart2: "oklch(0.7185 0.1417 144.8887)", + chart3: "oklch(0.6731 0.1624 144.2083)", + chart4: "oklch(0.6291 0.1543 144.2031)", + chart5: "oklch(0.5752 0.1446 144.1813)", + sidebar: "oklch(0.2683 0.0279 150.7681)", + sidebarForeground: "oklch(0.9423 0.0097 72.6595)", + sidebarPrimary: "oklch(0.6731 0.1624 144.2083)", + sidebarPrimaryForeground: "oklch(0.2157 0.0453 145.7256)", + sidebarAccent: "oklch(0.5752 0.1446 144.1813)", + sidebarAccentForeground: "oklch(0.9423 0.0097 72.6595)", + sidebarBorder: "oklch(0.3942 0.0265 142.9926)", + sidebarRing: "oklch(0.6731 0.1624 144.2083)", + fontSans: "Montserrat, sans-serif", + fontSerif: "Merriweather, serif", + fontMono: "Source Code Pro, monospace", + radius: "0.5rem", + "shadow-2xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0 1px 3px 0px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + shadow: "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10)", + "shadow-md": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0 1px 3px 0px hsl(0 0% 0% / 0.25)", +}; + +/** + * Neo Brutalism theme - Bold flat colors, hard black borders, zero radius, offset shadows + */ +export const neoBrutalismTheme: ThemeTokens = { + background: "oklch(1.0000 0 0)", + foreground: "oklch(0 0 0)", + card: "oklch(1.0000 0 0)", + cardForeground: "oklch(0 0 0)", + popover: "oklch(1.0000 0 0)", + popoverForeground: "oklch(0 0 0)", + primary: "oklch(0.6489 0.2370 26.9728)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9680 0.2110 109.7692)", + secondaryForeground: "oklch(0 0 0)", + muted: "oklch(0.9551 0 0)", + mutedForeground: "oklch(0.3211 0 0)", + accent: "oklch(0.5635 0.2408 260.8178)", + accentForeground: "oklch(1.0000 0 0)", + destructive: "oklch(0 0 0)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0 0 0)", + input: "oklch(0 0 0)", + ring: "oklch(0.6489 0.2370 26.9728)", + chart1: "oklch(0.6489 0.2370 26.9728)", + chart2: "oklch(0.9680 0.2110 109.7692)", + chart3: "oklch(0.5635 0.2408 260.8178)", + chart4: "oklch(0.7323 0.2492 142.4953)", + chart5: "oklch(0.5931 0.2726 328.3634)", + sidebar: "oklch(0.9551 0 0)", + sidebarForeground: "oklch(0 0 0)", + sidebarPrimary: "oklch(0.6489 0.2370 26.9728)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.5635 0.2408 260.8178)", + sidebarAccentForeground: "oklch(1.0000 0 0)", + sidebarBorder: "oklch(0 0 0)", + sidebarRing: "oklch(0.6489 0.2370 26.9728)", + fontSans: "DM Sans, sans-serif", + fontSerif: "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + fontMono: "Space Mono, monospace", + radius: "0px", + "shadow-2xs": "4px 4px 0px 0px hsl(0 0% 0% / 0.50)", + "shadow-xs": "4px 4px 0px 0px hsl(0 0% 0% / 0.50)", + "shadow-sm": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00)", + shadow: "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00)", + "shadow-md": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00)", + "shadow-lg": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00)", + "shadow-xl": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00)", + "shadow-2xl": "4px 4px 0px 0px hsl(0 0% 0% / 2.50)", +}; + +/** + * Neo Brutalism dark theme + */ +export const neoBrutalismDarkTheme: ThemeTokens = { + background: "oklch(0 0 0)", + foreground: "oklch(1.0000 0 0)", + card: "oklch(0.3211 0 0)", + cardForeground: "oklch(1.0000 0 0)", + popover: "oklch(0.3211 0 0)", + popoverForeground: "oklch(1.0000 0 0)", + primary: "oklch(0.7044 0.1872 23.1858)", + primaryForeground: "oklch(0 0 0)", + secondary: "oklch(0.9691 0.2005 109.6228)", + secondaryForeground: "oklch(0 0 0)", + muted: "oklch(0.2178 0 0)", + mutedForeground: "oklch(0.8452 0 0)", + accent: "oklch(0.6755 0.1765 252.2592)", + accentForeground: "oklch(0 0 0)", + destructive: "oklch(1.0000 0 0)", + destructiveForeground: "oklch(0 0 0)", + border: "oklch(1.0000 0 0)", + input: "oklch(1.0000 0 0)", + ring: "oklch(0.7044 0.1872 23.1858)", + chart1: "oklch(0.7044 0.1872 23.1858)", + chart2: "oklch(0.9691 0.2005 109.6228)", + chart3: "oklch(0.6755 0.1765 252.2592)", + chart4: "oklch(0.7395 0.2268 142.8504)", + chart5: "oklch(0.6131 0.2458 328.0714)", + sidebar: "oklch(0 0 0)", + sidebarForeground: "oklch(1.0000 0 0)", + sidebarPrimary: "oklch(0.7044 0.1872 23.1858)", + sidebarPrimaryForeground: "oklch(0 0 0)", + sidebarAccent: "oklch(0.6755 0.1765 252.2592)", + sidebarAccentForeground: "oklch(0 0 0)", + sidebarBorder: "oklch(1.0000 0 0)", + sidebarRing: "oklch(0.7044 0.1872 23.1858)", + fontSans: "DM Sans, sans-serif", + fontSerif: "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + fontMono: "Space Mono, monospace", + radius: "0px", + "shadow-2xs": "4px 4px 0px 0px hsl(0 0% 0% / 0.50)", + "shadow-xs": "4px 4px 0px 0px hsl(0 0% 0% / 0.50)", + "shadow-sm": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00)", + shadow: "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00)", + "shadow-md": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00)", + "shadow-lg": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00)", + "shadow-xl": "4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00)", + "shadow-2xl": "4px 4px 0px 0px hsl(0 0% 0% / 2.50)", +}; + +/** + * Notebook theme - Handwritten feel with grayscale tones, warm accents, and subtle shadows + */ +export const notebookTheme: ThemeTokens = { + background: "oklch(0.9821 0 0)", + foreground: "oklch(0.3485 0 0)", + card: "oklch(1.0000 0 0)", + cardForeground: "oklch(0.3485 0 0)", + popover: "oklch(1.0000 0 0)", + popoverForeground: "oklch(0.3485 0 0)", + primary: "oklch(0.4891 0 0)", + primaryForeground: "oklch(0.9551 0 0)", + secondary: "oklch(0.9006 0 0)", + secondaryForeground: "oklch(0.3485 0 0)", + muted: "oklch(0.9158 0 0)", + mutedForeground: "oklch(0.4313 0 0)", + accent: "oklch(0.9354 0.0456 94.8549)", + accentForeground: "oklch(0.4015 0.0436 37.9587)", + destructive: "oklch(0.6627 0.0978 20.0041)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.5538 0.0025 17.2320)", + input: "oklch(1.0000 0 0)", + ring: "oklch(0.7058 0 0)", + chart1: "oklch(0.3211 0 0)", + chart2: "oklch(0.4495 0 0)", + chart3: "oklch(0.5693 0 0)", + chart4: "oklch(0.6830 0 0)", + chart5: "oklch(0.7921 0 0)", + sidebar: "oklch(0.9551 0 0)", + sidebarForeground: "oklch(0.3485 0 0)", + sidebarPrimary: "oklch(0.4891 0 0)", + sidebarPrimaryForeground: "oklch(0.9551 0 0)", + sidebarAccent: "oklch(0.9354 0.0456 94.8549)", + sidebarAccentForeground: "oklch(0.4015 0.0436 37.9587)", + sidebarBorder: "oklch(0.8078 0 0)", + sidebarRing: "oklch(0.7058 0 0)", + fontSans: "Architects Daughter, sans-serif", + fontSerif: "\"Times New Roman\", Times, serif", + fontMono: "\"Courier New\", Courier, monospace", + radius: "0.625rem", + "shadow-2xs": "1px 4px 5px 0px hsl(0 0% 0% / 0.01)", + "shadow-xs": "1px 4px 5px 0px hsl(0 0% 0% / 0.01)", + "shadow-sm": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 1px 2px -1px hsl(0 0% 0% / 0.03)", + shadow: "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 1px 2px -1px hsl(0 0% 0% / 0.03)", + "shadow-md": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 2px 4px -1px hsl(0 0% 0% / 0.03)", + "shadow-lg": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 4px 6px -1px hsl(0 0% 0% / 0.03)", + "shadow-xl": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 8px 10px -1px hsl(0 0% 0% / 0.03)", + "shadow-2xl": "1px 4px 5px 0px hsl(0 0% 0% / 0.07)", +}; + +/** + * Notebook dark theme + */ +export const notebookDarkTheme: ThemeTokens = { + background: "oklch(0.2891 0 0)", + foreground: "oklch(0.8945 0 0)", + card: "oklch(0.3211 0 0)", + cardForeground: "oklch(0.8945 0 0)", + popover: "oklch(0.3211 0 0)", + popoverForeground: "oklch(0.8945 0 0)", + primary: "oklch(0.7572 0 0)", + primaryForeground: "oklch(0.2891 0 0)", + secondary: "oklch(0.4676 0 0)", + secondaryForeground: "oklch(0.8078 0 0)", + muted: "oklch(0.3904 0 0)", + mutedForeground: "oklch(0.7058 0 0)", + accent: "oklch(0.9067 0 0)", + accentForeground: "oklch(0.3211 0 0)", + destructive: "oklch(0.7915 0.0491 18.2410)", + destructiveForeground: "oklch(0.2891 0 0)", + border: "oklch(0.4276 0 0)", + input: "oklch(0.3211 0 0)", + ring: "oklch(0.8078 0 0)", + chart1: "oklch(0.9521 0 0)", + chart2: "oklch(0.8576 0 0)", + chart3: "oklch(0.7572 0 0)", + chart4: "oklch(0.6534 0 0)", + chart5: "oklch(0.5452 0 0)", + sidebar: "oklch(0.2478 0 0)", + sidebarForeground: "oklch(0.8945 0 0)", + sidebarPrimary: "oklch(0.7572 0 0)", + sidebarPrimaryForeground: "oklch(0.2478 0 0)", + sidebarAccent: "oklch(0.9067 0 0)", + sidebarAccentForeground: "oklch(0.3211 0 0)", + sidebarBorder: "oklch(0.4276 0 0)", + sidebarRing: "oklch(0.8078 0 0)", + fontSans: "Architects Daughter, sans-serif", + fontSerif: "Georgia, serif", + fontMono: "\"Fira Code\", \"Courier New\", monospace", + radius: "0.625rem", + "shadow-2xs": "1px 4px 5px 0px hsl(0 0% 0% / 0.01)", + "shadow-xs": "1px 4px 5px 0px hsl(0 0% 0% / 0.01)", + "shadow-sm": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 1px 2px -1px hsl(0 0% 0% / 0.03)", + shadow: "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 1px 2px -1px hsl(0 0% 0% / 0.03)", + "shadow-md": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 2px 4px -1px hsl(0 0% 0% / 0.03)", + "shadow-lg": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 4px 6px -1px hsl(0 0% 0% / 0.03)", + "shadow-xl": "1px 4px 5px 0px hsl(0 0% 0% / 0.03), 1px 8px 10px -1px hsl(0 0% 0% / 0.03)", + "shadow-2xl": "1px 4px 5px 0px hsl(0 0% 0% / 0.07)", +}; + +/** + * Ocean Breeze theme - Teal-green primary on cool blue-grey with DM Sans + */ +export const oceanBreezeTheme: ThemeTokens = { + background: "oklch(0.9751 0.0127 244.2507)", + foreground: "oklch(0.3729 0.0306 259.7328)", + card: "oklch(1.0000 0 0)", + cardForeground: "oklch(0.3729 0.0306 259.7328)", + popover: "oklch(1.0000 0 0)", + popoverForeground: "oklch(0.3729 0.0306 259.7328)", + primary: "oklch(0.7227 0.1920 149.5793)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9514 0.0250 236.8242)", + secondaryForeground: "oklch(0.4461 0.0263 256.8018)", + muted: "oklch(0.9670 0.0029 264.5419)", + mutedForeground: "oklch(0.5510 0.0234 264.3637)", + accent: "oklch(0.9505 0.0507 163.0508)", + accentForeground: "oklch(0.3729 0.0306 259.7328)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.9276 0.0058 264.5313)", + input: "oklch(0.9276 0.0058 264.5313)", + ring: "oklch(0.7227 0.1920 149.5793)", + chart1: "oklch(0.7227 0.1920 149.5793)", + chart2: "oklch(0.6959 0.1491 162.4796)", + chart3: "oklch(0.5960 0.1274 163.2254)", + chart4: "oklch(0.5081 0.1049 165.6121)", + chart5: "oklch(0.4318 0.0865 166.9128)", + sidebar: "oklch(0.9514 0.0250 236.8242)", + sidebarForeground: "oklch(0.3729 0.0306 259.7328)", + sidebarPrimary: "oklch(0.7227 0.1920 149.5793)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.9505 0.0507 163.0508)", + sidebarAccentForeground: "oklch(0.3729 0.0306 259.7328)", + sidebarBorder: "oklch(0.9276 0.0058 264.5313)", + sidebarRing: "oklch(0.7227 0.1920 149.5793)", + fontSans: "DM Sans, sans-serif", + fontSerif: "Lora, serif", + fontMono: "IBM Plex Mono, monospace", + radius: "0.5rem", + "shadow-2xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + shadow: "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + "shadow-md": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.25)", +}; + +/** + * Ocean Breeze dark theme + */ +export const oceanBreezeDarkTheme: ThemeTokens = { + background: "oklch(0.2077 0.0398 265.7549)", + foreground: "oklch(0.8717 0.0093 258.3382)", + card: "oklch(0.2795 0.0368 260.0310)", + cardForeground: "oklch(0.8717 0.0093 258.3382)", + popover: "oklch(0.2795 0.0368 260.0310)", + popoverForeground: "oklch(0.8717 0.0093 258.3382)", + primary: "oklch(0.7729 0.1535 163.2231)", + primaryForeground: "oklch(0.2077 0.0398 265.7549)", + secondary: "oklch(0.3351 0.0331 260.9120)", + secondaryForeground: "oklch(0.7118 0.0129 286.0665)", + muted: "oklch(0.2463 0.0275 259.9628)", + mutedForeground: "oklch(0.5510 0.0234 264.3637)", + accent: "oklch(0.3729 0.0306 259.7328)", + accentForeground: "oklch(0.7118 0.0129 286.0665)", + destructive: "oklch(0.6368 0.2078 25.3313)", + destructiveForeground: "oklch(0.2077 0.0398 265.7549)", + border: "oklch(0.4461 0.0263 256.8018)", + input: "oklch(0.4461 0.0263 256.8018)", + ring: "oklch(0.7729 0.1535 163.2231)", + chart1: "oklch(0.7729 0.1535 163.2231)", + chart2: "oklch(0.7845 0.1325 181.9120)", + chart3: "oklch(0.7227 0.1920 149.5793)", + chart4: "oklch(0.6959 0.1491 162.4796)", + chart5: "oklch(0.5960 0.1274 163.2254)", + sidebar: "oklch(0.2795 0.0368 260.0310)", + sidebarForeground: "oklch(0.8717 0.0093 258.3382)", + sidebarPrimary: "oklch(0.7729 0.1535 163.2231)", + sidebarPrimaryForeground: "oklch(0.2077 0.0398 265.7549)", + sidebarAccent: "oklch(0.3729 0.0306 259.7328)", + sidebarAccentForeground: "oklch(0.7118 0.0129 286.0665)", + sidebarBorder: "oklch(0.4461 0.0263 256.8018)", + sidebarRing: "oklch(0.7729 0.1535 163.2231)", + fontSans: "DM Sans, sans-serif", + fontSerif: "Lora, serif", + fontMono: "IBM Plex Mono, monospace", + radius: "0.5rem", + "shadow-2xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0px 4px 8px -1px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + shadow: "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 1px 2px -2px hsl(0 0% 0% / 0.10)", + "shadow-md": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 2px 4px -2px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 4px 6px -2px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.10), 0px 8px 10px -2px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0px 4px 8px -1px hsl(0 0% 0% / 0.25)", +}; + +/** + * Supabase theme - Mint-green primary on near-white, Outfit font, colorful charts + */ +export const supabaseTheme: ThemeTokens = { + background: "oklch(0.9911 0 0)", + foreground: "oklch(0.2046 0 0)", + card: "oklch(0.9911 0 0)", + cardForeground: "oklch(0.2046 0 0)", + popover: "oklch(0.9911 0 0)", + popoverForeground: "oklch(0.4386 0 0)", + primary: "oklch(0.8348 0.1302 160.9080)", + primaryForeground: "oklch(0.2626 0.0147 166.4589)", + secondary: "oklch(0.9940 0 0)", + secondaryForeground: "oklch(0.2046 0 0)", + muted: "oklch(0.9461 0 0)", + mutedForeground: "oklch(0.2435 0 0)", + accent: "oklch(0.9461 0 0)", + accentForeground: "oklch(0.2435 0 0)", + destructive: "oklch(0.5523 0.1927 32.7272)", + destructiveForeground: "oklch(0.9934 0.0032 17.2118)", + border: "oklch(0.9037 0 0)", + input: "oklch(0.9731 0 0)", + ring: "oklch(0.8348 0.1302 160.9080)", + chart1: "oklch(0.8348 0.1302 160.9080)", + chart2: "oklch(0.6231 0.1880 259.8145)", + chart3: "oklch(0.6056 0.2189 292.7172)", + chart4: "oklch(0.7686 0.1647 70.0804)", + chart5: "oklch(0.6959 0.1491 162.4796)", + sidebar: "oklch(0.9911 0 0)", + sidebarForeground: "oklch(0.5452 0 0)", + sidebarPrimary: "oklch(0.8348 0.1302 160.9080)", + sidebarPrimaryForeground: "oklch(0.2626 0.0147 166.4589)", + sidebarAccent: "oklch(0.9461 0 0)", + sidebarAccentForeground: "oklch(0.2435 0 0)", + sidebarBorder: "oklch(0.9037 0 0)", + sidebarRing: "oklch(0.8348 0.1302 160.9080)", + fontSans: "Outfit, sans-serif", + fontSerif: "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + fontMono: "monospace", + radius: "0.5rem", + "shadow-2xs": "0px 1px 3px 0px hsl(0 0% 0% / 0.09)", + "shadow-xs": "0px 1px 3px 0px hsl(0 0% 0% / 0.09)", + "shadow-sm": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17)", + shadow: "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17)", + "shadow-md": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 2px 4px -1px hsl(0 0% 0% / 0.17)", + "shadow-lg": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 4px 6px -1px hsl(0 0% 0% / 0.17)", + "shadow-xl": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 8px 10px -1px hsl(0 0% 0% / 0.17)", + "shadow-2xl": "0px 1px 3px 0px hsl(0 0% 0% / 0.43)", +}; + +/** + * Supabase dark theme + */ +export const supabaseDarkTheme: ThemeTokens = { + background: "oklch(0.1822 0 0)", + foreground: "oklch(0.9288 0.0126 255.5078)", + card: "oklch(0.2046 0 0)", + cardForeground: "oklch(0.9288 0.0126 255.5078)", + popover: "oklch(0.2603 0 0)", + popoverForeground: "oklch(0.7348 0 0)", + primary: "oklch(0.4365 0.1044 156.7556)", + primaryForeground: "oklch(0.9213 0.0135 167.1556)", + secondary: "oklch(0.2603 0 0)", + secondaryForeground: "oklch(0.9851 0 0)", + muted: "oklch(0.2393 0 0)", + mutedForeground: "oklch(0.7122 0 0)", + accent: "oklch(0.3132 0 0)", + accentForeground: "oklch(0.9851 0 0)", + destructive: "oklch(0.3123 0.0852 29.7877)", + destructiveForeground: "oklch(0.9368 0.0045 34.3092)", + border: "oklch(0.2809 0 0)", + input: "oklch(0.2603 0 0)", + ring: "oklch(0.8003 0.1821 151.7110)", + chart1: "oklch(0.8003 0.1821 151.7110)", + chart2: "oklch(0.7137 0.1434 254.6240)", + chart3: "oklch(0.7090 0.1592 293.5412)", + chart4: "oklch(0.8369 0.1644 84.4286)", + chart5: "oklch(0.7845 0.1325 181.9120)", + sidebar: "oklch(0.1822 0 0)", + sidebarForeground: "oklch(0.6301 0 0)", + sidebarPrimary: "oklch(0.4365 0.1044 156.7556)", + sidebarPrimaryForeground: "oklch(0.9213 0.0135 167.1556)", + sidebarAccent: "oklch(0.3132 0 0)", + sidebarAccentForeground: "oklch(0.9851 0 0)", + sidebarBorder: "oklch(0.2809 0 0)", + sidebarRing: "oklch(0.8003 0.1821 151.7110)", + fontSans: "Outfit, sans-serif", + fontSerif: "ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif", + fontMono: "monospace", + radius: "0.5rem", + "shadow-2xs": "0px 1px 3px 0px hsl(0 0% 0% / 0.09)", + "shadow-xs": "0px 1px 3px 0px hsl(0 0% 0% / 0.09)", + "shadow-sm": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17)", + shadow: "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 1px 2px -1px hsl(0 0% 0% / 0.17)", + "shadow-md": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 2px 4px -1px hsl(0 0% 0% / 0.17)", + "shadow-lg": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 4px 6px -1px hsl(0 0% 0% / 0.17)", + "shadow-xl": "0px 1px 3px 0px hsl(0 0% 0% / 0.17), 0px 8px 10px -1px hsl(0 0% 0% / 0.17)", + "shadow-2xl": "0px 1px 3px 0px hsl(0 0% 0% / 0.43)", +}; + +/** + * Terminal theme - Green-on-black CRT aesthetic, VT323 monospace font, neon glow shadows + */ +export const terminalTheme: ThemeTokens = { + background: "oklch(0 0 0)", + foreground: "oklch(0.8686 0.2776 144.4661)", + card: "oklch(0.1149 0 0)", + cardForeground: "oklch(0.8686 0.2776 144.4661)", + popover: "oklch(0 0 0)", + popoverForeground: "oklch(0.8686 0.2776 144.4661)", + primary: "oklch(0.8686 0.2776 144.4661)", + primaryForeground: "oklch(0 0 0)", + secondary: "oklch(0.3053 0.1039 142.4953)", + secondaryForeground: "oklch(0.8686 0.2776 144.4661)", + muted: "oklch(0.1887 0.0642 142.4953)", + mutedForeground: "oklch(0.5638 0.1872 143.2450)", + accent: "oklch(0.8686 0.2776 144.4661)", + accentForeground: "oklch(0 0 0)", + destructive: "oklch(0.6280 0.2577 29.2339)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.3053 0.1039 142.4953)", + input: "oklch(0 0 0)", + ring: "oklch(0.8686 0.2776 144.4661)", + chart1: "oklch(0.8686 0.2776 144.4661)", + chart2: "oklch(0.5638 0.1872 143.2450)", + chart3: "oklch(0.3053 0.1039 142.4953)", + chart4: "oklch(0.1179 0.0327 343.3438)", + chart5: "oklch(0.8686 0.2776 144.4661)", + sidebar: "oklch(0.1149 0 0)", + sidebarForeground: "oklch(0.8686 0.2776 144.4661)", + sidebarPrimary: "oklch(0.8686 0.2776 144.4661)", + sidebarPrimaryForeground: "oklch(0 0 0)", + sidebarAccent: "oklch(0.3053 0.1039 142.4953)", + sidebarAccentForeground: "oklch(0.8686 0.2776 144.4661)", + sidebarBorder: "oklch(0.3053 0.1039 142.4953)", + sidebarRing: "oklch(0.8686 0.2776 144.4661)", + fontSans: "\"VT323\", \"Courier New\", monospace", + fontSerif: "Georgia, serif", + fontMono: "\"VT323\", monospace", + radius: "0rem", + "shadow-2xs": "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.10)", + "shadow-xs": "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.10)", + "shadow-sm": "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.20), 0px 1px 2px 0px hsl(135.2941 100% 50% / 0.20)", + shadow: "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.20), 0px 1px 2px 0px hsl(135.2941 100% 50% / 0.20)", + "shadow-md": "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.20), 0px 2px 4px 0px hsl(135.2941 100% 50% / 0.20)", + "shadow-lg": "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.20), 0px 4px 6px 0px hsl(135.2941 100% 50% / 0.20)", + "shadow-xl": "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.20), 0px 8px 10px 0px hsl(135.2941 100% 50% / 0.20)", + "shadow-2xl": "0px 0px 10px 1px hsl(135.2941 100% 50% / 0.50)", +}; + +/** + * Terminal dark theme (intensified glow) + */ +export const terminalDarkTheme: ThemeTokens = { + background: "oklch(0 0 0)", + foreground: "oklch(0.8686 0.2776 144.4661)", + card: "oklch(0.1149 0 0)", + cardForeground: "oklch(0.8686 0.2776 144.4661)", + popover: "oklch(0 0 0)", + popoverForeground: "oklch(0.8686 0.2776 144.4661)", + primary: "oklch(0.8686 0.2776 144.4661)", + primaryForeground: "oklch(0 0 0)", + secondary: "oklch(0.3053 0.1039 142.4953)", + secondaryForeground: "oklch(0.8686 0.2776 144.4661)", + muted: "oklch(0.1887 0.0642 142.4953)", + mutedForeground: "oklch(0.5638 0.1872 143.2450)", + accent: "oklch(0.8686 0.2776 144.4661)", + accentForeground: "oklch(0 0 0)", + destructive: "oklch(0.6280 0.2577 29.2339)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.3053 0.1039 142.4953)", + input: "oklch(0 0 0)", + ring: "oklch(0.8686 0.2776 144.4661)", + chart1: "oklch(0.8686 0.2776 144.4661)", + chart2: "oklch(0.5638 0.1872 143.2450)", + chart3: "oklch(0.3053 0.1039 142.4953)", + chart4: "oklch(0.1179 0.0327 343.3438)", + chart5: "oklch(0.8686 0.2776 144.4661)", + sidebar: "oklch(0.1149 0 0)", + sidebarForeground: "oklch(0.8686 0.2776 144.4661)", + sidebarPrimary: "oklch(0.8686 0.2776 144.4661)", + sidebarPrimaryForeground: "oklch(0 0 0)", + sidebarAccent: "oklch(0.3053 0.1039 142.4953)", + sidebarAccentForeground: "oklch(0.8686 0.2776 144.4661)", + sidebarBorder: "oklch(0.3053 0.1039 142.4953)", + sidebarRing: "oklch(0.8686 0.2776 144.4661)", + fontSans: "\"VT323\", \"Courier New\", monospace", + fontSerif: "Georgia, serif", + fontMono: "\"VT323\", monospace", + radius: "0rem", + "shadow-2xs": "0px 0px 15px 2px hsl(135.2941 100% 50% / 0.20)", + "shadow-xs": "0px 0px 15px 2px hsl(135.2941 100% 50% / 0.20)", + "shadow-sm": "0px 0px 15px 2px hsl(135.2941 100% 50% / 0.40), 0px 1px 2px 1px hsl(135.2941 100% 50% / 0.40)", + shadow: "0px 0px 15px 2px hsl(135.2941 100% 50% / 0.40), 0px 1px 2px 1px hsl(135.2941 100% 50% / 0.40)", + "shadow-md": "0px 0px 15px 2px hsl(135.2941 100% 50% / 0.40), 0px 2px 4px 1px hsl(135.2941 100% 50% / 0.40)", + "shadow-lg": "0px 0px 15px 2px hsl(135.2941 100% 50% / 0.40), 0px 4px 6px 1px hsl(135.2941 100% 50% / 0.40)", + "shadow-xl": "0px 0px 15px 2px hsl(135.2941 100% 50% / 0.40), 0px 8px 10px 1px hsl(135.2941 100% 50% / 0.40)", + "shadow-2xl": "0px 0px 15px 2px hsl(135.2941 100% 50% / 1.00)", +}; + +/** + * WhatsApp theme - Teal primary with green accents, rounded 1rem radius, system fonts + */ +export const whatsappTheme: ThemeTokens = { + background: "oklch(0.9605 0.0046 258.3248)", + foreground: "oklch(0.2153 0.0187 235.1251)", + card: "oklch(1.0000 0 0)", + cardForeground: "oklch(0.2153 0.0187 235.1251)", + popover: "oklch(1.0000 0 0)", + popoverForeground: "oklch(0.2153 0.0187 235.1251)", + primary: "oklch(0.4335 0.0754 182.2315)", + primaryForeground: "oklch(1.0000 0 0)", + secondary: "oklch(0.9644 0.0208 166.1014)", + secondaryForeground: "oklch(0.4335 0.0754 182.2315)", + muted: "oklch(0.9605 0.0046 258.3248)", + mutedForeground: "oklch(0.5589 0.0255 233.7233)", + accent: "oklch(0.7610 0.2015 149.7403)", + accentForeground: "oklch(1.0000 0 0)", + destructive: "oklch(0.6257 0.2058 29.0773)", + destructiveForeground: "oklch(1.0000 0 0)", + border: "oklch(0.9436 0.0051 228.8204)", + input: "oklch(0.9436 0.0051 228.8204)", + ring: "oklch(0.7610 0.2015 149.7403)", + chart1: "oklch(0.7610 0.2015 149.7403)", + chart2: "oklch(0.4335 0.0754 182.2315)", + chart3: "oklch(0.5762 0.0995 182.3964)", + chart4: "oklch(0.7356 0.1370 232.8053)", + chart5: "oklch(0.6509 0.1283 170.4258)", + sidebar: "oklch(1.0000 0 0)", + sidebarForeground: "oklch(0.2153 0.0187 235.1251)", + sidebarPrimary: "oklch(0.4335 0.0754 182.2315)", + sidebarPrimaryForeground: "oklch(1.0000 0 0)", + sidebarAccent: "oklch(0.9644 0.0208 166.1014)", + sidebarAccentForeground: "oklch(0.4335 0.0754 182.2315)", + sidebarBorder: "oklch(0.9436 0.0051 228.8204)", + sidebarRing: "oklch(0.7610 0.2015 149.7403)", + fontSans: "Segoe UI, Helvetica Neue, Helvetica, Lucida Grande, Arial, Ubuntu, Cantarell, Fira Sans, sans-serif", + fontSerif: "Georgia, serif", + fontMono: "SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace", + radius: "1rem", + "shadow-2xs": "0px 2px 10px 0px hsl(0 0% 0% / 0.05)", + "shadow-xs": "0px 2px 10px 0px hsl(0 0% 0% / 0.05)", + "shadow-sm": "0px 2px 10px 0px hsl(0 0% 0% / 0.10), 0px 1px 2px -1px hsl(0 0% 0% / 0.10)", + shadow: "0px 2px 10px 0px hsl(0 0% 0% / 0.10), 0px 1px 2px -1px hsl(0 0% 0% / 0.10)", + "shadow-md": "0px 2px 10px 0px hsl(0 0% 0% / 0.10), 0px 2px 4px -1px hsl(0 0% 0% / 0.10)", + "shadow-lg": "0px 2px 10px 0px hsl(0 0% 0% / 0.10), 0px 4px 6px -1px hsl(0 0% 0% / 0.10)", + "shadow-xl": "0px 2px 10px 0px hsl(0 0% 0% / 0.10), 0px 8px 10px -1px hsl(0 0% 0% / 0.10)", + "shadow-2xl": "0px 2px 10px 0px hsl(0 0% 0% / 0.25)", +}; + +/** + * WhatsApp dark theme + */ +export const whatsappDarkTheme: ThemeTokens = { + background: "oklch(0.1854 0.0182 238.2143)", + foreground: "oklch(0.9436 0.0051 228.8204)", + card: "oklch(0.2848 0.0230 235.6578)", + cardForeground: "oklch(0.9436 0.0051 228.8204)", + popover: "oklch(0.2848 0.0230 235.6578)", + popoverForeground: "oklch(0.9436 0.0051 228.8204)", + primary: "oklch(0.6509 0.1283 170.4258)", + primaryForeground: "oklch(0.2153 0.0187 235.1251)", + secondary: "oklch(0.2933 0.0423 172.8195)", + secondaryForeground: "oklch(0.6509 0.1283 170.4258)", + muted: "oklch(0.2456 0.0195 239.1061)", + mutedForeground: "oklch(0.6637 0.0236 235.1968)", + accent: "oklch(0.7610 0.2015 149.7403)", + accentForeground: "oklch(0.2153 0.0187 235.1251)", + destructive: "oklch(0.6257 0.2058 29.0773)", + destructiveForeground: "oklch(0.9436 0.0051 228.8204)", + border: "oklch(0.3351 0.0253 234.8586)", + input: "oklch(0.3351 0.0253 234.8586)", + ring: "oklch(0.6509 0.1283 170.4258)", + chart1: "oklch(0.7610 0.2015 149.7403)", + chart2: "oklch(0.6509 0.1283 170.4258)", + chart3: "oklch(0.5762 0.0995 182.3964)", + chart4: "oklch(0.7356 0.1370 232.8053)", + chart5: "oklch(0.4335 0.0754 182.2315)", + sidebar: "oklch(0.2153 0.0187 235.1251)", + sidebarForeground: "oklch(0.9436 0.0051 228.8204)", + sidebarPrimary: "oklch(0.6509 0.1283 170.4258)", + sidebarPrimaryForeground: "oklch(0.2153 0.0187 235.1251)", + sidebarAccent: "oklch(0.2933 0.0423 172.8195)", + sidebarAccentForeground: "oklch(0.6509 0.1283 170.4258)", + sidebarBorder: "oklch(0.3351 0.0253 234.8586)", + sidebarRing: "oklch(0.6509 0.1283 170.4258)", + fontSans: "Segoe UI, Helvetica Neue, Helvetica, Lucida Grande, Arial, Ubuntu, Cantarell, Fira Sans, sans-serif", + fontSerif: "Georgia, serif", + fontMono: "SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace", + radius: "1rem", + "shadow-2xs": "0px 4px 12px 0px hsl(0 0% 0% / 0.20)", + "shadow-xs": "0px 4px 12px 0px hsl(0 0% 0% / 0.20)", + "shadow-sm": "0px 4px 12px 0px hsl(0 0% 0% / 0.40), 0px 1px 2px -1px hsl(0 0% 0% / 0.40)", + shadow: "0px 4px 12px 0px hsl(0 0% 0% / 0.40), 0px 1px 2px -1px hsl(0 0% 0% / 0.40)", + "shadow-md": "0px 4px 12px 0px hsl(0 0% 0% / 0.40), 0px 2px 4px -1px hsl(0 0% 0% / 0.40)", + "shadow-lg": "0px 4px 12px 0px hsl(0 0% 0% / 0.40), 0px 4px 6px -1px hsl(0 0% 0% / 0.40)", + "shadow-xl": "0px 4px 12px 0px hsl(0 0% 0% / 0.40), 0px 8px 10px -1px hsl(0 0% 0% / 0.40)", + "shadow-2xl": "0px 4px 12px 0px hsl(0 0% 0% / 1.00)", +}; + /** * Helper to create a custom theme with defaults filled in */ From 362de4c10e89868e40d486b6ca6de7fca6f263fb Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:13:31 +0600 Subject: [PATCH 19/20] Implement feature X to enhance user experience and optimize performance --- package-lock.json | 669 ++++------------------------------------------ 1 file changed, 53 insertions(+), 616 deletions(-) diff --git a/package-lock.json b/package-lock.json index 22485d0..9742b22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,6 @@ "@wordpress/hooks": "^4.39.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "cmdk": "^1.1.1", "input-otp": "^1.4.2", "lucide-react": "^0.563.0", "recharts": "^2.15.4", @@ -160,6 +159,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2189,6 +2189,7 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2251,6 +2252,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2291,6 +2293,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -2380,6 +2383,7 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -2601,6 +2605,7 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -4044,6 +4049,7 @@ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -4067,6 +4073,7 @@ "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" }, @@ -4080,6 +4087,7 @@ "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, @@ -4562,6 +4570,7 @@ "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" @@ -4589,6 +4598,7 @@ "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", @@ -4617,6 +4627,7 @@ "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -5127,447 +5138,6 @@ "node": ">=10" } }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", - "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.2.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -6223,6 +5793,7 @@ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -6639,7 +6210,6 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -6660,7 +6230,6 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -6671,7 +6240,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -6685,7 +6253,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -6696,7 +6263,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -6711,8 +6277,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", @@ -6798,8 +6363,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -6982,6 +6546,7 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -7186,6 +6751,7 @@ "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -7260,6 +6826,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -7472,6 +7039,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -8271,6 +7839,7 @@ "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.25.7", @@ -9506,6 +9075,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -9599,6 +9169,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -9800,18 +9371,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -10783,6 +10342,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -11415,22 +10975,6 @@ "node": ">=6" } }, - "node_modules/cmdk": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", - "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "^1.1.1", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-id": "^1.1.0", - "@radix-ui/react-primitive": "^2.0.2" - }, - "peerDependencies": { - "react": "^18 || ^19 || ^19.0.0-rc", - "react-dom": "^18 || ^19 || ^19.0.0-rc" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -12674,7 +12218,6 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -12717,18 +12260,13 @@ "dev": true, "license": "MIT" }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, "node_modules/devtools-protocol": { "version": "0.0.1507524", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1507524.tgz", "integrity": "sha512-OjaNE7qpk6GRTXtqQjAE5bGx6+c4F1zZH0YXtpZQLM92HNXx4zMAaqlKhP4T52DosG6hDW8gPMNhGOF8xbwk/w==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/diff-sequences": { "version": "29.6.3", @@ -12784,8 +12322,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -13339,6 +12876,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -13443,6 +12981,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -13499,6 +13038,7 @@ "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -13622,6 +13162,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -15371,15 +14912,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -17339,6 +16871,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -18439,7 +17972,8 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz", "integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/ws": { "version": "8.19.0", @@ -18979,7 +18513,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -19812,6 +19345,7 @@ "integrity": "sha512-cuXAJJB1Rdqz0UO6w524matlBqDBjcNt7Ru+RDIu4y6RI1gVqiWBnylrK8sPRk81gGBA0X8hJbDXolVOoTc+sA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^6.12.6", "ajv-errors": "^1.0.1", @@ -20603,7 +20137,6 @@ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "playwright-core": "1.58.2" }, @@ -20623,7 +20156,6 @@ "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -20642,7 +20174,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -20693,6 +20224,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -21661,6 +21193,7 @@ "integrity": "sha512-X4UlrxDTH8oom9qXlcjnydsjAOD2BmB6yFmvS4Z2zdTzqqpRWb+fbqrH412+l+OUXmbzJlSXjlMFYPgYG12IAA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -22088,6 +21621,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -22163,6 +21697,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -22183,57 +21718,11 @@ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -22249,28 +21738,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -22532,7 +21999,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", @@ -23079,6 +22547,7 @@ "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -23183,6 +22652,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -24044,6 +23514,7 @@ "integrity": "sha512-885uSIn8NQw2ZG7vy84K45lHCOSyz1DVsDV8pHiHQj3J0riCuWLNeO50lK9z98zE8kjhgTtxAAkMTy5nkmNRKQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", @@ -24495,6 +23966,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.19", @@ -24824,6 +24296,7 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -25493,6 +24966,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -25774,6 +25248,7 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -25886,6 +25361,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -26038,6 +25514,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -26183,27 +25660,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-memo-one": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", @@ -26213,28 +25669,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -26457,6 +25891,7 @@ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -26565,6 +26000,7 @@ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -26650,6 +26086,7 @@ "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", From 4560196fd69682a63f46e6c443c86a5269b4d4af Mon Sep 17 00:00:00 2001 From: Aunshon <32583103+Aunshon@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:15:47 +0600 Subject: [PATCH 20/20] Implement feature X to enhance user experience and fix bug Y in module Z --- package-lock.json | 669 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 616 insertions(+), 53 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9742b22..22485d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@wordpress/hooks": "^4.39.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "input-otp": "^1.4.2", "lucide-react": "^0.563.0", "recharts": "^2.15.4", @@ -159,7 +160,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2189,7 +2189,6 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2252,7 +2251,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -2293,7 +2291,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2383,7 +2380,6 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", - "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -2605,7 +2601,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -4049,7 +4044,6 @@ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -4073,7 +4067,6 @@ "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" }, @@ -4087,7 +4080,6 @@ "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, @@ -4570,7 +4562,6 @@ "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" @@ -4598,7 +4589,6 @@ "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", @@ -4627,7 +4617,6 @@ "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" } @@ -5138,6 +5127,447 @@ "node": ">=10" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -5793,7 +6223,6 @@ "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -6210,6 +6639,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -6230,6 +6660,7 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -6240,6 +6671,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -6253,6 +6685,7 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -6263,6 +6696,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -6277,7 +6711,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", @@ -6363,7 +6798,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -6546,7 +6982,6 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -6751,7 +7186,6 @@ "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -6826,7 +7260,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -7039,7 +7472,6 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -7839,7 +8271,6 @@ "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.25.7", @@ -9075,7 +9506,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -9169,7 +9599,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -9371,6 +9800,18 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -10342,7 +10783,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -10975,6 +11415,22 @@ "node": ">=6" } }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -12218,6 +12674,7 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -12260,13 +12717,18 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devtools-protocol": { "version": "0.0.1507524", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1507524.tgz", "integrity": "sha512-OjaNE7qpk6GRTXtqQjAE5bGx6+c4F1zZH0YXtpZQLM92HNXx4zMAaqlKhP4T52DosG6hDW8gPMNhGOF8xbwk/w==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/diff-sequences": { "version": "29.6.3", @@ -12322,7 +12784,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dom-converter": { "version": "0.2.0", @@ -12876,7 +13339,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -12981,7 +13443,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -13038,7 +13499,6 @@ "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -13162,7 +13622,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -14912,6 +15371,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -16871,7 +17339,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -17972,8 +18439,7 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz", "integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/ws": { "version": "8.19.0", @@ -18513,6 +18979,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -19345,7 +19812,6 @@ "integrity": "sha512-cuXAJJB1Rdqz0UO6w524matlBqDBjcNt7Ru+RDIu4y6RI1gVqiWBnylrK8sPRk81gGBA0X8hJbDXolVOoTc+sA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^6.12.6", "ajv-errors": "^1.0.1", @@ -20137,6 +20603,7 @@ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright-core": "1.58.2" }, @@ -20156,6 +20623,7 @@ "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -20174,6 +20642,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -20224,7 +20693,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -21193,7 +21661,6 @@ "integrity": "sha512-X4UlrxDTH8oom9qXlcjnydsjAOD2BmB6yFmvS4Z2zdTzqqpRWb+fbqrH412+l+OUXmbzJlSXjlMFYPgYG12IAA==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -21621,7 +22088,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -21697,7 +22163,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -21718,11 +22183,57 @@ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -21738,6 +22249,28 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -21999,8 +22532,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", @@ -22547,7 +23079,6 @@ "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -22652,7 +23183,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -23514,7 +24044,6 @@ "integrity": "sha512-885uSIn8NQw2ZG7vy84K45lHCOSyz1DVsDV8pHiHQj3J0riCuWLNeO50lK9z98zE8kjhgTtxAAkMTy5nkmNRKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.1", @@ -23966,7 +24495,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.19", @@ -24296,7 +24824,6 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -24966,7 +25493,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -25248,7 +25774,6 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=10" }, @@ -25361,7 +25886,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -25514,7 +26038,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -25660,6 +26183,27 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-memo-one": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", @@ -25669,6 +26213,28 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -25891,7 +26457,6 @@ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -26000,7 +26565,6 @@ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -26086,7 +26650,6 @@ "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5",