diff --git a/package-lock.json b/package-lock.json index 0948e85..923e219 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,25 @@ "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==", + "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 +286,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 +304,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 +361,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 +384,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 +1336,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 +1368,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 +1773,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 +2627,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 +2713,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 +2821,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 +3207,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 +4118,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 +6271,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 +7282,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 +7518,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 +7850,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 +8224,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 +8291,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 +9035,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 +9374,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/app/components/Header.tsx b/src/app/components/Header.tsx new file mode 100644 index 0000000..ad14623 --- /dev/null +++ b/src/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/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 ( + + + + ) + }, +) + +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) => ( + + ))} + + ) +}) 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 ( + + + + ) +}) + +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 ( +