diff --git a/app/components/Header.tsx b/app/components/Header.tsx
new file mode 100644
index 0000000..ad14623
--- /dev/null
+++ b/app/components/Header.tsx
@@ -0,0 +1,37 @@
+import { Box, Flex, Link, Heading } from "@chakra-ui/react";
+import NextLink from "next/link";
+
+const Header: React.FC = () => {
+ return (
+
+
+
+
+
+ Ballistic Builder
+
+
+
+
+
+
+ Builder
+
+
+
+
+ Products
+
+
+
+
+ Sign In
+
+
+
+
+
+ );
+};
+
+export default Header;
\ No newline at end of file
diff --git a/app/page.tsx b/app/page.tsx
index 99636a2..bfce3d8 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -47,7 +47,7 @@ export default function Home() {
@@ -64,7 +64,7 @@ export default function Home() {
About Us
- Firearm Builder is your go-to platform for customizing, building,
+ Ballistic Builder is your go-to platform for customizing, building,
and exploring firearm parts. Designed for enthusiasts by
enthusiasts, we make firearm building easy and accessible.
@@ -90,6 +90,8 @@ export default function Home() {
)
diff --git a/package-lock.json b/package-lock.json
index 0948e85..79f7dd4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,8 +8,11 @@
"name": "balistics-builder",
"version": "0.1.0",
"dependencies": {
- "@emotion/react": "^11.13.3",
+ "@chakra-ui/react": "^3.1.2",
+ "@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.0",
+ "@headlessui/react": "^2.2.0",
+ "@heroicons/react": "^2.2.0",
"@mui/icons-material": "^6.1.7",
"@mui/joy": "^5.0.0-beta.48",
"@mui/material": "^6.1.7",
@@ -21,9 +24,11 @@
"drizzle-orm": "^0.36.3",
"fontsource-roboto": "^4.0.0",
"next": "15.0.3",
+ "next-themes": "^0.4.3",
"pg": "^8.13.1",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-dom": "18.2.0",
+ "react-icons": "^5.3.0"
},
"devDependencies": {
"@types/node": "^20.17.6",
@@ -53,6 +58,68 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@ark-ui/react": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@ark-ui/react/-/react-4.4.3.tgz",
+ "integrity": "sha512-Prd5EWcyL5PIigl8H70acVacL+Abl0l9gFw0sRNAZ9+3cGaXPhL4ol3s3AW9MU811ZAESU1xifsrwMyhpmcgmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@internationalized/date": "3.5.6",
+ "@zag-js/accordion": "0.77.1",
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/auto-resize": "0.77.1",
+ "@zag-js/avatar": "0.77.1",
+ "@zag-js/carousel": "0.77.1",
+ "@zag-js/checkbox": "0.77.1",
+ "@zag-js/clipboard": "0.77.1",
+ "@zag-js/collapsible": "0.77.1",
+ "@zag-js/collection": "0.77.1",
+ "@zag-js/color-picker": "0.77.1",
+ "@zag-js/color-utils": "0.77.1",
+ "@zag-js/combobox": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/date-picker": "0.77.1",
+ "@zag-js/date-utils": "0.77.1",
+ "@zag-js/dialog": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/editable": "0.77.1",
+ "@zag-js/file-upload": "0.77.1",
+ "@zag-js/file-utils": "0.77.1",
+ "@zag-js/highlight-word": "0.77.1",
+ "@zag-js/hover-card": "0.77.1",
+ "@zag-js/i18n-utils": "0.77.1",
+ "@zag-js/menu": "0.77.1",
+ "@zag-js/number-input": "0.77.1",
+ "@zag-js/pagination": "0.77.1",
+ "@zag-js/pin-input": "0.77.1",
+ "@zag-js/popover": "0.77.1",
+ "@zag-js/presence": "0.77.1",
+ "@zag-js/progress": "0.77.1",
+ "@zag-js/qr-code": "0.77.1",
+ "@zag-js/radio-group": "0.77.1",
+ "@zag-js/rating-group": "0.77.1",
+ "@zag-js/react": "0.77.1",
+ "@zag-js/select": "0.77.1",
+ "@zag-js/signature-pad": "0.77.1",
+ "@zag-js/slider": "0.77.1",
+ "@zag-js/splitter": "0.77.1",
+ "@zag-js/steps": "0.77.1",
+ "@zag-js/switch": "0.77.1",
+ "@zag-js/tabs": "0.77.1",
+ "@zag-js/tags-input": "0.77.1",
+ "@zag-js/time-picker": "0.77.1",
+ "@zag-js/timer": "0.77.1",
+ "@zag-js/toast": "0.77.1",
+ "@zag-js/toggle-group": "0.77.1",
+ "@zag-js/tooltip": "0.77.1",
+ "@zag-js/tree-view": "0.77.1",
+ "@zag-js/types": "0.77.1"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.26.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
@@ -184,6 +251,26 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@chakra-ui/react": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.1.2.tgz",
+ "integrity": "sha512-lf3XxbP8frId1r6VZwQjEXrMY8S/FdkD/cBr3udWHXHDxy6oDDT6clSU7/BTjl2HP3eitXNUrcNdBm+X4gXTAg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ark-ui/react": "4.4.3",
+ "@emotion/is-prop-valid": "1.3.1",
+ "@emotion/serialize": "1.3.2",
+ "@emotion/use-insertion-effect-with-fallbacks": "1.1.0",
+ "@emotion/utils": "1.4.1",
+ "@pandacss/is-valid-prop": "0.41.0",
+ "csstype": "3.1.3"
+ },
+ "peerDependencies": {
+ "@emotion/react": ">=11",
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
"node_modules/@drizzle-team/brocli": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz",
@@ -200,15 +287,16 @@
}
},
"node_modules/@emotion/babel-plugin": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
- "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==",
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0",
- "@emotion/serialize": "^1.2.0",
+ "@emotion/serialize": "^1.3.3",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0",
@@ -217,18 +305,44 @@
"stylis": "4.2.0"
}
},
+ "node_modules/@emotion/babel-plugin/node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
"node_modules/@emotion/cache": {
- "version": "11.13.1",
- "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz",
- "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==",
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.5.tgz",
+ "integrity": "sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==",
+ "license": "MIT",
"dependencies": {
"@emotion/memoize": "^0.9.0",
"@emotion/sheet": "^1.4.0",
- "@emotion/utils": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
"@emotion/weak-memoize": "^0.4.0",
"stylis": "4.2.0"
}
},
+ "node_modules/@emotion/cache/node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
"node_modules/@emotion/hash": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
@@ -248,16 +362,17 @@
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
},
"node_modules/@emotion/react": {
- "version": "11.13.3",
- "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz",
- "integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==",
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.5.tgz",
+ "integrity": "sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ==",
+ "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.18.3",
- "@emotion/babel-plugin": "^11.12.0",
- "@emotion/cache": "^11.13.0",
- "@emotion/serialize": "^1.3.1",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.13.5",
+ "@emotion/serialize": "^1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
- "@emotion/utils": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
"@emotion/weak-memoize": "^0.4.0",
"hoist-non-react-statics": "^3.3.1"
},
@@ -270,6 +385,25 @@
}
}
},
+ "node_modules/@emotion/react/node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/react/node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
"node_modules/@emotion/serialize": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz",
@@ -1203,6 +1337,21 @@
"@floating-ui/utils": "^0.2.8"
}
},
+ "node_modules/@floating-ui/react": {
+ "version": "0.26.28",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz",
+ "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.1.2",
+ "@floating-ui/utils": "^0.2.8",
+ "tabbable": "^6.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
"node_modules/@floating-ui/react-dom": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
@@ -1220,6 +1369,34 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
"integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
},
+ "node_modules/@headlessui/react": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.0.tgz",
+ "integrity": "sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/react": "^0.26.16",
+ "@react-aria/focus": "^3.17.1",
+ "@react-aria/interactions": "^3.21.3",
+ "@tanstack/react-virtual": "^3.8.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^18 || ^19 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/@heroicons/react": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
+ "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">= 16 || ^19.0.0-rc"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -1597,6 +1774,24 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@internationalized/date": {
+ "version": "3.5.6",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.5.6.tgz",
+ "integrity": "sha512-jLxQjefH9VI5P9UQuqB6qNKnvFt1Ky1TPIzHGsIlCi7sZZoMR8SdYbBGRvM0y+Jtb+ez4ieBzmiAUcpmPYpyOw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@internationalized/number": {
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.5.4.tgz",
+ "integrity": "sha512-h9huwWjNqYyE2FXZZewWqmCdkw1HeFds5q4Siuoms3hUQC5iPJK3aBmkFZoDSLN4UD0Bl8G22L/NdHpeOr+/7A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -2433,6 +2628,11 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@pandacss/is-valid-prop": {
+ "version": "0.41.0",
+ "resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-0.41.0.tgz",
+ "integrity": "sha512-BE6h6CsJk14ugIRrsazJtN3fcg+KDFRat1Bs93YFKH6jd4DOb1yUyVvC70jKqPVvg70zEcV8acZ7VdcU5TLu+w=="
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -2514,6 +2714,89 @@
"@prisma/debug": "5.22.0"
}
},
+ "node_modules/@react-aria/focus": {
+ "version": "3.18.4",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.18.4.tgz",
+ "integrity": "sha512-91J35077w9UNaMK1cpMUEFRkNNz0uZjnSwiyBCFuRdaVuivO53wNC9XtWSDNDdcO5cGy87vfJRVAiyoCn/mjqA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.22.4",
+ "@react-aria/utils": "^3.25.3",
+ "@react-types/shared": "^3.25.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-aria/interactions": {
+ "version": "3.22.4",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.4.tgz",
+ "integrity": "sha512-E0vsgtpItmknq/MJELqYJwib+YN18Qag8nroqwjk1qOnBa9ROIkUhWJerLi1qs5diXq9LHKehZDXRlwPvdEFww==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.6",
+ "@react-aria/utils": "^3.25.3",
+ "@react-types/shared": "^3.25.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-aria/ssr": {
+ "version": "3.9.6",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.6.tgz",
+ "integrity": "sha512-iLo82l82ilMiVGy342SELjshuWottlb5+VefO3jOQqQRNYnJBFpUSadswDPbRimSgJUZuFwIEYs6AabkP038fA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-aria/utils": {
+ "version": "3.25.3",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.25.3.tgz",
+ "integrity": "sha512-PR5H/2vaD8fSq0H/UB9inNbc8KDcVmW6fYAfSWkkn+OAdhTTMVKqXXrZuZBWyFfSD5Ze7VN6acr4hrOQm2bmrA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.6",
+ "@react-stately/utils": "^3.10.4",
+ "@react-types/shared": "^3.25.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-stately/utils": {
+ "version": "3.10.4",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.4.tgz",
+ "integrity": "sha512-gBEQEIMRh5f60KCm7QKQ2WfvhB2gLUr9b72sqUdIZ2EG+xuPgaIlCBeSicvjmjBvYZwOjoOEnmIkcx2GHp/HWw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@react-types/shared": {
+ "version": "3.25.0",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.25.0.tgz",
+ "integrity": "sha512-OZSyhzU6vTdW3eV/mz5i6hQwQUhkRs7xwY2d1aqPvTdMe0+2cY7Fwp45PAiwYLEj73i9ro2FxF9qC4DvHGSCgQ==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -2539,6 +2822,33 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@tanstack/react-virtual": {
+ "version": "3.10.9",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.9.tgz",
+ "integrity": "sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.10.9"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.10.9",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.9.tgz",
+ "integrity": "sha512-kBknKOKzmeR7lN+vSadaKWXaLS0SZZG+oqpQ/k80Q6g9REn6zRHS/ZYdrIzHnpHgy/eWs00SujveUN/GJT2qTw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -2898,6 +3208,825 @@
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
"dev": true
},
+ "node_modules/@zag-js/accordion": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/accordion/-/accordion-0.77.1.tgz",
+ "integrity": "sha512-KEXFPZB+Z2NfdQLNDOZ5fbRzv++mIDmZdpOPjP0kur7asVhLEyhLtpBEfXKMdF1fZoYOeXT4R6loZ5fRXPfK+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/anatomy": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/anatomy/-/anatomy-0.77.1.tgz",
+ "integrity": "sha512-VMj+z4kco9zVKDEsabQDy8IYCqXdMqdZ2Z+n4IeEOV93oX7iG86vNHgZ7NXykN2jSR/Bka+LcGtAstaUvVw2dA==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/aria-hidden": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/aria-hidden/-/aria-hidden-0.77.1.tgz",
+ "integrity": "sha512-Nx8hYDXMsOfGxxLQcfL2pAo4UutE7IGdbYbacsnqbfJhg/vDyTkf4Uhy7HXvZAccGxtj5kb2WeCbtzh9lklwsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "aria-hidden": "1.2.4"
+ }
+ },
+ "node_modules/@zag-js/auto-resize": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/auto-resize/-/auto-resize-0.77.1.tgz",
+ "integrity": "sha512-CIvUaxhwuqkpS/+Q816C531deN+RT8SRzDy3YfuvKRfGtEfRRTNuwk9P2dlo6MoinfORcjvX1y4EAaBjA/lsxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-query": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/avatar": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/avatar/-/avatar-0.77.1.tgz",
+ "integrity": "sha512-wERKUzjLCElAKk6CNsBe6U4tKZNQTr9AZKOQqbONWJr6wISy7Ftu5el0Yp0SbUxmwacfB9ghdHslTbaThz190g==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/carousel": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/carousel/-/carousel-0.77.1.tgz",
+ "integrity": "sha512-sSVzQ/ZUAmJrArvkwCz1z/er9zLg3HDsyFDPvIJIqDAqZNatmKAth0Gia8wuWnz5YV1YGsLS8OeHr1lXYWvLQQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/checkbox": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/checkbox/-/checkbox-0.77.1.tgz",
+ "integrity": "sha512-PbG/IU80tN1F5V+tGzyAN54p37kS4cQ8U/MUrtBxFOGMy3kGVeVMQCX/xo9fz6H49L+2+4XVzfkTHBDyNVuSxg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/focus-visible": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/clipboard": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/clipboard/-/clipboard-0.77.1.tgz",
+ "integrity": "sha512-1eLgL3dxEIMTZhe+0fkv05PX8i2LZprLf71hLqHPcjt/DDa/g4tDpoDG9HBgEM68s8mFLB3niwbfbpVgepcR6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/collapsible": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/collapsible/-/collapsible-0.77.1.tgz",
+ "integrity": "sha512-Wh/PJCEHdt0nzpo/HqwLXHN/nC6aYZXKlV7tztTPYzUOOF5/g1QiGE0ecQEX1tpKEHME+Ro3lwwI0vAh3L6Evg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/collection": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/collection/-/collection-0.77.1.tgz",
+ "integrity": "sha512-YwdpSRy3yqFRLqOqNpkQJ6cVH3JS9MLhW+f4FKypfvz1tLLTpt/uMnKAOwoIVy+EjCuzeMwUtR7MQF/kK5y56A==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/color-picker": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/color-picker/-/color-picker-0.77.1.tgz",
+ "integrity": "sha512-NV3g5J2zQmnv4jMMkKFlzhX8vvX7W6etQX0ZfaxUGKBFaGf/Vfdow0EEyurf+QqGkxGTWRI4rZncy5/K02n9Cg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/color-utils": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/text-selection": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/color-utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/color-utils/-/color-utils-0.77.1.tgz",
+ "integrity": "sha512-6Z7zoAOQr3LprL6POV1gzA9tzzz4FHLtfo9ZqgN3SxbhFXj0xw1hhEB6COwJxqsNL9jqN2yhXBj3RBY89WsWzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/numeric-range": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/combobox": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/combobox/-/combobox-0.77.1.tgz",
+ "integrity": "sha512-uAT/ByipNCm0eNdPZJzBqqbSjtSeSHSAdSyki2puyLtl779G6vRZv44aKey+0LKxmTZYKD1neMl06dWwtdnA9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/aria-hidden": "0.77.1",
+ "@zag-js/collection": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/core": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/core/-/core-0.77.1.tgz",
+ "integrity": "sha512-tY5A/XayGdtiSutjQl4jBzoj2xdka8JD4JuzffsAT7aWJklbfiuIKc0R7dbAviRQ1vFe0Jvmrd3FZz85aJJfdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/store": "0.77.1",
+ "@zag-js/utils": "0.77.1",
+ "klona": "2.0.6"
+ }
+ },
+ "node_modules/@zag-js/date-picker": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/date-picker/-/date-picker-0.77.1.tgz",
+ "integrity": "sha512-Rci3u5YvpObAVbYKp5lUmWyvS0VFambjhZYc0avFp7MTHhRZErXKviq/q1wqvWWtfrAZKRuQrG5Rex7+E9zDMg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/date-utils": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/live-region": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/text-selection": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ },
+ "peerDependencies": {
+ "@internationalized/date": ">=3.0.0"
+ }
+ },
+ "node_modules/@zag-js/date-utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/date-utils/-/date-utils-0.77.1.tgz",
+ "integrity": "sha512-lPYI76n/PO2LZ+PVqgKqLZfYvpNTwOdGdbBFSkwBS7eUvleEd2/oi7AE1jJaKMZ3+Bf/zy1lM5e4dlY09xRFQw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@internationalized/date": ">=3.0.0"
+ }
+ },
+ "node_modules/@zag-js/dialog": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/dialog/-/dialog-0.77.1.tgz",
+ "integrity": "sha512-RaJInIhlihpPUpWheweZPfcHgDv35xvsAG75JLQgGI9NU7seTrxL6I8ADugASPr4l77dBmdu6nhC5o9AeJNEYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/aria-hidden": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/remove-scroll": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1",
+ "focus-trap": "7.6.0"
+ }
+ },
+ "node_modules/@zag-js/dismissable": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/dismissable/-/dismissable-0.77.1.tgz",
+ "integrity": "sha512-S0u3NAyVuO2DQH+B1v+e/35BHw2jgnQ+2X+RfzpunNd5Iu1mZA3dekbxPbP8U24jguRuqQiI2WFvw3YMbno9vg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/interact-outside": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/dom-event": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/dom-event/-/dom-event-0.77.1.tgz",
+ "integrity": "sha512-W5LYu/arBgHCGh3UYkkPclEYlDlZXbST+QPvma5pXv4pzkrFS0P189sLNEedE4hkIgkbIRwdaL6YJITbKD03cA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/text-selection": "0.77.1",
+ "@zag-js/types": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/dom-query": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.77.1.tgz",
+ "integrity": "sha512-hr+4lzx4wHqhunjMzAmNp7sma5K58o0ti1h5gXpei1puoeGs8epZfzjW/ZTsKyuVgH3+0f80YOC+oTK6rDAhcw==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/editable": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/editable/-/editable-0.77.1.tgz",
+ "integrity": "sha512-iSnamhmODF5LdcGkgnqQBkRP7AyfYL7mCjRY/69kQFcXtsK8psWJxQQZLDJTzylMxMHRM1EwS452NDIG0P3/6w==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/interact-outside": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/element-rect": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/element-rect/-/element-rect-0.77.1.tgz",
+ "integrity": "sha512-cHCzdtp30wrM+trYdv0kN9wqUqYc743/muob0gHanDvvbQv8TVZ/tABA6bksL/bWCXk50bm6jiAKV/7dPYdtCQ==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/element-size": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.77.1.tgz",
+ "integrity": "sha512-USzS/Q10TW02vHmWKUQ1Fizy8cQ6Aco0IWVHaKkEdzmyCJPL+XZnm5Xe9B8nDpsLt9qgR5TblB0zqqr2EqmQkw==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/file-upload": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/file-upload/-/file-upload-0.77.1.tgz",
+ "integrity": "sha512-0MaVDnAuzsL4NO1gssRutuCacFqLql76uF4qaXt6GWygmGpLP24gVfcBeXaBD2HHRB3IZ70MQx8oBq91sNaYMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/file-utils": "0.77.1",
+ "@zag-js/i18n-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/file-utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/file-utils/-/file-utils-0.77.1.tgz",
+ "integrity": "sha512-lBGdjIdoETUdDlL5NxFtKdl5aSd9JvkokuNHTj1VJjBaW1KHQjzDNMJMgPabDyekQWcIOxNok33MhtiW3y3rNA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/i18n-utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/focus-visible": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.77.1.tgz",
+ "integrity": "sha512-hQgkYDxbFuiHvV/bFQGQ278s/WXX/M+7qwr9o4If3lSsIz1U5tfUl7vg7K8cNgr9l5tWpWlb7SeGZ0bqrZWNwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-query": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/form-utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/form-utils/-/form-utils-0.77.1.tgz",
+ "integrity": "sha512-1AVpIBtAelR4i6V8yJuhVGGAT9MeTbC86ckOH23GsH73QlvK+U55G2PckF0ClWeJ1AHw/vfy4OwibAULvv6cIg==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/highlight-word": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/highlight-word/-/highlight-word-0.77.1.tgz",
+ "integrity": "sha512-71Ykri3NHAXUE689pPpAoQOxYhHGZAx0eGjpMH3ZAlmXlG5QXCAeGG3EiDY+REPY5egIkGz6woCWj0E4iKta9Q==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/hover-card": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/hover-card/-/hover-card-0.77.1.tgz",
+ "integrity": "sha512-3/pA79VSF4Z+57FD4hQt6UiSMNPL9OO1I0LryM7FhgHqgQ5HA+ICFYdgpoEwQXdYKkyhZ/LetfpXS5gw038+QQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/i18n-utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/i18n-utils/-/i18n-utils-0.77.1.tgz",
+ "integrity": "sha512-HJAaCXf6r8b72JajIEQmnekRX/7Dz2sBMrAqpvIV6dpMDjCVcyow8WgfDqE46ipdNLi2XL1lgwaW3h5ckYEL+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-query": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/interact-outside": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/interact-outside/-/interact-outside-0.77.1.tgz",
+ "integrity": "sha512-q5GhN4CPtYy/YXh8Fv8VCofuYpQ0D2X6r+/gscf4C/5QhXka8q4RwhJXjXnv+7b3jvTTjtXovZ9RqWdNw5rEcg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/live-region": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/live-region/-/live-region-0.77.1.tgz",
+ "integrity": "sha512-NqTJWRfolf343X7NeDbaFDeC96lSlAAI1BO3ALV8cRIcEO+XF7iW1/8Cdyi2mEXaCvENv7OoBR8pRxD72RqN1g==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/menu": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/menu/-/menu-0.77.1.tgz",
+ "integrity": "sha512-NZ4YfiBWpByF98IaSOwASRZHCRIyj/Xbut3F2bTtoIsG+qQYEbQ4g3qXbmkjJC1GM7AmyiI54ZlKqoNn9wGZ7w==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/rect-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/number-input": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/number-input/-/number-input-0.77.1.tgz",
+ "integrity": "sha512-/1fUh0Jrg/Lzc5ilRIsNo2/k7LUm8nXfxogef6yVADPxROUImrRfS1wQaf79L+8vibDyKGRxyPBgEcVjHX1Gaw==",
+ "license": "MIT",
+ "dependencies": {
+ "@internationalized/number": "3.5.4",
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/number-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/number-utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/number-utils/-/number-utils-0.77.1.tgz",
+ "integrity": "sha512-liP+TsEWP4GtjaaNihYe4MmLkFfI8I2TpDDnPlyo0tnCZLd1/+rNvcuU7lwVck7OOL4NX8uuRnSBP58toRKv6A==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/numeric-range": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/numeric-range/-/numeric-range-0.77.1.tgz",
+ "integrity": "sha512-ny75qTNaebomkeWUI7X86MSE7c77/Ek8Oi6wNY6Til6YugaLCm2I5P9BO25sGcYj1w3FeUz2uCxRkPMtnxamrg==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/pagination": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/pagination/-/pagination-0.77.1.tgz",
+ "integrity": "sha512-/Ud7kzamnp1F0w2ImerFjH3N9JOSS1JzPfd9BgvyfqkYXQCaUGMNBjiRidOFMTOBUW/ftwuPLZfW6f5FGLEjkQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/pin-input": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/pin-input/-/pin-input-0.77.1.tgz",
+ "integrity": "sha512-PhSfQg72lx0dzIWwqcCNZ0nHJ0QgknzE2qL/wDcOQ/J/MYReRx2lX1+RzOmFheNLV+LrAIenXOTL4xCF+8Gfig==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/popover": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/popover/-/popover-0.77.1.tgz",
+ "integrity": "sha512-9LVuyY8LjZf6v26Uvc3+uINy740cPgkcRWaiBiW8SunsyaLzcZIA6PSOIbE14XE2lEENIeBIOYbafuahM45gBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/aria-hidden": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/remove-scroll": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1",
+ "focus-trap": "7.6.0"
+ }
+ },
+ "node_modules/@zag-js/popper": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/popper/-/popper-0.77.1.tgz",
+ "integrity": "sha512-+DlFlRwuLyUiKl8i+efBYzC6UutcSt1ROHRgmGeB9zwSPvtn1pKlaUqSkxAY2lUDHU56RX8entF5RAeZ8mGwOg==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "1.6.12",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/presence": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/presence/-/presence-0.77.1.tgz",
+ "integrity": "sha512-bVgkleWPZxO3FZCBeXHSL2lTJN8ZaIwRbH2MAwdk70VxNYVtWvo3KsbiNNGR/R5PwAPf45T0x99S+sOrByqMgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/core": "0.77.1",
+ "@zag-js/types": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/progress": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/progress/-/progress-0.77.1.tgz",
+ "integrity": "sha512-wX7isF+6ExNm/ci9gMowtZa7cVMW7ss6VAqnwIpzTu8KBCo6fArD/e1EOpeUilrs1qiiDCLhDbZ07OKG0tRVSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/qr-code": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/qr-code/-/qr-code-0.77.1.tgz",
+ "integrity": "sha512-LY5GwSprGhB6wfY/3XFeENiSj+AKUmzSqR3k2KixAeE0H7amPFr27kbeEX33nCvzBE1ZAXFHPtTa3/rvneXk4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1",
+ "proxy-memoize": "3.0.1",
+ "uqr": "0.1.2"
+ }
+ },
+ "node_modules/@zag-js/radio-group": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/radio-group/-/radio-group-0.77.1.tgz",
+ "integrity": "sha512-d4KF4qaVSWO+OqdnZ4DWTNywdgRSaRENTE02nBIGwSwOVPFIP8kQCtd0W+0nVFcXR9e7BIncj1ckOzxZM/+BUA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/element-rect": "0.77.1",
+ "@zag-js/focus-visible": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/rating-group": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/rating-group/-/rating-group-0.77.1.tgz",
+ "integrity": "sha512-cBkwCHxOJyCVHDUmKqKRcwDsoYL3kGtZ0WEviUAOVFHR2ZUm24lm7+1geuPrQcEXpSBmIXNbke/jyM0+haxSDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/react": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/react/-/react-0.77.1.tgz",
+ "integrity": "sha512-clP04/bKty4FUh5oTCoQydEiMQt1TO1W7tZ+rq+H9eqstzpaHYbl/FScsioHXecl43jROdd3EPquI8TK3snlZw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/core": "0.77.1",
+ "@zag-js/store": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "proxy-compare": "3.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+ },
+ "node_modules/@zag-js/rect-utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/rect-utils/-/rect-utils-0.77.1.tgz",
+ "integrity": "sha512-AIT90ALk7yrpWu4dJTDOfWOxQNLeGDqbINt+3wz50nwVLMmF3KFG34RMPFwt1mwAYEhON4QD1JjedbL+dXfd7g==",
+ "license": "MIT"
+ },
+ "node_modules/@zag-js/remove-scroll": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/remove-scroll/-/remove-scroll-0.77.1.tgz",
+ "integrity": "sha512-dqRl2sbghzyjQY/xngrllcq4/KvhDYKpP3OV13rFjHEJJnQNYfyRrRF5b2n6W6qZmsNr+xTL+OHk2qWl+BCMvA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-query": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/select": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/select/-/select-0.77.1.tgz",
+ "integrity": "sha512-aE+g4xDegGrsdlqDLALh84stwRJwQakNXSw2Rk+gP7BtFvrZ6cHizYvaZVHoVwSn/oNAozYk/eUQMYK1HOdNuw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/collection": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/signature-pad": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/signature-pad/-/signature-pad-0.77.1.tgz",
+ "integrity": "sha512-B2muP6rhevuV27Y4A5hZt/5GR7WpaUSq7B7a/jAiYZmp8Tutmz1zRFsS9Zc9husESAhJyrtA1AkNDGQiYVau8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1",
+ "perfect-freehand": "^1.2.2"
+ }
+ },
+ "node_modules/@zag-js/slider": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/slider/-/slider-0.77.1.tgz",
+ "integrity": "sha512-AYcWiQquLyxOKsHreuw+KVf6MEOmBGYuq9qlXm62ZoI5OZIgxKUEw69P8IhP3afowXnrrhq8gnqgEj7W//dDSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/element-size": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/numeric-range": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/splitter": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/splitter/-/splitter-0.77.1.tgz",
+ "integrity": "sha512-KaNM/3vHAdl2otVzu2G+Y24tqvAy0r3n1yLvU5lNIkDwlr+gwNWJy0cMOXf3DFokhI5ijMbtuux8dFT7Wmib+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/number-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/steps": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/steps/-/steps-0.77.1.tgz",
+ "integrity": "sha512-CbVlWNQkHy+SRzTWTKd0sWvKXfg112ped6/I6ei/tSC4vqJdFSm9/QRXGvFiSy06wOoN3Oqlw93KlwbdpEhH+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/store": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/store/-/store-0.77.1.tgz",
+ "integrity": "sha512-qk9uuXehAiq9BG0Rhd6nGwYI1WiXa3KcFydxbiMnlGiET8/zAeNTw5biYW5riptAmZ6xiwVUNtzg0T58+3YIag==",
+ "license": "MIT",
+ "dependencies": {
+ "proxy-compare": "3.0.0"
+ }
+ },
+ "node_modules/@zag-js/switch": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/switch/-/switch-0.77.1.tgz",
+ "integrity": "sha512-GbIdY+Ph3XZWISOCQ3/MM+tbq/EnyEGGs1falAlVmuaVfS1gGsa9p8NKjy2mlrE+Ho8aScZgSYZfzoZfFVcWDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/focus-visible": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/tabs": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/tabs/-/tabs-0.77.1.tgz",
+ "integrity": "sha512-YEL+Vyx2c6sp3qj3rgb9X81gBPOrCGke1OshZMkv6nUhmzVvajfAwKdwbTKSZ4PwLTPAkfyjd8t1MFdWdutCKg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/element-rect": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/tags-input": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/tags-input/-/tags-input-0.77.1.tgz",
+ "integrity": "sha512-+N+vtfRDNzAngqT+zk5PwoXJafaIQWioEAEMvIJYn77DNZU+Vi0Du9T1O9/hDcI75/cPtdXCIE0oor+fWDHneA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/auto-resize": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/form-utils": "0.77.1",
+ "@zag-js/interact-outside": "0.77.1",
+ "@zag-js/live-region": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/text-selection": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/text-selection/-/text-selection-0.77.1.tgz",
+ "integrity": "sha512-5bg4qvEQCQBTW7Ow4yuzumgt0fWWRSqRXaOr/27xDuyTgq7pCQzH5Yfg0pWoQGBMop9djrxN3Z1XrESbXJyZEA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/dom-query": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/time-picker": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/time-picker/-/time-picker-0.77.1.tgz",
+ "integrity": "sha512-Dq7SD/CBv5qrknxx3t5b/cotmS6eZx5BCPkXQfKIC8jajdpSSLsWq891RSrEk7zTAGjx5iY1q3VSGT5EyPEIOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-event": "^0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ },
+ "peerDependencies": {
+ "@internationalized/date": ">=3.0.0"
+ }
+ },
+ "node_modules/@zag-js/timer": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/timer/-/timer-0.77.1.tgz",
+ "integrity": "sha512-INSMVQYJCkvEgy4bvr0g+PUPvtetm0Zrh9wC29UqgbQKpdcsvFKI8yDu3Sm4Mk9dp0AkMhS2GhT92r+TeHLomg==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/toast": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/toast/-/toast-0.77.1.tgz",
+ "integrity": "sha512-ohaoox2TXf0NpC4W3mNKgjyZGg+Zz/+QeQBtglcIBLyr39o/pkrK3wHc27+twKciu4ZcWC5jucsR6lo9A12wbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dismissable": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/toggle-group": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/toggle-group/-/toggle-group-0.77.1.tgz",
+ "integrity": "sha512-wQXUBClzBmPHL0jqTOXD78mmlIABObxgqHG3jMgutl/7TqPMk65jatR0piWxkAF8dn+Oav5HLIOaHFKR/m+RCw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/tooltip": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/tooltip/-/tooltip-0.77.1.tgz",
+ "integrity": "sha512-0Vu9rC9StV+QrXMsGiOOvGY3NIVqKQt1oh5AaFyIo/SglnJ2UvYB7c/ERMSyW/YoTi/Pv7+7kaZzitR2JGQ+Cw==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/focus-visible": "0.77.1",
+ "@zag-js/popper": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/tree-view": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/tree-view/-/tree-view-0.77.1.tgz",
+ "integrity": "sha512-3Otb+pVB7KFbCs4Xi4w6mU0sYz3z/+CaTQp3jN6VRNzUMSCVKRar/NuZbnmCExj+4iLUEvANrOlkneBr6stFpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@zag-js/anatomy": "0.77.1",
+ "@zag-js/collection": "0.77.1",
+ "@zag-js/core": "0.77.1",
+ "@zag-js/dom-event": "0.77.1",
+ "@zag-js/dom-query": "0.77.1",
+ "@zag-js/types": "0.77.1",
+ "@zag-js/utils": "0.77.1"
+ }
+ },
+ "node_modules/@zag-js/types": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/types/-/types-0.77.1.tgz",
+ "integrity": "sha512-GtZKdiltPDxp19qmXa/L+a1ffL67bmSxAPlT/wVv2G7uLtL82GKKT86m2yaUqKq+VUE47kXjarj9pTcTrwTSVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "3.1.3"
+ }
+ },
+ "node_modules/@zag-js/utils": {
+ "version": "0.77.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/utils/-/utils-0.77.1.tgz",
+ "integrity": "sha512-sYCRwWQlQeYuRUvuDX0ji6Dnt/Ld6bIbVXV7NtbHCpz/G0sOnVaHJLTOoIFt1KEIrm9QvDtj/JFJGNi9Jc1Bew==",
+ "license": "MIT"
+ },
"node_modules/acorn": {
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
@@ -2990,6 +4119,18 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
+ "node_modules/aria-hidden": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz",
+ "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==",
+ "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",
@@ -5131,6 +6272,15 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
+ "node_modules/focus-trap": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.0.tgz",
+ "integrity": "sha512-1td0l3pMkWJLFipobUcGaf+5DTY4PLDDrcqoSaKP8ediO/CoWCCYk/fT/Y2A4e6TNB+Sh6clRJCjOPPnKoNHnQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tabbable": "^6.2.0"
+ }
+ },
"node_modules/fontsource-roboto": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fontsource-roboto/-/fontsource-roboto-4.0.0.tgz",
@@ -6133,6 +7283,15 @@
"json-buffer": "3.0.1"
}
},
+ "node_modules/klona": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
+ "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/language-subtag-registry": {
"version": "0.3.23",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
@@ -6360,6 +7519,16 @@
}
}
},
+ "node_modules/next-themes": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.3.tgz",
+ "integrity": "sha512-nG84VPkTdUHR2YeD89YchvV4I9RbiMAql3GiLEQlPvq1ioaqPaIReK+yMRdg/zgiXws620qS1rU30TiWmmG9lA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
+ }
+ },
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -6682,6 +7851,12 @@
"node": ">=8"
}
},
+ "node_modules/perfect-freehand": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.2.tgz",
+ "integrity": "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==",
+ "license": "MIT"
+ },
"node_modules/pg": {
"version": "8.13.1",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz",
@@ -7050,6 +8225,21 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/proxy-compare": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.0.tgz",
+ "integrity": "sha512-y44MCkgtZUCT9tZGuE278fB7PWVf7fRYy0vbRXAts2o5F0EfC4fIQrvQQGBJo1WJbFcVLXzApOscyJuZqHQc1w==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-memoize": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/proxy-memoize/-/proxy-memoize-3.0.1.tgz",
+ "integrity": "sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g==",
+ "license": "MIT",
+ "dependencies": {
+ "proxy-compare": "^3.0.0"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -7102,6 +8292,15 @@
"react": "^18.2.0"
}
},
+ "node_modules/react-icons": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
+ "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@@ -7837,6 +9036,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/tabbable": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==",
+ "license": "MIT"
+ },
"node_modules/tailwindcss": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz",
@@ -8170,6 +9375,12 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/uqr": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz",
+ "integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==",
+ "license": "MIT"
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
diff --git a/package.json b/package.json
index ffb31f2..66d9e70 100644
--- a/package.json
+++ b/package.json
@@ -9,8 +9,11 @@
"lint": "next lint"
},
"dependencies": {
- "@emotion/react": "^11.13.3",
+ "@chakra-ui/react": "^3.1.2",
+ "@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.0",
+ "@headlessui/react": "^2.2.0",
+ "@heroicons/react": "^2.2.0",
"@mui/icons-material": "^6.1.7",
"@mui/joy": "^5.0.0-beta.48",
"@mui/material": "^6.1.7",
@@ -22,9 +25,11 @@
"drizzle-orm": "^0.36.3",
"fontsource-roboto": "^4.0.0",
"next": "15.0.3",
+ "next-themes": "^0.4.3",
"pg": "^8.13.1",
"react": "18.2.0",
- "react-dom": "18.2.0"
+ "react-dom": "18.2.0",
+ "react-icons": "^5.3.0"
},
"devDependencies": {
"@types/node": "^20.17.6",
diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx
new file mode 100644
index 0000000..d8763da
--- /dev/null
+++ b/src/components/ui/accordion.tsx
@@ -0,0 +1,47 @@
+import { Accordion, HStack } from "@chakra-ui/react"
+import * as React from "react"
+import { LuChevronDown } from "react-icons/lu"
+
+interface AccordionItemTriggerProps extends Accordion.ItemTriggerProps {
+ indicatorPlacement?: "start" | "end"
+}
+
+export const AccordionItemTrigger = React.forwardRef<
+ HTMLButtonElement,
+ AccordionItemTriggerProps
+>(function AccordionItemTrigger(props, ref) {
+ const { children, indicatorPlacement = "end", ...rest } = props
+ return (
+
+ {indicatorPlacement === "start" && (
+
+
+
+ )}
+
+ {children}
+
+ {indicatorPlacement === "end" && (
+
+
+
+ )}
+
+ )
+})
+
+interface AccordionItemContentProps extends Accordion.ItemContentProps {}
+
+export const AccordionItemContent = React.forwardRef<
+ HTMLDivElement,
+ AccordionItemContentProps
+>(function AccordionItemContent(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const AccordionRoot = Accordion.Root
+export const AccordionItem = Accordion.Item
diff --git a/src/components/ui/action-bar.tsx b/src/components/ui/action-bar.tsx
new file mode 100644
index 0000000..98f798a
--- /dev/null
+++ b/src/components/ui/action-bar.tsx
@@ -0,0 +1,40 @@
+import { ActionBar, Portal } from "@chakra-ui/react"
+import { CloseButton } from "./close-button"
+import * as React from "react"
+
+interface ActionBarContentProps extends ActionBar.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+}
+
+export const ActionBarContent = React.forwardRef<
+ HTMLDivElement,
+ ActionBarContentProps
+>(function ActionBarContent(props, ref) {
+ const { children, portalled = true, portalRef, ...rest } = props
+
+ return (
+
+
+
+ {children}
+
+
+
+ )
+})
+
+export const ActionBarCloseTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ActionBar.CloseTriggerProps
+>(function ActionBarCloseTrigger(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const ActionBarRoot = ActionBar.Root
+export const ActionBarSelectionTrigger = ActionBar.SelectionTrigger
+export const ActionBarSeparator = ActionBar.Separator
diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx
new file mode 100644
index 0000000..5c1cd6f
--- /dev/null
+++ b/src/components/ui/alert.tsx
@@ -0,0 +1,51 @@
+import { Alert as ChakraAlert } from "@chakra-ui/react"
+import { CloseButton } from "./close-button"
+import * as React from "react"
+
+export interface AlertProps extends Omit {
+ startElement?: React.ReactNode
+ endElement?: React.ReactNode
+ title?: React.ReactNode
+ icon?: React.ReactElement
+ closable?: boolean
+ onClose?: () => void
+}
+
+export const Alert = React.forwardRef(
+ function Alert(props, ref) {
+ const {
+ title,
+ children,
+ icon,
+ closable,
+ onClose,
+ startElement,
+ endElement,
+ ...rest
+ } = props
+ return (
+
+ {startElement || {icon} }
+ {children ? (
+
+ {title}
+ {children}
+
+ ) : (
+ {title}
+ )}
+ {endElement}
+ {closable && (
+
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
new file mode 100644
index 0000000..cd84664
--- /dev/null
+++ b/src/components/ui/avatar.tsx
@@ -0,0 +1,74 @@
+"use client"
+
+import type { GroupProps, SlotRecipeProps } from "@chakra-ui/react"
+import { Avatar as ChakraAvatar, Group } from "@chakra-ui/react"
+import * as React from "react"
+
+type ImageProps = React.ImgHTMLAttributes
+
+export interface AvatarProps extends ChakraAvatar.RootProps {
+ name?: string
+ src?: string
+ srcSet?: string
+ loading?: ImageProps["loading"]
+ icon?: React.ReactElement
+ fallback?: React.ReactNode
+}
+
+export const Avatar = React.forwardRef(
+ function Avatar(props, ref) {
+ const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
+ props
+ return (
+
+
+ {fallback}
+
+
+ {children}
+
+ )
+ },
+)
+
+interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
+ name?: string
+ icon?: React.ReactElement
+}
+
+const AvatarFallback = React.forwardRef(
+ function AvatarFallback(props, ref) {
+ const { name, icon, children, ...rest } = props
+ return (
+
+ {children}
+ {name != null && children == null && <>{getInitials(name)}>}
+ {name == null && children == null && (
+ {icon}
+ )}
+
+ )
+ },
+)
+
+function getInitials(name: string) {
+ const names = name.trim().split(" ")
+ const firstName = names[0] != null ? names[0] : ""
+ const lastName = names.length > 1 ? names[names.length - 1] : ""
+ return firstName && lastName
+ ? `${firstName.charAt(0)}${lastName.charAt(0)}`
+ : firstName.charAt(0)
+}
+
+interface AvatarGroupProps extends GroupProps, SlotRecipeProps<"avatar"> {}
+
+export const AvatarGroup = React.forwardRef(
+ function AvatarGroup(props, ref) {
+ const { size, variant, borderless, ...rest } = props
+ return (
+
+
+
+ )
+ },
+)
diff --git a/src/components/ui/blockquote.tsx b/src/components/ui/blockquote.tsx
new file mode 100644
index 0000000..166446b
--- /dev/null
+++ b/src/components/ui/blockquote.tsx
@@ -0,0 +1,31 @@
+import { Blockquote as ChakraBlockquote } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface BlockquoteProps extends ChakraBlockquote.RootProps {
+ cite?: React.ReactNode
+ citeUrl?: string
+ icon?: React.ReactNode
+ showDash?: boolean
+}
+
+export const Blockquote = React.forwardRef(
+ function Blockquote(props, ref) {
+ const { children, cite, citeUrl, showDash, icon, ...rest } = props
+
+ return (
+
+ {icon}
+
+ {children}
+
+ {cite && (
+
+ {showDash ? <>—> : null} {cite}
+
+ )}
+
+ )
+ },
+)
+
+export const BlockquoteIcon = ChakraBlockquote.Icon
diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx
new file mode 100644
index 0000000..960e769
--- /dev/null
+++ b/src/components/ui/breadcrumb.tsx
@@ -0,0 +1,40 @@
+import { Breadcrumb, type SystemStyleObject } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface BreadcrumbRootProps extends Breadcrumb.RootProps {
+ separator?: React.ReactNode
+ separatorGap?: SystemStyleObject["gap"]
+}
+
+export const BreadcrumbRoot = React.forwardRef<
+ HTMLDivElement,
+ BreadcrumbRootProps
+>(function BreadcrumbRoot(props, ref) {
+ const { separator, separatorGap, children, ...rest } = props
+
+ const validChildren = React.Children.toArray(children).filter(
+ React.isValidElement,
+ )
+
+ return (
+
+
+ {validChildren.map((child, index) => {
+ const last = index === validChildren.length - 1
+ return (
+
+ {child}
+ {!last && (
+ {separator}
+ )}
+
+ )
+ })}
+
+
+ )
+})
+
+export const BreadcrumbLink = Breadcrumb.Link
+export const BreadcrumbCurrentLink = Breadcrumb.CurrentLink
+export const BreadcrumbEllipsis = Breadcrumb.Ellipsis
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
new file mode 100644
index 0000000..21d5f4b
--- /dev/null
+++ b/src/components/ui/button.tsx
@@ -0,0 +1,40 @@
+import type { ButtonProps as ChakraButtonProps } from "@chakra-ui/react"
+import {
+ AbsoluteCenter,
+ Button as ChakraButton,
+ Span,
+ Spinner,
+} from "@chakra-ui/react"
+import * as React from "react"
+
+interface ButtonLoadingProps {
+ loading?: boolean
+ loadingText?: React.ReactNode
+}
+
+export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {}
+
+export const Button = React.forwardRef(
+ function Button(props, ref) {
+ const { loading, disabled, loadingText, children, ...rest } = props
+ return (
+
+ {loading && !loadingText ? (
+ <>
+
+
+
+ {children}
+ >
+ ) : loading && loadingText ? (
+ <>
+
+ {loadingText}
+ >
+ ) : (
+ children
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/checkbox-card.tsx b/src/components/ui/checkbox-card.tsx
new file mode 100644
index 0000000..063925b
--- /dev/null
+++ b/src/components/ui/checkbox-card.tsx
@@ -0,0 +1,58 @@
+import { CheckboxCard as ChakraCheckboxCard } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface CheckboxCardProps extends ChakraCheckboxCard.RootProps {
+ icon?: React.ReactElement
+ label?: React.ReactNode
+ description?: React.ReactNode
+ addon?: React.ReactNode
+ indicator?: React.ReactNode | null
+ indicatorPlacement?: "start" | "end" | "inside"
+ inputProps?: React.InputHTMLAttributes
+}
+
+export const CheckboxCard = React.forwardRef<
+ HTMLInputElement,
+ CheckboxCardProps
+>(function CheckboxCard(props, ref) {
+ const {
+ inputProps,
+ label,
+ description,
+ icon,
+ addon,
+ indicator = ,
+ indicatorPlacement = "end",
+ ...rest
+ } = props
+
+ const hasContent = label || description || icon
+ const ContentWrapper = indicator ? ChakraCheckboxCard.Content : React.Fragment
+
+ return (
+
+
+
+ {indicatorPlacement === "start" && indicator}
+ {hasContent && (
+
+ {icon}
+ {label && (
+ {label}
+ )}
+ {description && (
+
+ {description}
+
+ )}
+ {indicatorPlacement === "inside" && indicator}
+
+ )}
+ {indicatorPlacement === "end" && indicator}
+
+ {addon && {addon} }
+
+ )
+})
+
+export const CheckboxCardIndicator = ChakraCheckboxCard.Indicator
diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..2a27c2f
--- /dev/null
+++ b/src/components/ui/checkbox.tsx
@@ -0,0 +1,25 @@
+import { Checkbox as ChakraCheckbox } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface CheckboxProps extends ChakraCheckbox.RootProps {
+ icon?: React.ReactNode
+ inputProps?: React.InputHTMLAttributes
+ rootRef?: React.Ref
+}
+
+export const Checkbox = React.forwardRef(
+ function Checkbox(props, ref) {
+ const { icon, children, inputProps, rootRef, ...rest } = props
+ return (
+
+
+
+ {icon || }
+
+ {children != null && (
+ {children}
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/clipboard.tsx b/src/components/ui/clipboard.tsx
new file mode 100644
index 0000000..958cb59
--- /dev/null
+++ b/src/components/ui/clipboard.tsx
@@ -0,0 +1,108 @@
+import type { ButtonProps, InputProps } from "@chakra-ui/react"
+import {
+ Button,
+ Clipboard as ChakraClipboard,
+ IconButton,
+ Input,
+} from "@chakra-ui/react"
+import * as React from "react"
+import { LuCheck, LuClipboard, LuLink } from "react-icons/lu"
+
+const ClipboardIcon = React.forwardRef<
+ HTMLDivElement,
+ ChakraClipboard.IndicatorProps
+>(function ClipboardIcon(props, ref) {
+ return (
+ } {...props} ref={ref}>
+
+
+ )
+})
+
+const ClipboardCopyText = React.forwardRef<
+ HTMLDivElement,
+ ChakraClipboard.IndicatorProps
+>(function ClipboardCopyText(props, ref) {
+ return (
+
+ Copy
+
+ )
+})
+
+export const ClipboardLabel = React.forwardRef<
+ HTMLLabelElement,
+ ChakraClipboard.LabelProps
+>(function ClipboardLabel(props, ref) {
+ return (
+
+ )
+})
+
+export const ClipboardButton = React.forwardRef(
+ function ClipboardButton(props, ref) {
+ return (
+
+
+
+
+
+
+ )
+ },
+)
+
+export const ClipboardLink = React.forwardRef(
+ function ClipboardLink(props, ref) {
+ return (
+
+
+
+
+
+
+ )
+ },
+)
+
+export const ClipboardIconButton = React.forwardRef<
+ HTMLButtonElement,
+ ButtonProps
+>(function ClipboardIconButton(props, ref) {
+ return (
+
+
+
+
+
+
+ )
+})
+
+export const ClipboardInput = React.forwardRef(
+ function ClipboardInputElement(props, ref) {
+ return (
+
+
+
+ )
+ },
+)
+
+export const ClipboardRoot = ChakraClipboard.Root
diff --git a/src/components/ui/close-button.tsx b/src/components/ui/close-button.tsx
new file mode 100644
index 0000000..8a99007
--- /dev/null
+++ b/src/components/ui/close-button.tsx
@@ -0,0 +1,17 @@
+import type { ButtonProps as ChakraCloseButtonProps } from "@chakra-ui/react"
+import { IconButton as ChakraIconButton } from "@chakra-ui/react"
+import * as React from "react"
+import { LuX } from "react-icons/lu"
+
+export interface CloseButtonProps extends ChakraCloseButtonProps {}
+
+export const CloseButton = React.forwardRef<
+ HTMLButtonElement,
+ CloseButtonProps
+>(function CloseButton(props, ref) {
+ return (
+
+ {props.children ?? }
+
+ )
+})
diff --git a/src/components/ui/color-mode.tsx b/src/components/ui/color-mode.tsx
new file mode 100644
index 0000000..a34b968
--- /dev/null
+++ b/src/components/ui/color-mode.tsx
@@ -0,0 +1,67 @@
+"use client"
+
+import type { IconButtonProps } from "@chakra-ui/react"
+import { ClientOnly, IconButton, Skeleton } from "@chakra-ui/react"
+import { ThemeProvider, useTheme } from "next-themes"
+import type { ThemeProviderProps } from "next-themes"
+import * as React from "react"
+import { LuMoon, LuSun } from "react-icons/lu"
+
+export interface ColorModeProviderProps extends ThemeProviderProps {}
+
+export function ColorModeProvider(props: ColorModeProviderProps) {
+ return (
+
+ )
+}
+
+export function useColorMode() {
+ const { resolvedTheme, setTheme } = useTheme()
+ const toggleColorMode = () => {
+ setTheme(resolvedTheme === "light" ? "dark" : "light")
+ }
+ return {
+ colorMode: resolvedTheme,
+ setColorMode: setTheme,
+ toggleColorMode,
+ }
+}
+
+export function useColorModeValue(light: T, dark: T) {
+ const { colorMode } = useColorMode()
+ return colorMode === "light" ? light : dark
+}
+
+export function ColorModeIcon() {
+ const { colorMode } = useColorMode()
+ return colorMode === "light" ? :
+}
+
+interface ColorModeButtonProps extends Omit {}
+
+export const ColorModeButton = React.forwardRef<
+ HTMLButtonElement,
+ ColorModeButtonProps
+>(function ColorModeButton(props, ref) {
+ const { toggleColorMode } = useColorMode()
+ return (
+ }>
+
+
+
+
+ )
+})
diff --git a/src/components/ui/data-list.tsx b/src/components/ui/data-list.tsx
new file mode 100644
index 0000000..ef7cae8
--- /dev/null
+++ b/src/components/ui/data-list.tsx
@@ -0,0 +1,30 @@
+import { DataList as ChakraDataList } from "@chakra-ui/react"
+import { InfoTip } from "./toggle-tip"
+import * as React from "react"
+
+export const DataListRoot = ChakraDataList.Root
+
+interface ItemProps extends ChakraDataList.ItemProps {
+ label: React.ReactNode
+ value: React.ReactNode
+ info?: React.ReactNode
+ grow?: boolean
+}
+
+export const DataListItem = React.forwardRef(
+ function DataListItem(props, ref) {
+ const { label, info, value, children, grow, ...rest } = props
+ return (
+
+
+ {label}
+ {info && {info} }
+
+
+ {value}
+
+ {children}
+
+ )
+ },
+)
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..89d68a5
--- /dev/null
+++ b/src/components/ui/dialog.tsx
@@ -0,0 +1,62 @@
+import { Dialog as ChakraDialog, Portal } from "@chakra-ui/react"
+import { CloseButton } from "./close-button"
+import * as React from "react"
+
+interface DialogContentProps extends ChakraDialog.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+ backdrop?: boolean
+}
+
+export const DialogContent = React.forwardRef<
+ HTMLDivElement,
+ DialogContentProps
+>(function DialogContent(props, ref) {
+ const {
+ children,
+ portalled = true,
+ portalRef,
+ backdrop = true,
+ ...rest
+ } = props
+
+ return (
+
+ {backdrop && }
+
+
+ {children}
+
+
+
+ )
+})
+
+export const DialogCloseTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraDialog.CloseTriggerProps
+>(function DialogCloseTrigger(props, ref) {
+ return (
+
+
+ {props.children}
+
+
+ )
+})
+
+export const DialogRoot = ChakraDialog.Root
+export const DialogFooter = ChakraDialog.Footer
+export const DialogHeader = ChakraDialog.Header
+export const DialogBody = ChakraDialog.Body
+export const DialogBackdrop = ChakraDialog.Backdrop
+export const DialogTitle = ChakraDialog.Title
+export const DialogDescription = ChakraDialog.Description
+export const DialogTrigger = ChakraDialog.Trigger
+export const DialogActionTrigger = ChakraDialog.ActionTrigger
diff --git a/src/components/ui/drawer.tsx b/src/components/ui/drawer.tsx
new file mode 100644
index 0000000..ccb96c8
--- /dev/null
+++ b/src/components/ui/drawer.tsx
@@ -0,0 +1,52 @@
+import { Drawer as ChakraDrawer, Portal } from "@chakra-ui/react"
+import { CloseButton } from "./close-button"
+import * as React from "react"
+
+interface DrawerContentProps extends ChakraDrawer.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+ offset?: ChakraDrawer.ContentProps["padding"]
+}
+
+export const DrawerContent = React.forwardRef<
+ HTMLDivElement,
+ DrawerContentProps
+>(function DrawerContent(props, ref) {
+ const { children, portalled = true, portalRef, offset, ...rest } = props
+ return (
+
+
+
+ {children}
+
+
+
+ )
+})
+
+export const DrawerCloseTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraDrawer.CloseTriggerProps
+>(function DrawerCloseTrigger(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const DrawerTrigger = ChakraDrawer.Trigger
+export const DrawerRoot = ChakraDrawer.Root
+export const DrawerFooter = ChakraDrawer.Footer
+export const DrawerHeader = ChakraDrawer.Header
+export const DrawerBody = ChakraDrawer.Body
+export const DrawerBackdrop = ChakraDrawer.Backdrop
+export const DrawerDescription = ChakraDrawer.Description
+export const DrawerTitle = ChakraDrawer.Title
+export const DrawerActionTrigger = ChakraDrawer.ActionTrigger
diff --git a/src/components/ui/empty-state.tsx b/src/components/ui/empty-state.tsx
new file mode 100644
index 0000000..d643e53
--- /dev/null
+++ b/src/components/ui/empty-state.tsx
@@ -0,0 +1,34 @@
+import { EmptyState as ChakraEmptyState, VStack } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface EmptyStateProps extends ChakraEmptyState.RootProps {
+ title: string
+ description?: string
+ icon?: React.ReactNode
+}
+
+export const EmptyState = React.forwardRef(
+ function EmptyState(props, ref) {
+ const { title, description, icon, children, ...rest } = props
+ return (
+
+
+ {icon && (
+ {icon}
+ )}
+ {description ? (
+
+ {title}
+
+ {description}
+
+
+ ) : (
+ {title}
+ )}
+ {children}
+
+
+ )
+ },
+)
diff --git a/src/components/ui/field.tsx b/src/components/ui/field.tsx
new file mode 100644
index 0000000..dd3b66f
--- /dev/null
+++ b/src/components/ui/field.tsx
@@ -0,0 +1,33 @@
+import { Field as ChakraField } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface FieldProps extends Omit {
+ label?: React.ReactNode
+ helperText?: React.ReactNode
+ errorText?: React.ReactNode
+ optionalText?: React.ReactNode
+}
+
+export const Field = React.forwardRef(
+ function Field(props, ref) {
+ const { label, children, helperText, errorText, optionalText, ...rest } =
+ props
+ return (
+
+ {label && (
+
+ {label}
+
+
+ )}
+ {children}
+ {helperText && (
+ {helperText}
+ )}
+ {errorText && (
+ {errorText}
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/file-button.tsx b/src/components/ui/file-button.tsx
new file mode 100644
index 0000000..29e5220
--- /dev/null
+++ b/src/components/ui/file-button.tsx
@@ -0,0 +1,170 @@
+"use client"
+
+import type { ButtonProps, RecipeProps } from "@chakra-ui/react"
+import {
+ Button,
+ FileUpload as ChakraFileUpload,
+ Icon,
+ IconButton,
+ Span,
+ Text,
+ useFileUploadContext,
+ useRecipe,
+} from "@chakra-ui/react"
+import * as React from "react"
+import { LuFile, LuUpload, LuX } from "react-icons/lu"
+
+export interface FileUploadRootProps extends ChakraFileUpload.RootProps {
+ inputProps?: React.InputHTMLAttributes
+}
+
+export const FileUploadRoot = React.forwardRef<
+ HTMLInputElement,
+ FileUploadRootProps
+>(function FileUploadRoot(props, ref) {
+ const { children, inputProps, ...rest } = props
+ return (
+
+
+ {children}
+
+ )
+})
+
+export interface FileUploadDropzoneProps
+ extends ChakraFileUpload.DropzoneProps {
+ label: React.ReactNode
+ description?: React.ReactNode
+}
+
+export const FileUploadDropzone = React.forwardRef<
+ HTMLInputElement,
+ FileUploadDropzoneProps
+>(function FileUploadDropzone(props, ref) {
+ const { children, label, description, ...rest } = props
+ return (
+
+
+
+
+
+ {label}
+ {description && {description} }
+
+ {children}
+
+ )
+})
+
+interface VisibilityProps {
+ showSize?: boolean
+ clearable?: boolean
+}
+
+interface FileUploadItemProps extends VisibilityProps {
+ file: File
+}
+
+const FileUploadItem = React.forwardRef(
+ function FileUploadItem(props, ref) {
+ const { file, showSize, clearable } = props
+ return (
+
+
+
+
+
+
+
+ {showSize ? (
+
+
+
+
+ ) : (
+
+ )}
+
+ {clearable && (
+
+
+
+
+
+ )}
+
+ )
+ },
+)
+
+interface FileUploadListProps
+ extends VisibilityProps,
+ ChakraFileUpload.ItemGroupProps {
+ files?: File[]
+}
+
+export const FileUploadList = React.forwardRef<
+ HTMLUListElement,
+ FileUploadListProps
+>(function FileUploadList(props, ref) {
+ const { showSize, clearable, files, ...rest } = props
+
+ const fileUpload = useFileUploadContext()
+ const acceptedFiles = files ?? fileUpload.acceptedFiles
+
+ if (acceptedFiles.length === 0) return null
+
+ return (
+
+ {acceptedFiles.map((file) => (
+
+ ))}
+
+ )
+})
+
+type Assign = Omit & U
+
+interface FileInputProps extends Assign> {
+ placeholder?: React.ReactNode
+}
+
+export const FileInput = React.forwardRef(
+ function FileInput(props, ref) {
+ const inputRecipe = useRecipe({ key: "input" })
+ const [recipeProps, restProps] = inputRecipe.splitVariantProps(props)
+ const { placeholder = "Select file(s)", ...rest } = restProps
+ return (
+
+
+
+ {({ acceptedFiles }) => {
+ if (acceptedFiles.length === 1) {
+ return {acceptedFiles[0].name}
+ }
+ if (acceptedFiles.length > 1) {
+ return {acceptedFiles.length} files
+ }
+ return {placeholder}
+ }}
+
+
+
+ )
+ },
+)
+
+export const FileUploadLabel = ChakraFileUpload.Label
+export const FileUploadClearTrigger = ChakraFileUpload.ClearTrigger
+export const FileUploadTrigger = ChakraFileUpload.Trigger
diff --git a/src/components/ui/hover-card.tsx b/src/components/ui/hover-card.tsx
new file mode 100644
index 0000000..36299c1
--- /dev/null
+++ b/src/components/ui/hover-card.tsx
@@ -0,0 +1,36 @@
+import { HoverCard, Portal } from "@chakra-ui/react"
+import * as React from "react"
+
+interface HoverCardContentProps extends HoverCard.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+}
+
+export const HoverCardContent = React.forwardRef<
+ HTMLDivElement,
+ HoverCardContentProps
+>(function HoverCardContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props
+
+ return (
+
+
+
+
+
+ )
+})
+
+export const HoverCardArrow = React.forwardRef<
+ HTMLDivElement,
+ HoverCard.ArrowProps
+>(function HoverCardArrow(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const HoverCardRoot = HoverCard.Root
+export const HoverCardTrigger = HoverCard.Trigger
diff --git a/src/components/ui/input-group.tsx b/src/components/ui/input-group.tsx
new file mode 100644
index 0000000..1124a61
--- /dev/null
+++ b/src/components/ui/input-group.tsx
@@ -0,0 +1,50 @@
+import type { BoxProps, InputElementProps } from "@chakra-ui/react"
+import { Group, InputElement } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface InputGroupProps extends BoxProps {
+ startElementProps?: InputElementProps
+ endElementProps?: InputElementProps
+ startElement?: React.ReactNode
+ endElement?: React.ReactNode
+ children: React.ReactElement
+ startOffset?: InputElementProps["paddingStart"]
+ endOffset?: InputElementProps["paddingEnd"]
+}
+
+export const InputGroup = React.forwardRef(
+ function InputGroup(props, ref) {
+ const {
+ startElement,
+ startElementProps,
+ endElement,
+ endElementProps,
+ children,
+ startOffset = "6px",
+ endOffset = "6px",
+ ...rest
+ } = props
+
+ return (
+
+ {startElement && (
+
+ {startElement}
+
+ )}
+ {React.cloneElement(children, {
+ ...(startElement && {
+ ps: `calc(var(--input-height) - ${startOffset})`,
+ }),
+ ...(endElement && { pe: `calc(var(--input-height) - ${endOffset})` }),
+ ...children.props,
+ })}
+ {endElement && (
+
+ {endElement}
+
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/link-button.tsx b/src/components/ui/link-button.tsx
new file mode 100644
index 0000000..defa1c3
--- /dev/null
+++ b/src/components/ui/link-button.tsx
@@ -0,0 +1,12 @@
+"use client"
+
+import type { HTMLChakraProps, RecipeProps } from "@chakra-ui/react"
+import { createRecipeContext } from "@chakra-ui/react"
+
+export interface LinkButtonProps
+ extends HTMLChakraProps<"a", RecipeProps<"button">> {}
+
+const { withContext } = createRecipeContext({ key: "button" })
+
+// Replace "a" with your framework's link component
+export const LinkButton = withContext("a")
diff --git a/src/components/ui/menu.tsx b/src/components/ui/menu.tsx
new file mode 100644
index 0000000..763005b
--- /dev/null
+++ b/src/components/ui/menu.tsx
@@ -0,0 +1,110 @@
+"use client"
+
+import { AbsoluteCenter, Menu as ChakraMenu, Portal } from "@chakra-ui/react"
+import * as React from "react"
+import { LuCheck, LuChevronRight } from "react-icons/lu"
+
+interface MenuContentProps extends ChakraMenu.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+}
+
+export const MenuContent = React.forwardRef(
+ function MenuContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props
+ return (
+
+
+
+
+
+ )
+ },
+)
+
+export const MenuArrow = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.ArrowProps
+>(function MenuArrow(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const MenuCheckboxItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.CheckboxItemProps
+>(function MenuCheckboxItem(props, ref) {
+ return (
+
+
+
+
+ {props.children}
+
+ )
+})
+
+export const MenuRadioItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.RadioItemProps
+>(function MenuRadioItem(props, ref) {
+ const { children, ...rest } = props
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+})
+
+export const MenuItemGroup = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.ItemGroupProps
+>(function MenuItemGroup(props, ref) {
+ const { title, children, ...rest } = props
+ return (
+
+ {title && (
+
+ {title}
+
+ )}
+ {children}
+
+ )
+})
+
+export interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
+ startIcon?: React.ReactNode
+}
+
+export const MenuTriggerItem = React.forwardRef<
+ HTMLDivElement,
+ MenuTriggerItemProps
+>(function MenuTriggerItem(props, ref) {
+ const { startIcon, children, ...rest } = props
+ return (
+
+ {startIcon}
+ {children}
+
+
+ )
+})
+
+export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup
+export const MenuContextTrigger = ChakraMenu.ContextTrigger
+export const MenuRoot = ChakraMenu.Root
+export const MenuSeparator = ChakraMenu.Separator
+
+export const MenuItem = ChakraMenu.Item
+export const MenuItemText = ChakraMenu.ItemText
+export const MenuItemCommand = ChakraMenu.ItemCommand
+export const MenuTrigger = ChakraMenu.Trigger
diff --git a/src/components/ui/native-select.tsx b/src/components/ui/native-select.tsx
new file mode 100644
index 0000000..9e6ebf5
--- /dev/null
+++ b/src/components/ui/native-select.tsx
@@ -0,0 +1,57 @@
+"use client"
+
+import { NativeSelect as Select } from "@chakra-ui/react"
+import * as React from "react"
+
+interface NativeSelectRootProps extends Select.RootProps {
+ icon?: React.ReactNode
+}
+
+export const NativeSelectRoot = React.forwardRef<
+ HTMLDivElement,
+ NativeSelectRootProps
+>(function NativeSelect(props, ref) {
+ const { icon, children, ...rest } = props
+ return (
+
+ {children}
+ {icon}
+
+ )
+})
+
+interface NativeSelectItem {
+ value: string
+ label: string
+ disabled?: boolean
+}
+
+interface NativeSelectField extends Select.FieldProps {
+ items?: Array
+}
+
+export const NativeSelectField = React.forwardRef<
+ HTMLSelectElement,
+ NativeSelectField
+>(function NativeSelectField(props, ref) {
+ const { items: itemsProp, children, ...rest } = props
+
+ const items = React.useMemo(
+ () =>
+ itemsProp?.map((item) =>
+ typeof item === "string" ? { label: item, value: item } : item,
+ ),
+ [itemsProp],
+ )
+
+ return (
+
+ {children}
+ {items?.map((item) => (
+
+ {item.label}
+
+ ))}
+
+ )
+})
diff --git a/src/components/ui/number-input.tsx b/src/components/ui/number-input.tsx
new file mode 100644
index 0000000..7ddab85
--- /dev/null
+++ b/src/components/ui/number-input.tsx
@@ -0,0 +1,24 @@
+import { NumberInput as ChakraNumberInput } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface NumberInputProps extends ChakraNumberInput.RootProps {}
+
+export const NumberInputRoot = React.forwardRef<
+ HTMLDivElement,
+ NumberInputProps
+>(function NumberInput(props, ref) {
+ const { children, ...rest } = props
+ return (
+
+ {children}
+
+
+
+
+
+ )
+})
+
+export const NumberInputField = ChakraNumberInput.Input
+export const NumberInputScruber = ChakraNumberInput.Scrubber
+export const NumberInputLabel = ChakraNumberInput.Label
diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx
new file mode 100644
index 0000000..8201ed8
--- /dev/null
+++ b/src/components/ui/pagination.tsx
@@ -0,0 +1,208 @@
+"use client"
+
+import type { ButtonProps, TextProps } from "@chakra-ui/react"
+import {
+ Button,
+ Pagination as ChakraPagination,
+ IconButton,
+ Text,
+ createContext,
+ usePaginationContext,
+} from "@chakra-ui/react"
+import * as React from "react"
+import {
+ HiChevronLeft,
+ HiChevronRight,
+ HiMiniEllipsisHorizontal,
+} from "react-icons/hi2"
+import { LinkButton } from "./link-button"
+
+interface ButtonVariantMap {
+ current: ButtonProps["variant"]
+ default: ButtonProps["variant"]
+ ellipsis: ButtonProps["variant"]
+}
+
+type PaginationVariant = "outline" | "solid" | "subtle"
+
+interface ButtonVariantContext {
+ size: ButtonProps["size"]
+ variantMap: ButtonVariantMap
+ getHref?: (page: number) => string
+}
+
+const [RootPropsProvider, useRootProps] = createContext({
+ name: "RootPropsProvider",
+})
+
+export interface PaginationRootProps
+ extends Omit {
+ size?: ButtonProps["size"]
+ variant?: PaginationVariant
+ getHref?: (page: number) => string
+}
+
+const variantMap: Record = {
+ outline: { default: "ghost", ellipsis: "plain", current: "outline" },
+ solid: { default: "outline", ellipsis: "outline", current: "solid" },
+ subtle: { default: "ghost", ellipsis: "plain", current: "subtle" },
+}
+
+export const PaginationRoot = React.forwardRef<
+ HTMLDivElement,
+ PaginationRootProps
+>(function PaginationRoot(props, ref) {
+ const { size = "sm", variant = "outline", getHref, ...rest } = props
+ return (
+
+
+
+ )
+})
+
+export const PaginationEllipsis = React.forwardRef<
+ HTMLDivElement,
+ ChakraPagination.EllipsisProps
+>(function PaginationEllipsis(props, ref) {
+ const { size, variantMap } = useRootProps()
+ return (
+
+
+
+
+
+ )
+})
+
+export const PaginationItem = React.forwardRef<
+ HTMLButtonElement,
+ ChakraPagination.ItemProps
+>(function PaginationItem(props, ref) {
+ const { page } = usePaginationContext()
+ const { size, variantMap, getHref } = useRootProps()
+
+ const current = page === props.value
+ const variant = current ? variantMap.current : variantMap.default
+
+ if (getHref) {
+ return (
+
+ {props.value}
+
+ )
+ }
+
+ return (
+
+
+ {props.value}
+
+
+ )
+})
+
+export const PaginationPrevTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraPagination.PrevTriggerProps
+>(function PaginationPrevTrigger(props, ref) {
+ const { size, variantMap, getHref } = useRootProps()
+ const { previousPage } = usePaginationContext()
+
+ if (getHref) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ )
+})
+
+export const PaginationNextTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraPagination.NextTriggerProps
+>(function PaginationNextTrigger(props, ref) {
+ const { size, variantMap, getHref } = useRootProps()
+ const { nextPage } = usePaginationContext()
+
+ if (getHref) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ )
+})
+
+export const PaginationItems = (props: React.HTMLAttributes) => {
+ return (
+
+ {({ pages }) =>
+ pages.map((page, index) => {
+ return page.type === "ellipsis" ? (
+
+ ) : (
+
+ )
+ })
+ }
+
+ )
+}
+
+interface PageTextProps extends TextProps {
+ format?: "short" | "compact" | "long"
+}
+
+export const PaginationPageText = React.forwardRef<
+ HTMLParagraphElement,
+ PageTextProps
+>(function PaginationPageText(props, ref) {
+ const { format = "compact", ...rest } = props
+ const { page, totalPages, pageRange, count } = usePaginationContext()
+ const content = React.useMemo(() => {
+ if (format === "short") return `${page} / ${totalPages}`
+ if (format === "compact") return `${page} of ${totalPages}`
+ return `${pageRange.start + 1} - ${pageRange.end} of ${count}`
+ }, [format, page, totalPages, pageRange, count])
+
+ return (
+
+ {content}
+
+ )
+})
diff --git a/src/components/ui/password-input.tsx b/src/components/ui/password-input.tsx
new file mode 100644
index 0000000..0c608a9
--- /dev/null
+++ b/src/components/ui/password-input.tsx
@@ -0,0 +1,148 @@
+"use client"
+
+import type {
+ ButtonProps,
+ GroupProps,
+ InputProps,
+ StackProps,
+} from "@chakra-ui/react"
+import {
+ Box,
+ HStack,
+ IconButton,
+ Input,
+ Stack,
+ mergeRefs,
+ useControllableState,
+} from "@chakra-ui/react"
+import * as React from "react"
+import { LuEye, LuEyeOff } from "react-icons/lu"
+import { InputGroup } from "./input-group"
+
+export interface PasswordVisibilityProps {
+ defaultVisible?: boolean
+ visible?: boolean
+ onVisibleChange?: (visible: boolean) => void
+ visibilityIcon?: { on: React.ReactNode; off: React.ReactNode }
+}
+
+export interface PasswordInputProps
+ extends InputProps,
+ PasswordVisibilityProps {
+ rootProps?: GroupProps
+}
+
+export const PasswordInput = React.forwardRef<
+ HTMLInputElement,
+ PasswordInputProps
+>(function PasswordInput(props, ref) {
+ const {
+ rootProps,
+ defaultVisible,
+ visible: visibleProp,
+ onVisibleChange,
+ visibilityIcon = { on: , off: },
+ ...rest
+ } = props
+
+ const [visible, setVisible] = useControllableState({
+ value: visibleProp,
+ defaultValue: defaultVisible || false,
+ onChange: onVisibleChange,
+ })
+
+ const inputRef = React.useRef(null)
+
+ return (
+ {
+ if (rest.disabled) return
+ if (e.button !== 0) return
+ e.preventDefault()
+ setVisible(!visible)
+ }}
+ >
+ {visible ? visibilityIcon.off : visibilityIcon.on}
+
+ }
+ {...rootProps}
+ >
+
+
+ )
+})
+
+const VisibilityTrigger = React.forwardRef(
+ function VisibilityTrigger(props, ref) {
+ return (
+
+ )
+ },
+)
+
+interface PasswordStrengthMeterProps extends StackProps {
+ max?: number
+ value: number
+}
+
+export const PasswordStrengthMeter = React.forwardRef<
+ HTMLDivElement,
+ PasswordStrengthMeterProps
+>(function PasswordStrengthMeter(props, ref) {
+ const { max = 4, value, ...rest } = props
+
+ const percent = (value / max) * 100
+ const { label, colorPalette } = getColorPalette(percent)
+
+ return (
+
+
+ {Array.from({ length: max }).map((_, index) => (
+
+ ))}
+
+ {label && {label} }
+
+ )
+})
+
+function getColorPalette(percent: number) {
+ switch (true) {
+ case percent < 33:
+ return { label: "Low", colorPalette: "red" }
+ case percent < 66:
+ return { label: "Medium", colorPalette: "orange" }
+ default:
+ return { label: "High", colorPalette: "green" }
+ }
+}
diff --git a/src/components/ui/pin-input.tsx b/src/components/ui/pin-input.tsx
new file mode 100644
index 0000000..c969c80
--- /dev/null
+++ b/src/components/ui/pin-input.tsx
@@ -0,0 +1,27 @@
+import { PinInput as ChakraPinInput, Group } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface PinInputProps extends ChakraPinInput.RootProps {
+ rootRef?: React.Ref
+ count?: number
+ inputProps?: React.InputHTMLAttributes
+ attached?: boolean
+}
+
+export const PinInput = React.forwardRef(
+ function PinInput(props, ref) {
+ const { count = 4, inputProps, rootRef, attached, ...rest } = props
+ return (
+
+
+
+
+ {Array.from({ length: count }).map((_, index) => (
+
+ ))}
+
+
+
+ )
+ },
+)
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
new file mode 100644
index 0000000..3320659
--- /dev/null
+++ b/src/components/ui/popover.tsx
@@ -0,0 +1,59 @@
+import { Popover as ChakraPopover, Portal } from "@chakra-ui/react"
+import { CloseButton } from "./close-button"
+import * as React from "react"
+
+interface PopoverContentProps extends ChakraPopover.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+}
+
+export const PopoverContent = React.forwardRef<
+ HTMLDivElement,
+ PopoverContentProps
+>(function PopoverContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props
+ return (
+
+
+
+
+
+ )
+})
+
+export const PopoverArrow = React.forwardRef<
+ HTMLDivElement,
+ ChakraPopover.ArrowProps
+>(function PopoverArrow(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const PopoverCloseTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraPopover.CloseTriggerProps
+>(function PopoverCloseTrigger(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const PopoverTitle = ChakraPopover.Title
+export const PopoverDescription = ChakraPopover.Description
+export const PopoverFooter = ChakraPopover.Footer
+export const PopoverHeader = ChakraPopover.Header
+export const PopoverRoot = ChakraPopover.Root
+export const PopoverBody = ChakraPopover.Body
+export const PopoverTrigger = ChakraPopover.Trigger
diff --git a/src/components/ui/progress-circle.tsx b/src/components/ui/progress-circle.tsx
new file mode 100644
index 0000000..2d430cb
--- /dev/null
+++ b/src/components/ui/progress-circle.tsx
@@ -0,0 +1,37 @@
+import type { SystemStyleObject } from "@chakra-ui/react"
+import {
+ AbsoluteCenter,
+ ProgressCircle as ChakraProgressCircle,
+} from "@chakra-ui/react"
+import * as React from "react"
+
+interface ProgressCircleRingProps extends ChakraProgressCircle.CircleProps {
+ trackColor?: SystemStyleObject["stroke"]
+ cap?: SystemStyleObject["strokeLinecap"]
+}
+
+export const ProgressCircleRing = React.forwardRef<
+ SVGSVGElement,
+ ProgressCircleRingProps
+>(function ProgressCircleRing(props, ref) {
+ const { trackColor, cap, color, ...rest } = props
+ return (
+
+
+
+
+ )
+})
+
+export const ProgressCircleValueText = React.forwardRef<
+ HTMLDivElement,
+ ChakraProgressCircle.ValueTextProps
+>(function ProgressCircleValueText(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const ProgressCircleRoot = ChakraProgressCircle.Root
diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx
new file mode 100644
index 0000000..9ab22d8
--- /dev/null
+++ b/src/components/ui/progress.tsx
@@ -0,0 +1,34 @@
+import { Progress as ChakraProgress } from "@chakra-ui/react"
+import { InfoTip } from "./toggle-tip"
+import * as React from "react"
+
+export const ProgressBar = React.forwardRef<
+ HTMLDivElement,
+ ChakraProgress.TrackProps
+>(function ProgressBar(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export interface ProgressLabelProps extends ChakraProgress.LabelProps {
+ info?: React.ReactNode
+}
+
+export const ProgressLabel = React.forwardRef<
+ HTMLDivElement,
+ ProgressLabelProps
+>(function ProgressLabel(props, ref) {
+ const { children, info, ...rest } = props
+ return (
+
+ {children}
+ {info && {info} }
+
+ )
+})
+
+export const ProgressRoot = ChakraProgress.Root
+export const ProgressValueText = ChakraProgress.ValueText
diff --git a/src/components/ui/prose.tsx b/src/components/ui/prose.tsx
new file mode 100644
index 0000000..e34a37b
--- /dev/null
+++ b/src/components/ui/prose.tsx
@@ -0,0 +1,264 @@
+"use client"
+
+import { chakra } from "@chakra-ui/react"
+
+export const Prose = chakra("div", {
+ base: {
+ color: "fg.muted",
+ maxWidth: "65ch",
+ fontSize: "sm",
+ lineHeight: "1.7em",
+ "& p": {
+ marginTop: "1em",
+ marginBottom: "1em",
+ },
+ "& blockquote": {
+ marginTop: "1.285em",
+ marginBottom: "1.285em",
+ paddingInline: "1.285em",
+ borderInlineStartWidth: "0.25em",
+ },
+ "& a": {
+ color: "fg",
+ textDecoration: "underline",
+ textUnderlineOffset: "3px",
+ textDecorationThickness: "2px",
+ textDecorationColor: "border.muted",
+ fontWeight: "500",
+ },
+ "& strong": {
+ fontWeight: "600",
+ },
+ "& a strong": {
+ color: "inherit",
+ },
+ "& h1": {
+ fontSize: "2.15em",
+ letterSpacing: "-0.02em",
+ marginTop: "0",
+ marginBottom: "0.8em",
+ lineHeight: "1.2em",
+ },
+ "& h2": {
+ fontSize: "1.4em",
+ letterSpacing: "-0.02em",
+ marginTop: "1.6em",
+ marginBottom: "0.8em",
+ lineHeight: "1.4em",
+ },
+ "& h3": {
+ fontSize: "1.285em",
+ letterSpacing: "-0.01em",
+ marginTop: "1.5em",
+ marginBottom: "0.4em",
+ lineHeight: "1.5em",
+ },
+ "& h4": {
+ marginTop: "1.4em",
+ marginBottom: "0.5em",
+ letterSpacing: "-0.01em",
+ lineHeight: "1.5em",
+ },
+ "& img": {
+ marginTop: "1.7em",
+ marginBottom: "1.7em",
+ borderRadius: "lg",
+ boxShadow: "inset",
+ },
+ "& picture": {
+ marginTop: "1.7em",
+ marginBottom: "1.7em",
+ },
+ "& picture > img": {
+ marginTop: "0",
+ marginBottom: "0",
+ },
+ "& video": {
+ marginTop: "1.7em",
+ marginBottom: "1.7em",
+ },
+ "& kbd": {
+ fontSize: "0.85em",
+ borderRadius: "xs",
+ paddingTop: "0.15em",
+ paddingBottom: "0.15em",
+ paddingInlineEnd: "0.35em",
+ paddingInlineStart: "0.35em",
+ fontFamily: "inherit",
+ color: "fg.muted",
+ "--shadow": "colors.border",
+ boxShadow: "0 0 0 1px var(--shadow),0 1px 0 1px var(--shadow)",
+ },
+ "& code": {
+ fontSize: "0.925em",
+ letterSpacing: "-0.01em",
+ borderRadius: "md",
+ borderWidth: "1px",
+ padding: "0.25em",
+ },
+ "& pre code": {
+ fontSize: "inherit",
+ letterSpacing: "inherit",
+ borderWidth: "inherit",
+ padding: "0",
+ },
+ "& h2 code": {
+ fontSize: "0.9em",
+ },
+ "& h3 code": {
+ fontSize: "0.8em",
+ },
+ "& pre": {
+ backgroundColor: "bg.subtle",
+ marginTop: "1.6em",
+ marginBottom: "1.6em",
+ borderRadius: "md",
+ fontSize: "0.9em",
+ paddingTop: "0.65em",
+ paddingBottom: "0.65em",
+ paddingInlineEnd: "1em",
+ paddingInlineStart: "1em",
+ overflowX: "auto",
+ fontWeight: "400",
+ },
+ "& ol": {
+ marginTop: "1em",
+ marginBottom: "1em",
+ paddingInlineStart: "1.5em",
+ },
+ "& ul": {
+ marginTop: "1em",
+ marginBottom: "1em",
+ paddingInlineStart: "1.5em",
+ },
+ "& li": {
+ marginTop: "0.285em",
+ marginBottom: "0.285em",
+ },
+ "& ol > li": {
+ paddingInlineStart: "0.4em",
+ listStyleType: "decimal",
+ "&::marker": {
+ color: "fg.muted",
+ },
+ },
+ "& ul > li": {
+ paddingInlineStart: "0.4em",
+ listStyleType: "disc",
+ "&::marker": {
+ color: "fg.muted",
+ },
+ },
+ "& > ul > li p": {
+ marginTop: "0.5em",
+ marginBottom: "0.5em",
+ },
+ "& > ul > li > p:first-of-type": {
+ marginTop: "1em",
+ },
+ "& > ul > li > p:last-of-type": {
+ marginBottom: "1em",
+ },
+ "& > ol > li > p:first-of-type": {
+ marginTop: "1em",
+ },
+ "& > ol > li > p:last-of-type": {
+ marginBottom: "1em",
+ },
+ "& ul ul, ul ol, ol ul, ol ol": {
+ marginTop: "0.5em",
+ marginBottom: "0.5em",
+ },
+ "& dl": {
+ marginTop: "1em",
+ marginBottom: "1em",
+ },
+ "& dt": {
+ fontWeight: "600",
+ marginTop: "1em",
+ },
+ "& dd": {
+ marginTop: "0.285em",
+ paddingInlineStart: "1.5em",
+ },
+ "& hr": {
+ marginTop: "2.25em",
+ marginBottom: "2.25em",
+ },
+ "& :is(h1,h2,h3,h4,h5,hr) + *": {
+ marginTop: "0",
+ },
+ "& table": {
+ width: "100%",
+ tableLayout: "auto",
+ textAlign: "start",
+ lineHeight: "1.5em",
+ marginTop: "2em",
+ marginBottom: "2em",
+ },
+ "& thead": {
+ borderBottomWidth: "1px",
+ color: "fg",
+ },
+ "& tbody tr": {
+ borderBottomWidth: "1px",
+ borderBottomColor: "border",
+ },
+ "& thead th": {
+ paddingInlineEnd: "1em",
+ paddingBottom: "0.65em",
+ paddingInlineStart: "1em",
+ fontWeight: "medium",
+ textAlign: "start",
+ },
+ "& thead th:first-of-type": {
+ paddingInlineStart: "0",
+ },
+ "& thead th:last-of-type": {
+ paddingInlineEnd: "0",
+ },
+ "& tbody td, tfoot td": {
+ paddingTop: "0.65em",
+ paddingInlineEnd: "1em",
+ paddingBottom: "0.65em",
+ paddingInlineStart: "1em",
+ },
+ "& tbody td:first-of-type, tfoot td:first-of-type": {
+ paddingInlineStart: "0",
+ },
+ "& tbody td:last-of-type, tfoot td:last-of-type": {
+ paddingInlineEnd: "0",
+ },
+ "& figure": {
+ marginTop: "1.625em",
+ marginBottom: "1.625em",
+ },
+ "& figure > *": {
+ marginTop: "0",
+ marginBottom: "0",
+ },
+ "& figcaption": {
+ fontSize: "0.85em",
+ lineHeight: "1.25em",
+ marginTop: "0.85em",
+ color: "fg.muted",
+ },
+ "& h1, h2, h3, h4": {
+ color: "fg",
+ fontWeight: "600",
+ },
+ },
+ variants: {
+ size: {
+ md: {
+ fontSize: "sm",
+ },
+ lg: {
+ fontSize: "md",
+ },
+ },
+ },
+ defaultVariants: {
+ size: "md",
+ },
+})
diff --git a/src/components/ui/provider.tsx b/src/components/ui/provider.tsx
new file mode 100644
index 0000000..fd0331b
--- /dev/null
+++ b/src/components/ui/provider.tsx
@@ -0,0 +1,15 @@
+"use client"
+
+import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
+import {
+ ColorModeProvider,
+ type ColorModeProviderProps,
+} from "./color-mode"
+
+export function Provider(props: ColorModeProviderProps) {
+ return (
+
+
+
+ )
+}
diff --git a/src/components/ui/radio-card.tsx b/src/components/ui/radio-card.tsx
new file mode 100644
index 0000000..d2fef42
--- /dev/null
+++ b/src/components/ui/radio-card.tsx
@@ -0,0 +1,58 @@
+import { RadioCard } from "@chakra-ui/react"
+import * as React from "react"
+
+interface RadioCardItemProps extends RadioCard.ItemProps {
+ icon?: React.ReactElement
+ label?: React.ReactNode
+ description?: React.ReactNode
+ addon?: React.ReactNode
+ indicator?: React.ReactNode | null
+ indicatorPlacement?: "start" | "end" | "inside"
+ inputProps?: React.InputHTMLAttributes
+}
+
+export const RadioCardItem = React.forwardRef<
+ HTMLInputElement,
+ RadioCardItemProps
+>(function RadioCardItem(props, ref) {
+ const {
+ inputProps,
+ label,
+ description,
+ addon,
+ icon,
+ indicator = ,
+ indicatorPlacement = "end",
+ ...rest
+ } = props
+
+ const hasContent = label || description || icon
+ const ContentWrapper = indicator ? RadioCard.ItemContent : React.Fragment
+
+ return (
+
+
+
+ {indicatorPlacement === "start" && indicator}
+ {hasContent && (
+
+ {icon}
+ {label && {label} }
+ {description && (
+
+ {description}
+
+ )}
+ {indicatorPlacement === "inside" && indicator}
+
+ )}
+ {indicatorPlacement === "end" && indicator}
+
+ {addon && {addon} }
+
+ )
+})
+
+export const RadioCardRoot = RadioCard.Root
+export const RadioCardLabel = RadioCard.Label
+export const RadioCardItemIndicator = RadioCard.ItemIndicator
diff --git a/src/components/ui/radio.tsx b/src/components/ui/radio.tsx
new file mode 100644
index 0000000..b3919d0
--- /dev/null
+++ b/src/components/ui/radio.tsx
@@ -0,0 +1,24 @@
+import { RadioGroup as ChakraRadioGroup } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface RadioProps extends ChakraRadioGroup.ItemProps {
+ rootRef?: React.Ref
+ inputProps?: React.InputHTMLAttributes
+}
+
+export const Radio = React.forwardRef(
+ function Radio(props, ref) {
+ const { children, inputProps, rootRef, ...rest } = props
+ return (
+
+
+
+ {children && (
+ {children}
+ )}
+
+ )
+ },
+)
+
+export const RadioGroup = ChakraRadioGroup.Root
diff --git a/src/components/ui/rating.tsx b/src/components/ui/rating.tsx
new file mode 100644
index 0000000..5609f26
--- /dev/null
+++ b/src/components/ui/rating.tsx
@@ -0,0 +1,27 @@
+import { RatingGroup } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface RatingProps extends RatingGroup.RootProps {
+ icon?: React.ReactElement
+ count?: number
+ label?: React.ReactNode
+}
+
+export const Rating = React.forwardRef(
+ function Rating(props, ref) {
+ const { icon, count = 5, label, ...rest } = props
+ return (
+
+ {label && {label} }
+
+
+ {Array.from({ length: count }).map((_, index) => (
+
+
+
+ ))}
+
+
+ )
+ },
+)
diff --git a/src/components/ui/segmented-control.tsx b/src/components/ui/segmented-control.tsx
new file mode 100644
index 0000000..aa38adf
--- /dev/null
+++ b/src/components/ui/segmented-control.tsx
@@ -0,0 +1,47 @@
+"use client"
+
+import { For, SegmentGroup } from "@chakra-ui/react"
+import * as React from "react"
+
+interface Item {
+ value: string
+ label: React.ReactNode
+ disabled?: boolean
+}
+
+export interface SegmentedControlProps extends SegmentGroup.RootProps {
+ items: Array
+}
+
+function normalize(items: Array): Item[] {
+ return items.map((item) => {
+ if (typeof item === "string") return { value: item, label: item }
+ return item
+ })
+}
+
+export const SegmentedControl = React.forwardRef<
+ HTMLDivElement,
+ SegmentedControlProps
+>(function SegmentedControl(props, ref) {
+ const { items, ...rest } = props
+ const data = React.useMemo(() => normalize(items), [items])
+
+ return (
+
+
+
+ {(item) => (
+
+ {item.label}
+
+
+ )}
+
+
+ )
+})
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
new file mode 100644
index 0000000..99d84e6
--- /dev/null
+++ b/src/components/ui/select.tsx
@@ -0,0 +1,143 @@
+"use client"
+
+import type { CollectionItem } from "@chakra-ui/react"
+import { Select as ChakraSelect, Portal } from "@chakra-ui/react"
+import { CloseButton } from "./close-button"
+import * as React from "react"
+
+interface SelectTriggerProps extends ChakraSelect.ControlProps {
+ clearable?: boolean
+}
+
+export const SelectTrigger = React.forwardRef<
+ HTMLButtonElement,
+ SelectTriggerProps
+>(function SelectTrigger(props, ref) {
+ const { children, clearable, ...rest } = props
+ return (
+
+ {children}
+
+ {clearable && }
+
+
+
+ )
+})
+
+const SelectClearTrigger = React.forwardRef<
+ HTMLButtonElement,
+ ChakraSelect.ClearTriggerProps
+>(function SelectClearTrigger(props, ref) {
+ return (
+
+
+
+ )
+})
+
+interface SelectContentProps extends ChakraSelect.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+}
+
+export const SelectContent = React.forwardRef<
+ HTMLDivElement,
+ SelectContentProps
+>(function SelectContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props
+ return (
+
+
+
+
+
+ )
+})
+
+export const SelectItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraSelect.ItemProps
+>(function SelectItem(props, ref) {
+ const { item, children, ...rest } = props
+ return (
+
+ {children}
+
+
+ )
+})
+
+interface SelectValueTextProps
+ extends Omit {
+ children?(items: CollectionItem[]): React.ReactNode
+}
+
+export const SelectValueText = React.forwardRef<
+ HTMLSpanElement,
+ SelectValueTextProps
+>(function SelectValueText(props, ref) {
+ const { children, ...rest } = props
+ return (
+
+
+ {(select) => {
+ const items = select.selectedItems
+ if (items.length === 0) return props.placeholder
+ if (children) return children(items)
+ if (items.length === 1)
+ return select.collection.stringifyItem(items[0])
+ return `${items.length} selected`
+ }}
+
+
+ )
+})
+
+export const SelectRoot = React.forwardRef<
+ HTMLDivElement,
+ ChakraSelect.RootProps
+>(function SelectRoot(props, ref) {
+ return (
+
+ {props.asChild ? (
+ props.children
+ ) : (
+ <>
+
+ {props.children}
+ >
+ )}
+
+ )
+}) as ChakraSelect.RootComponent
+
+interface SelectItemGroupProps extends ChakraSelect.ItemGroupProps {
+ label: React.ReactNode
+}
+
+export const SelectItemGroup = React.forwardRef<
+ HTMLDivElement,
+ SelectItemGroupProps
+>(function SelectItemGroup(props, ref) {
+ const { children, label, ...rest } = props
+ return (
+
+ {label}
+ {children}
+
+ )
+})
+
+export const SelectLabel = ChakraSelect.Label
+export const SelectItemText = ChakraSelect.ItemText
diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx
new file mode 100644
index 0000000..4f2c25b
--- /dev/null
+++ b/src/components/ui/skeleton.tsx
@@ -0,0 +1,47 @@
+import type {
+ SkeletonProps as ChakraSkeletonProps,
+ CircleProps,
+} from "@chakra-ui/react"
+import { Skeleton as ChakraSkeleton, Circle, Stack } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface SkeletonCircleProps extends ChakraSkeletonProps {
+ size?: CircleProps["size"]
+}
+
+export const SkeletonCircle = React.forwardRef<
+ HTMLDivElement,
+ SkeletonCircleProps
+>(function SkeletonCircle(props, ref) {
+ const { size, ...rest } = props
+ return (
+
+
+
+ )
+})
+
+export interface SkeletonTextProps extends ChakraSkeletonProps {
+ noOfLines?: number
+}
+
+export const SkeletonText = React.forwardRef(
+ function SkeletonText(props, ref) {
+ const { noOfLines = 3, gap, ...rest } = props
+ return (
+
+ {Array.from({ length: noOfLines }).map((_, index) => (
+
+ ))}
+
+ )
+ },
+)
+
+export const Skeleton = ChakraSkeleton
diff --git a/src/components/ui/slider.tsx b/src/components/ui/slider.tsx
new file mode 100644
index 0000000..37a6dc9
--- /dev/null
+++ b/src/components/ui/slider.tsx
@@ -0,0 +1,60 @@
+import { Slider as ChakraSlider, HStack } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface SliderProps extends ChakraSlider.RootProps {
+ marks?: Array
+ label?: React.ReactNode
+ showValue?: boolean
+}
+
+export const Slider = React.forwardRef(
+ function Slider(props, ref) {
+ const { marks: marksProp, label, showValue, ...rest } = props
+ const value = props.defaultValue ?? props.value
+
+ const marks = marksProp?.map((mark) => {
+ if (typeof mark === "number") return { value: mark, label: undefined }
+ return mark
+ })
+
+ const hasMarkLabel = !!marks?.some((mark) => mark.label)
+
+ return (
+
+ {label && !showValue && (
+ {label}
+ )}
+ {label && showValue && (
+
+ {label}
+
+
+ )}
+
+
+
+
+ {value?.map((_, index) => (
+
+
+
+ ))}
+
+ {marks?.length && (
+
+ {marks.map((mark, index) => {
+ const value = typeof mark === "number" ? mark : mark.value
+ const label = typeof mark === "number" ? undefined : mark.label
+ return (
+
+
+ {label}
+
+ )
+ })}
+
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/stat.tsx b/src/components/ui/stat.tsx
new file mode 100644
index 0000000..a1e60ad
--- /dev/null
+++ b/src/components/ui/stat.tsx
@@ -0,0 +1,68 @@
+import {
+ Badge,
+ type BadgeProps,
+ Stat as ChakraStat,
+ FormatNumber,
+} from "@chakra-ui/react"
+import { InfoTip } from "./toggle-tip"
+import * as React from "react"
+
+interface StatLabelProps extends ChakraStat.LabelProps {
+ info?: React.ReactNode
+}
+
+export const StatLabel = React.forwardRef(
+ function StatLabel(props, ref) {
+ const { info, children, ...rest } = props
+ return (
+
+ {children}
+ {info && {info} }
+
+ )
+ },
+)
+
+interface StatValueTextProps extends ChakraStat.ValueTextProps {
+ value?: number
+ formatOptions?: Intl.NumberFormatOptions
+}
+
+export const StatValueText = React.forwardRef<
+ HTMLDivElement,
+ StatValueTextProps
+>(function StatValueText(props, ref) {
+ const { value, formatOptions, children, ...rest } = props
+ return (
+
+ {children ||
+ (value != null && )}
+
+ )
+})
+
+export const StatUpTrend = React.forwardRef(
+ function StatUpTrend(props, ref) {
+ return (
+
+
+ {props.children}
+
+ )
+ },
+)
+
+export const StatDownTrend = React.forwardRef(
+ function StatDownTrend(props, ref) {
+ return (
+
+
+ {props.children}
+
+ )
+ },
+)
+
+export const StatRoot = ChakraStat.Root
+export const StatHelpText = ChakraStat.HelpText
+export const StatValueUnit = ChakraStat.ValueUnit
diff --git a/src/components/ui/status.tsx b/src/components/ui/status.tsx
new file mode 100644
index 0000000..5055463
--- /dev/null
+++ b/src/components/ui/status.tsx
@@ -0,0 +1,29 @@
+import type { ColorPalette } from "@chakra-ui/react"
+import { Status as ChakraStatus } from "@chakra-ui/react"
+import * as React from "react"
+
+type StatusValue = "success" | "error" | "warning" | "info"
+
+export interface StatusProps extends ChakraStatus.RootProps {
+ value?: StatusValue
+}
+
+const statusMap: Record = {
+ success: "green",
+ error: "red",
+ warning: "orange",
+ info: "blue",
+}
+
+export const Status = React.forwardRef(
+ function Status(props, ref) {
+ const { children, value = "info", ...rest } = props
+ const colorPalette = rest.colorPalette ?? statusMap[value]
+ return (
+
+
+ {children}
+
+ )
+ },
+)
diff --git a/src/components/ui/stepper-input.tsx b/src/components/ui/stepper-input.tsx
new file mode 100644
index 0000000..22d158d
--- /dev/null
+++ b/src/components/ui/stepper-input.tsx
@@ -0,0 +1,49 @@
+import { HStack, IconButton, NumberInput } from "@chakra-ui/react"
+import * as React from "react"
+import { LuMinus, LuPlus } from "react-icons/lu"
+
+export interface StepperInputProps extends NumberInput.RootProps {
+ label?: React.ReactNode
+}
+
+export const StepperInput = React.forwardRef(
+ function StepperInput(props, ref) {
+ const { label, ...rest } = props
+ return (
+
+ {label && {label} }
+
+
+
+
+
+
+ )
+ },
+)
+
+const DecrementTrigger = React.forwardRef<
+ HTMLButtonElement,
+ NumberInput.DecrementTriggerProps
+>(function DecrementTrigger(props, ref) {
+ return (
+
+
+
+
+
+ )
+})
+
+const IncrementTrigger = React.forwardRef<
+ HTMLButtonElement,
+ NumberInput.IncrementTriggerProps
+>(function IncrementTrigger(props, ref) {
+ return (
+
+
+
+
+
+ )
+})
diff --git a/src/components/ui/steps.tsx b/src/components/ui/steps.tsx
new file mode 100644
index 0000000..677c4c7
--- /dev/null
+++ b/src/components/ui/steps.tsx
@@ -0,0 +1,82 @@
+import { Box, Steps as ChakraSteps } from "@chakra-ui/react"
+import * as React from "react"
+import { LuCheck } from "react-icons/lu"
+
+interface StepInfoProps {
+ title?: React.ReactNode
+ description?: React.ReactNode
+}
+
+export interface StepsItemProps
+ extends Omit,
+ StepInfoProps {
+ completedIcon?: React.ReactNode
+ icon?: React.ReactNode
+}
+
+export const StepsItem = React.forwardRef(
+ function StepsItem(props, ref) {
+ const { title, description, completedIcon, icon, ...rest } = props
+ return (
+
+
+
+ }
+ incomplete={icon || }
+ />
+
+
+
+
+
+ )
+ },
+)
+
+const StepInfo = (props: StepInfoProps) => {
+ const { title, description } = props
+
+ if (title && description) {
+ return (
+
+ {title}
+ {description}
+
+ )
+ }
+
+ return (
+ <>
+ {title && {title} }
+ {description && (
+ {description}
+ )}
+ >
+ )
+}
+
+interface StepsIndicatorProps {
+ completedIcon: React.ReactNode
+ icon?: React.ReactNode
+}
+
+export const StepsIndicator = React.forwardRef<
+ HTMLDivElement,
+ StepsIndicatorProps
+>(function StepsIndicator(props, ref) {
+ const { icon = , completedIcon } = props
+ return (
+
+
+
+ )
+})
+
+export const StepsList = ChakraSteps.List
+export const StepsRoot = ChakraSteps.Root
+export const StepsContent = ChakraSteps.Content
+export const StepsCompletedContent = ChakraSteps.CompletedContent
+
+export const StepsNextTrigger = ChakraSteps.NextTrigger
+export const StepsPrevTrigger = ChakraSteps.PrevTrigger
diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx
new file mode 100644
index 0000000..a677ca2
--- /dev/null
+++ b/src/components/ui/switch.tsx
@@ -0,0 +1,39 @@
+import { Switch as ChakraSwitch } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface SwitchProps extends ChakraSwitch.RootProps {
+ inputProps?: React.InputHTMLAttributes
+ rootRef?: React.Ref
+ trackLabel?: { on: React.ReactNode; off: React.ReactNode }
+ thumbLabel?: { on: React.ReactNode; off: React.ReactNode }
+}
+
+export const Switch = React.forwardRef(
+ function Switch(props, ref) {
+ const { inputProps, children, rootRef, trackLabel, thumbLabel, ...rest } =
+ props
+
+ return (
+
+
+
+
+ {thumbLabel && (
+
+ {thumbLabel?.on}
+
+ )}
+
+ {trackLabel && (
+
+ {trackLabel.on}
+
+ )}
+
+ {children != null && (
+ {children}
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/tag.tsx b/src/components/ui/tag.tsx
new file mode 100644
index 0000000..728250f
--- /dev/null
+++ b/src/components/ui/tag.tsx
@@ -0,0 +1,39 @@
+import { Tag as ChakraTag } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface TagProps extends ChakraTag.RootProps {
+ startElement?: React.ReactNode
+ endElement?: React.ReactNode
+ onClose?: VoidFunction
+ closable?: boolean
+}
+
+export const Tag = React.forwardRef(
+ function Tag(props, ref) {
+ const {
+ startElement,
+ endElement,
+ onClose,
+ closable = !!onClose,
+ children,
+ ...rest
+ } = props
+
+ return (
+
+ {startElement && (
+ {startElement}
+ )}
+ {children}
+ {endElement && (
+ {endElement}
+ )}
+ {closable && (
+
+
+
+ )}
+
+ )
+ },
+)
diff --git a/src/components/ui/timeline.tsx b/src/components/ui/timeline.tsx
new file mode 100644
index 0000000..678c1f6
--- /dev/null
+++ b/src/components/ui/timeline.tsx
@@ -0,0 +1,21 @@
+import { Timeline as ChakraTimeline } from "@chakra-ui/react"
+import * as React from "react"
+
+export const TimelineConnector = React.forwardRef<
+ HTMLDivElement,
+ ChakraTimeline.IndicatorProps
+>(function TimelineConnector(props, ref) {
+ return (
+
+
+
+
+ )
+})
+
+export const TimelineRoot = ChakraTimeline.Root
+export const TimelineContent = ChakraTimeline.Content
+export const TimelineItem = ChakraTimeline.Item
+export const TimelineIndicator = ChakraTimeline.Indicator
+export const TimelineTitle = ChakraTimeline.Title
+export const TimelineDescription = ChakraTimeline.Description
diff --git a/src/components/ui/toaster.tsx b/src/components/ui/toaster.tsx
new file mode 100644
index 0000000..df6c2c3
--- /dev/null
+++ b/src/components/ui/toaster.tsx
@@ -0,0 +1,43 @@
+"use client"
+
+import {
+ Toaster as ChakraToaster,
+ Portal,
+ Spinner,
+ Stack,
+ Toast,
+ createToaster,
+} from "@chakra-ui/react"
+
+export const toaster = createToaster({
+ placement: "bottom-end",
+ pauseOnPageIdle: true,
+})
+
+export const Toaster = () => {
+ return (
+
+
+ {(toast) => (
+
+ {toast.type === "loading" ? (
+
+ ) : (
+
+ )}
+
+ {toast.title && {toast.title} }
+ {toast.description && (
+ {toast.description}
+ )}
+
+ {toast.action && (
+ {toast.action.label}
+ )}
+ {toast.meta?.closable && }
+
+ )}
+
+
+ )
+}
diff --git a/src/components/ui/toggle-tip.tsx b/src/components/ui/toggle-tip.tsx
new file mode 100644
index 0000000..7dc7eae
--- /dev/null
+++ b/src/components/ui/toggle-tip.tsx
@@ -0,0 +1,70 @@
+import { Popover as ChakraPopover, IconButton, Portal } from "@chakra-ui/react"
+import * as React from "react"
+import { HiOutlineInformationCircle } from "react-icons/hi"
+
+export interface ToggleTipProps extends ChakraPopover.RootProps {
+ showArrow?: boolean
+ portalled?: boolean
+ portalRef?: React.RefObject
+ content?: React.ReactNode
+}
+
+export const ToggleTip = React.forwardRef(
+ function ToggleTip(props, ref) {
+ const {
+ showArrow,
+ children,
+ portalled = true,
+ content,
+ portalRef,
+ ...rest
+ } = props
+
+ return (
+
+ {children}
+
+
+
+ {showArrow && (
+
+
+
+ )}
+ {content}
+
+
+
+
+ )
+ },
+)
+
+export const InfoTip = React.forwardRef<
+ HTMLDivElement,
+ Partial
+>(function InfoTip(props, ref) {
+ const { children, ...rest } = props
+ return (
+
+
+
+
+
+ )
+})
diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx
new file mode 100644
index 0000000..8b95973
--- /dev/null
+++ b/src/components/ui/toggle.tsx
@@ -0,0 +1,57 @@
+"use client"
+
+import type { ButtonProps } from "@chakra-ui/react"
+import {
+ Button,
+ Toggle as ChakraToggle,
+ useToggleContext,
+} from "@chakra-ui/react"
+import * as React from "react"
+
+interface ToggleProps extends ChakraToggle.RootProps {
+ variant?: keyof typeof variantMap
+ size?: ButtonProps["size"]
+}
+
+const variantMap = {
+ solid: { on: "solid", off: "outline" },
+ surface: { on: "surface", off: "outline" },
+ subtle: { on: "subtle", off: "ghost" },
+ ghost: { on: "subtle", off: "ghost" },
+} as const
+
+export const Toggle = React.forwardRef(
+ function Toggle(props, ref) {
+ const { variant = "subtle", size, children, ...rest } = props
+ const variantConfig = variantMap[variant]
+
+ return (
+
+
+ {children}
+
+
+ )
+ },
+)
+
+interface ToggleBaseButtonProps extends Omit {
+ variant: Record<"on" | "off", ButtonProps["variant"]>
+}
+
+const ToggleBaseButton = React.forwardRef<
+ HTMLButtonElement,
+ ToggleBaseButtonProps
+>(function ToggleBaseButton(props, ref) {
+ const toggle = useToggleContext()
+ const { variant, ...rest } = props
+ return (
+
+ )
+})
+
+export const ToggleIndicator = ChakraToggle.Indicator
diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx
new file mode 100644
index 0000000..644c37c
--- /dev/null
+++ b/src/components/ui/tooltip.tsx
@@ -0,0 +1,46 @@
+import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react"
+import * as React from "react"
+
+export interface TooltipProps extends ChakraTooltip.RootProps {
+ showArrow?: boolean
+ portalled?: boolean
+ portalRef?: React.RefObject
+ content: React.ReactNode
+ contentProps?: ChakraTooltip.ContentProps
+ disabled?: boolean
+}
+
+export const Tooltip = React.forwardRef(
+ function Tooltip(props, ref) {
+ const {
+ showArrow,
+ children,
+ disabled,
+ portalled,
+ content,
+ contentProps,
+ portalRef,
+ ...rest
+ } = props
+
+ if (disabled) return children
+
+ return (
+
+ {children}
+
+
+
+ {showArrow && (
+
+
+
+ )}
+ {content}
+
+
+
+
+ )
+ },
+)