Added shadcn dashboard block
This commit is contained in:
@@ -12,10 +12,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>This site requires JavaScript to operate. Please enable it in your browser settings.</noscript>
|
||||||
This site requires JavaScript to operate. Please enable it in your
|
|
||||||
browser settings.
|
|
||||||
</noscript>
|
|
||||||
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/vite-root.tsx"></script>
|
<script type="module" src="/src/vite-root.tsx"></script>
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
|
"@radix-ui/react-separator": "^1.1.7",
|
||||||
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@tailwindcss/vite": "^4.1.13",
|
"@tailwindcss/vite": "^4.1.13",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|||||||
639
web/pnpm-lock.yaml
generated
639
web/pnpm-lock.yaml
generated
@@ -11,6 +11,18 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@radix-ui/react-dialog':
|
||||||
|
specifier: ^1.1.15
|
||||||
|
version: 1.1.15(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-separator':
|
||||||
|
specifier: ^1.1.7
|
||||||
|
version: 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-slot':
|
||||||
|
specifier: ^1.2.3
|
||||||
|
version: 1.2.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-tooltip':
|
||||||
|
specifier: ^1.2.8
|
||||||
|
version: 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.13
|
specifier: ^4.1.13
|
||||||
version: 4.1.13(rolldown-vite@7.1.12(@types/node@24.5.2)(jiti@2.6.0))
|
version: 4.1.13(rolldown-vite@7.1.12(@types/node@24.5.2)(jiti@2.6.0))
|
||||||
@@ -217,6 +229,21 @@ packages:
|
|||||||
resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
|
resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
|
'@floating-ui/core@1.7.3':
|
||||||
|
resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.7.4':
|
||||||
|
resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==}
|
||||||
|
|
||||||
|
'@floating-ui/react-dom@2.1.6':
|
||||||
|
resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
react-dom: '>=16.8.0'
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.10':
|
||||||
|
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||||
|
|
||||||
'@humanfs/core@0.19.1':
|
'@humanfs/core@0.19.1':
|
||||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||||
engines: {node: '>=18.18.0'}
|
engines: {node: '>=18.18.0'}
|
||||||
@@ -275,6 +302,263 @@ packages:
|
|||||||
'@oxc-project/types@0.90.0':
|
'@oxc-project/types@0.90.0':
|
||||||
resolution: {integrity: sha512-fWvaufWUcLtm/OBKcNmxUkR0kQW5ZKAF0t03BXPqdzpxmnVCmSKzvUDRCOKnSagSfNzG/3ZdKpComH3GMy881g==}
|
resolution: {integrity: sha512-fWvaufWUcLtm/OBKcNmxUkR0kQW5ZKAF0t03BXPqdzpxmnVCmSKzvUDRCOKnSagSfNzG/3ZdKpComH3GMy881g==}
|
||||||
|
|
||||||
|
'@radix-ui/primitive@1.1.3':
|
||||||
|
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
||||||
|
|
||||||
|
'@radix-ui/react-arrow@1.1.7':
|
||||||
|
resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-compose-refs@1.1.2':
|
||||||
|
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-context@1.1.2':
|
||||||
|
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-dialog@1.1.15':
|
||||||
|
resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-dismissable-layer@1.1.11':
|
||||||
|
resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-guards@1.1.3':
|
||||||
|
resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-scope@1.1.7':
|
||||||
|
resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-id@1.1.1':
|
||||||
|
resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-popper@1.2.8':
|
||||||
|
resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-portal@1.1.9':
|
||||||
|
resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-presence@1.1.5':
|
||||||
|
resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-primitive@2.1.3':
|
||||||
|
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-separator@1.1.7':
|
||||||
|
resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-slot@1.2.3':
|
||||||
|
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-tooltip@1.2.8':
|
||||||
|
resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-callback-ref@1.1.1':
|
||||||
|
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-controllable-state@1.2.2':
|
||||||
|
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-effect-event@0.0.2':
|
||||||
|
resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-escape-keydown@1.1.1':
|
||||||
|
resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-layout-effect@1.1.1':
|
||||||
|
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-rect@1.1.1':
|
||||||
|
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-use-size@1.1.1':
|
||||||
|
resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-visually-hidden@1.2.3':
|
||||||
|
resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/rect@1.1.1':
|
||||||
|
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
|
||||||
|
|
||||||
'@rolldown/binding-android-arm64@1.0.0-beta.39':
|
'@rolldown/binding-android-arm64@1.0.0-beta.39':
|
||||||
resolution: {integrity: sha512-mjraAJQ3VRLPb3BUgVigHvmAYhiBpEeSM0dhvaO6XHtJ0k1o9Ng1Z6Qvlp4/1wDiUf7a10L5c3yleoGZ2r0Maw==}
|
resolution: {integrity: sha512-mjraAJQ3VRLPb3BUgVigHvmAYhiBpEeSM0dhvaO6XHtJ0k1o9Ng1Z6Qvlp4/1wDiUf7a10L5c3yleoGZ2r0Maw==}
|
||||||
engines: {node: ^20.19.0 || >=22.12.0}
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
@@ -575,6 +859,10 @@ packages:
|
|||||||
argparse@2.0.1:
|
argparse@2.0.1:
|
||||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
|
|
||||||
|
aria-hidden@1.2.6:
|
||||||
|
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
@@ -655,6 +943,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==}
|
resolution: {integrity: sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
detect-node-es@1.1.0:
|
||||||
|
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||||
|
|
||||||
electron-to-chromium@1.5.222:
|
electron-to-chromium@1.5.222:
|
||||||
resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==}
|
resolution: {integrity: sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==}
|
||||||
|
|
||||||
@@ -776,6 +1067,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
get-nonce@1.0.1:
|
||||||
|
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -1117,6 +1412,36 @@ packages:
|
|||||||
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
react-remove-scroll-bar@2.3.8:
|
||||||
|
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
react-remove-scroll@2.7.1:
|
||||||
|
resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
react-style-singleton@2.2.3:
|
||||||
|
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
react@19.1.1:
|
react@19.1.1:
|
||||||
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
|
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1275,6 +1600,26 @@ packages:
|
|||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
|
||||||
|
use-callback-ref@1.3.3:
|
||||||
|
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
use-sidecar@1.1.3:
|
||||||
|
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
use-sync-external-store@1.5.0:
|
use-sync-external-store@1.5.0:
|
||||||
resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
|
resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1479,6 +1824,23 @@ snapshots:
|
|||||||
'@eslint/core': 0.15.2
|
'@eslint/core': 0.15.2
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
|
'@floating-ui/core@1.7.3':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/utils': 0.2.10
|
||||||
|
|
||||||
|
'@floating-ui/dom@1.7.4':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/core': 1.7.3
|
||||||
|
'@floating-ui/utils': 0.2.10
|
||||||
|
|
||||||
|
'@floating-ui/react-dom@2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/dom': 1.7.4
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
|
||||||
|
'@floating-ui/utils@0.2.10': {}
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
|
|
||||||
'@humanfs/node@0.16.7':
|
'@humanfs/node@0.16.7':
|
||||||
@@ -1536,6 +1898,230 @@ snapshots:
|
|||||||
|
|
||||||
'@oxc-project/types@0.90.0': {}
|
'@oxc-project/types@0.90.0': {}
|
||||||
|
|
||||||
|
'@radix-ui/primitive@1.1.3': {}
|
||||||
|
|
||||||
|
'@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-context@1.1.2(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.3
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
aria-hidden: 1.2.6
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
react-remove-scroll: 2.7.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.3
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-id@1.1.1(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@floating-ui/react-dom': 2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-size': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/rect': 1.1.1
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-slot@1.2.3(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.3
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-use-rect@1.1.1(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/rect': 1.1.1
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-use-size@1.1.1(@types/react@19.1.13)(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
'@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||||
|
react: 19.1.1
|
||||||
|
react-dom: 19.1.1(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
'@types/react-dom': 19.1.9(@types/react@19.1.13)
|
||||||
|
|
||||||
|
'@radix-ui/rect@1.1.1': {}
|
||||||
|
|
||||||
'@rolldown/binding-android-arm64@1.0.0-beta.39':
|
'@rolldown/binding-android-arm64@1.0.0-beta.39':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -1823,6 +2409,10 @@ snapshots:
|
|||||||
|
|
||||||
argparse@2.0.1: {}
|
argparse@2.0.1: {}
|
||||||
|
|
||||||
|
aria-hidden@1.2.6:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
baseline-browser-mapping@2.8.6: {}
|
baseline-browser-mapping@2.8.6: {}
|
||||||
@@ -1891,6 +2481,8 @@ snapshots:
|
|||||||
|
|
||||||
detect-libc@2.1.0: {}
|
detect-libc@2.1.0: {}
|
||||||
|
|
||||||
|
detect-node-es@1.1.0: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.222: {}
|
electron-to-chromium@1.5.222: {}
|
||||||
|
|
||||||
enhanced-resolve@5.18.3:
|
enhanced-resolve@5.18.3:
|
||||||
@@ -2026,6 +2618,8 @@ snapshots:
|
|||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
|
get-nonce@1.0.1: {}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -2242,6 +2836,33 @@ snapshots:
|
|||||||
|
|
||||||
react-refresh@0.17.0: {}
|
react-refresh@0.17.0: {}
|
||||||
|
|
||||||
|
react-remove-scroll-bar@2.3.8(@types/react@19.1.13)(react@19.1.1):
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
react-remove-scroll@2.7.1(@types/react@19.1.13)(react@19.1.1):
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
react-remove-scroll-bar: 2.3.8(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
react-style-singleton: 2.2.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
tslib: 2.8.1
|
||||||
|
use-callback-ref: 1.3.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
use-sidecar: 1.1.3(@types/react@19.1.13)(react@19.1.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
react-style-singleton@2.2.3(@types/react@19.1.13)(react@19.1.1):
|
||||||
|
dependencies:
|
||||||
|
get-nonce: 1.0.1
|
||||||
|
react: 19.1.1
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
react@19.1.1: {}
|
react@19.1.1: {}
|
||||||
|
|
||||||
regexparam@3.0.0: {}
|
regexparam@3.0.0: {}
|
||||||
@@ -2336,8 +2957,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.8.3
|
typescript: 5.8.3
|
||||||
|
|
||||||
tslib@2.8.1:
|
tslib@2.8.1: {}
|
||||||
optional: true
|
|
||||||
|
|
||||||
tw-animate-css@1.3.8: {}
|
tw-animate-css@1.3.8: {}
|
||||||
|
|
||||||
@@ -2370,6 +2990,21 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
|
|
||||||
|
use-callback-ref@1.3.3(@types/react@19.1.13)(react@19.1.1):
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.1
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
|
use-sidecar@1.1.3(@types/react@19.1.13)(react@19.1.1):
|
||||||
|
dependencies:
|
||||||
|
detect-node-es: 1.1.0
|
||||||
|
react: 19.1.1
|
||||||
|
tslib: 2.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.13
|
||||||
|
|
||||||
use-sync-external-store@1.5.0(react@19.1.1):
|
use-sync-external-store@1.5.0(react@19.1.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.1.1
|
react: 19.1.1
|
||||||
|
|||||||
92
web/src/components/ui/breadcrumb.tsx
Normal file
92
web/src/components/ui/breadcrumb.tsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
||||||
|
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
||||||
|
return (
|
||||||
|
<ol
|
||||||
|
data-slot="breadcrumb-list"
|
||||||
|
className={cn(
|
||||||
|
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||||
|
return <li data-slot="breadcrumb-item" className={cn("inline-flex items-center gap-1.5", className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreadcrumbLink({
|
||||||
|
asChild,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"a"> & {
|
||||||
|
asChild?: boolean;
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot : "a";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp data-slot="breadcrumb-link" className={cn("hover:text-foreground transition-colors", className)} {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
data-slot="breadcrumb-page"
|
||||||
|
role="link"
|
||||||
|
aria-disabled="true"
|
||||||
|
aria-current="page"
|
||||||
|
className={cn("text-foreground font-normal", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">) {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
data-slot="breadcrumb-separator"
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cn("[&>svg]:size-3.5", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children ?? <ChevronRight />}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BreadcrumbEllipsis({ className, ...props }: React.ComponentProps<"span">) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
data-slot="breadcrumb-ellipsis"
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cn("flex size-9 items-center justify-center", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<MoreHorizontal className="size-4" />
|
||||||
|
<span className="sr-only">More</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
BreadcrumbEllipsis,
|
||||||
|
};
|
||||||
52
web/src/components/ui/button.tsx
Normal file
52
web/src/components/ui/button.tsx
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||||
|
outline:
|
||||||
|
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||||
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||||
|
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||||
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||||
|
icon: "size-9",
|
||||||
|
"icon-sm": "size-8",
|
||||||
|
"icon-lg": "size-10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
function Button({
|
||||||
|
className,
|
||||||
|
variant,
|
||||||
|
size,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"button"> &
|
||||||
|
VariantProps<typeof buttonVariants> & {
|
||||||
|
asChild?: boolean;
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot : "button";
|
||||||
|
|
||||||
|
return <Comp data-slot="button" className={cn(buttonVariants({ variant, size, className }))} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Button, buttonVariants };
|
||||||
56
web/src/components/ui/card.tsx
Normal file
56
web/src/components/ui/card.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card"
|
||||||
|
className={cn("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-header"
|
||||||
|
className={cn(
|
||||||
|
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="card-title" className={cn("leading-none font-semibold", className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="card-description" className={cn("text-muted-foreground text-sm", className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="card-action"
|
||||||
|
className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="card-content" className={cn("px-6", className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div data-slot="card-footer" className={cn("flex items-center px-6 [.border-t]:pt-6", className)} {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };
|
||||||
21
web/src/components/ui/input.tsx
Normal file
21
web/src/components/ui/input.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
data-slot="input"
|
||||||
|
className={cn(
|
||||||
|
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||||
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Input };
|
||||||
26
web/src/components/ui/separator.tsx
Normal file
26
web/src/components/ui/separator.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function Separator({
|
||||||
|
className,
|
||||||
|
orientation = "horizontal",
|
||||||
|
decorative = true,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<SeparatorPrimitive.Root
|
||||||
|
data-slot="separator"
|
||||||
|
decorative={decorative}
|
||||||
|
orientation={orientation}
|
||||||
|
className={cn(
|
||||||
|
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Separator };
|
||||||
103
web/src/components/ui/sheet.tsx
Normal file
103
web/src/components/ui/sheet.tsx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||||
|
import { XIcon } from "lucide-react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||||
|
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||||
|
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||||
|
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||||
|
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetOverlay({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
|
||||||
|
return (
|
||||||
|
<SheetPrimitive.Overlay
|
||||||
|
data-slot="sheet-overlay"
|
||||||
|
className={cn(
|
||||||
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
side = "right",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||||
|
side?: "top" | "right" | "bottom" | "left";
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SheetPortal>
|
||||||
|
<SheetOverlay />
|
||||||
|
<SheetPrimitive.Content
|
||||||
|
data-slot="sheet-content"
|
||||||
|
className={cn(
|
||||||
|
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||||
|
side === "right" &&
|
||||||
|
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||||
|
side === "left" &&
|
||||||
|
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||||
|
side === "top" &&
|
||||||
|
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
||||||
|
side === "bottom" &&
|
||||||
|
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||||
|
<XIcon className="size-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</SheetPrimitive.Close>
|
||||||
|
</SheetPrimitive.Content>
|
||||||
|
</SheetPortal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="sheet-header" className={cn("flex flex-col gap-1.5 p-4", className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {
|
||||||
|
return (
|
||||||
|
<SheetPrimitive.Title
|
||||||
|
data-slot="sheet-title"
|
||||||
|
className={cn("text-foreground font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SheetDescription({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Description>) {
|
||||||
|
return (
|
||||||
|
<SheetPrimitive.Description
|
||||||
|
data-slot="sheet-description"
|
||||||
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription };
|
||||||
677
web/src/components/ui/sidebar.tsx
Normal file
677
web/src/components/ui/sidebar.tsx
Normal file
@@ -0,0 +1,677 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
import { PanelLeftIcon } from "lucide-react";
|
||||||
|
|
||||||
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
|
||||||
|
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
||||||
|
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
||||||
|
const SIDEBAR_WIDTH = "16rem";
|
||||||
|
const SIDEBAR_WIDTH_MOBILE = "18rem";
|
||||||
|
const SIDEBAR_WIDTH_ICON = "3rem";
|
||||||
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
||||||
|
|
||||||
|
type SidebarContextProps = {
|
||||||
|
state: "expanded" | "collapsed";
|
||||||
|
open: boolean;
|
||||||
|
setOpen: (open: boolean) => void;
|
||||||
|
openMobile: boolean;
|
||||||
|
setOpenMobile: (open: boolean) => void;
|
||||||
|
isMobile: boolean;
|
||||||
|
toggleSidebar: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SidebarContext = React.createContext<SidebarContextProps | null>(null);
|
||||||
|
|
||||||
|
function useSidebar() {
|
||||||
|
const context = React.useContext(SidebarContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("useSidebar must be used within a SidebarProvider.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarProvider({
|
||||||
|
defaultOpen = true,
|
||||||
|
open: openProp,
|
||||||
|
onOpenChange: setOpenProp,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & {
|
||||||
|
defaultOpen?: boolean;
|
||||||
|
open?: boolean;
|
||||||
|
onOpenChange?: (open: boolean) => void;
|
||||||
|
}) {
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
const [openMobile, setOpenMobile] = React.useState(false);
|
||||||
|
|
||||||
|
// This is the internal state of the sidebar.
|
||||||
|
// We use openProp and setOpenProp for control from outside the component.
|
||||||
|
const [_open, _setOpen] = React.useState(defaultOpen);
|
||||||
|
const open = openProp ?? _open;
|
||||||
|
const setOpen = React.useCallback(
|
||||||
|
(value: boolean | ((value: boolean) => boolean)) => {
|
||||||
|
const openState = typeof value === "function" ? value(open) : value;
|
||||||
|
if (setOpenProp) {
|
||||||
|
setOpenProp(openState);
|
||||||
|
} else {
|
||||||
|
_setOpen(openState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This sets the cookie to keep the sidebar state.
|
||||||
|
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
||||||
|
},
|
||||||
|
[setOpenProp, open],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helper to toggle the sidebar.
|
||||||
|
const toggleSidebar = React.useCallback(() => {
|
||||||
|
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
|
||||||
|
}, [isMobile, setOpen, setOpenMobile]);
|
||||||
|
|
||||||
|
// Adds a keyboard shortcut to toggle the sidebar.
|
||||||
|
React.useEffect(() => {
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
|
||||||
|
event.preventDefault();
|
||||||
|
toggleSidebar();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, [toggleSidebar]);
|
||||||
|
|
||||||
|
// We add a state so that we can do data-state="expanded" or "collapsed".
|
||||||
|
// This makes it easier to style the sidebar with Tailwind classes.
|
||||||
|
const state = open ? "expanded" : "collapsed";
|
||||||
|
|
||||||
|
const contextValue = React.useMemo<SidebarContextProps>(
|
||||||
|
() => ({
|
||||||
|
state,
|
||||||
|
open,
|
||||||
|
setOpen,
|
||||||
|
isMobile,
|
||||||
|
openMobile,
|
||||||
|
setOpenMobile,
|
||||||
|
toggleSidebar,
|
||||||
|
}),
|
||||||
|
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SidebarContext.Provider value={contextValue}>
|
||||||
|
<TooltipProvider delayDuration={0}>
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-wrapper"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--sidebar-width": SIDEBAR_WIDTH,
|
||||||
|
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
||||||
|
...style,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
className={cn("group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</TooltipProvider>
|
||||||
|
</SidebarContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sidebar({
|
||||||
|
side = "left",
|
||||||
|
variant = "sidebar",
|
||||||
|
collapsible = "offcanvas",
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & {
|
||||||
|
side?: "left" | "right";
|
||||||
|
variant?: "sidebar" | "floating" | "inset";
|
||||||
|
collapsible?: "offcanvas" | "icon" | "none";
|
||||||
|
}) {
|
||||||
|
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
||||||
|
|
||||||
|
if (collapsible === "none") {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar"
|
||||||
|
className={cn("bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
|
||||||
|
<SheetContent
|
||||||
|
data-sidebar="sidebar"
|
||||||
|
data-slot="sidebar"
|
||||||
|
data-mobile="true"
|
||||||
|
className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
side={side}
|
||||||
|
>
|
||||||
|
<SheetHeader className="sr-only">
|
||||||
|
<SheetTitle>Sidebar</SheetTitle>
|
||||||
|
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
|
||||||
|
</SheetHeader>
|
||||||
|
<div className="flex h-full w-full flex-col">{children}</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="group peer text-sidebar-foreground hidden md:block"
|
||||||
|
data-state={state}
|
||||||
|
data-collapsible={state === "collapsed" ? collapsible : ""}
|
||||||
|
data-variant={variant}
|
||||||
|
data-side={side}
|
||||||
|
data-slot="sidebar"
|
||||||
|
>
|
||||||
|
{/* This is what handles the sidebar gap on desktop */}
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-gap"
|
||||||
|
className={cn(
|
||||||
|
"relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
|
||||||
|
"group-data-[collapsible=offcanvas]:w-0",
|
||||||
|
"group-data-[side=right]:rotate-180",
|
||||||
|
variant === "floating" || variant === "inset"
|
||||||
|
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
|
||||||
|
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon)",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-container"
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
|
||||||
|
side === "left"
|
||||||
|
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
||||||
|
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
||||||
|
// Adjust the padding for floating and inset variants.
|
||||||
|
variant === "floating" || variant === "inset"
|
||||||
|
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
|
||||||
|
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-sidebar="sidebar"
|
||||||
|
data-slot="sidebar-inner"
|
||||||
|
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {
|
||||||
|
const { toggleSidebar } = useSidebar();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
data-sidebar="trigger"
|
||||||
|
data-slot="sidebar-trigger"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className={cn("size-7", className)}
|
||||||
|
onClick={(event) => {
|
||||||
|
onClick?.(event);
|
||||||
|
toggleSidebar();
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<PanelLeftIcon />
|
||||||
|
<span className="sr-only">Toggle Sidebar</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
||||||
|
const { toggleSidebar } = useSidebar();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
data-sidebar="rail"
|
||||||
|
data-slot="sidebar-rail"
|
||||||
|
aria-label="Toggle Sidebar"
|
||||||
|
tabIndex={-1}
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
title="Toggle Sidebar"
|
||||||
|
className={cn(
|
||||||
|
"hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
|
||||||
|
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
|
||||||
|
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
||||||
|
"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
|
||||||
|
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
||||||
|
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
|
||||||
|
return (
|
||||||
|
<main
|
||||||
|
data-slot="sidebar-inset"
|
||||||
|
className={cn(
|
||||||
|
"bg-background relative flex w-full flex-1 flex-col",
|
||||||
|
"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>) {
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
data-slot="sidebar-input"
|
||||||
|
data-sidebar="input"
|
||||||
|
className={cn("bg-background h-8 w-full shadow-none", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-header"
|
||||||
|
data-sidebar="header"
|
||||||
|
className={cn("flex flex-col gap-2 p-2", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-footer"
|
||||||
|
data-sidebar="footer"
|
||||||
|
className={cn("flex flex-col gap-2 p-2", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
|
||||||
|
return (
|
||||||
|
<Separator
|
||||||
|
data-slot="sidebar-separator"
|
||||||
|
data-sidebar="separator"
|
||||||
|
className={cn("bg-sidebar-border mx-2 w-auto", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-content"
|
||||||
|
data-sidebar="content"
|
||||||
|
className={cn(
|
||||||
|
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-group"
|
||||||
|
data-sidebar="group"
|
||||||
|
className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarGroupLabel({
|
||||||
|
className,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & { asChild?: boolean }) {
|
||||||
|
const Comp = asChild ? Slot : "div";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="sidebar-group-label"
|
||||||
|
data-sidebar="group-label"
|
||||||
|
className={cn(
|
||||||
|
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
|
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarGroupAction({
|
||||||
|
className,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"button"> & { asChild?: boolean }) {
|
||||||
|
const Comp = asChild ? Slot : "button";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="sidebar-group-action"
|
||||||
|
data-sidebar="group-action"
|
||||||
|
className={cn(
|
||||||
|
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
|
// Increases the hit area of the button on mobile.
|
||||||
|
"after:absolute after:-inset-2 md:after:hidden",
|
||||||
|
"group-data-[collapsible=icon]:hidden",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarGroupContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-group-content"
|
||||||
|
data-sidebar="group-content"
|
||||||
|
className={cn("w-full text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
|
||||||
|
return (
|
||||||
|
<ul
|
||||||
|
data-slot="sidebar-menu"
|
||||||
|
data-sidebar="menu"
|
||||||
|
className={cn("flex w-full min-w-0 flex-col gap-1", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
data-slot="sidebar-menu-item"
|
||||||
|
data-sidebar="menu-item"
|
||||||
|
className={cn("group/menu-item relative", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarMenuButtonVariants = cva(
|
||||||
|
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||||
|
outline:
|
||||||
|
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-8 text-sm",
|
||||||
|
sm: "h-7 text-xs",
|
||||||
|
lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
function SidebarMenuButton({
|
||||||
|
asChild = false,
|
||||||
|
isActive = false,
|
||||||
|
variant = "default",
|
||||||
|
size = "default",
|
||||||
|
tooltip,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"button"> & {
|
||||||
|
asChild?: boolean;
|
||||||
|
isActive?: boolean;
|
||||||
|
tooltip?: string | React.ComponentProps<typeof TooltipContent>;
|
||||||
|
} & VariantProps<typeof sidebarMenuButtonVariants>) {
|
||||||
|
const Comp = asChild ? Slot : "button";
|
||||||
|
const { isMobile, state } = useSidebar();
|
||||||
|
|
||||||
|
const button = (
|
||||||
|
<Comp
|
||||||
|
data-slot="sidebar-menu-button"
|
||||||
|
data-sidebar="menu-button"
|
||||||
|
data-size={size}
|
||||||
|
data-active={isActive}
|
||||||
|
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!tooltip) {
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof tooltip === "string") {
|
||||||
|
tooltip = {
|
||||||
|
children: tooltip,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
||||||
|
<TooltipContent side="right" align="center" hidden={state !== "collapsed" || isMobile} {...tooltip} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenuAction({
|
||||||
|
className,
|
||||||
|
asChild = false,
|
||||||
|
showOnHover = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"button"> & {
|
||||||
|
asChild?: boolean;
|
||||||
|
showOnHover?: boolean;
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot : "button";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="sidebar-menu-action"
|
||||||
|
data-sidebar="menu-action"
|
||||||
|
className={cn(
|
||||||
|
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
|
// Increases the hit area of the button on mobile.
|
||||||
|
"after:absolute after:-inset-2 md:after:hidden",
|
||||||
|
"peer-data-[size=sm]/menu-button:top-1",
|
||||||
|
"peer-data-[size=default]/menu-button:top-1.5",
|
||||||
|
"peer-data-[size=lg]/menu-button:top-2.5",
|
||||||
|
"group-data-[collapsible=icon]:hidden",
|
||||||
|
showOnHover &&
|
||||||
|
"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenuBadge({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-menu-badge"
|
||||||
|
data-sidebar="menu-badge"
|
||||||
|
className={cn(
|
||||||
|
"text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none",
|
||||||
|
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
|
||||||
|
"peer-data-[size=sm]/menu-button:top-1",
|
||||||
|
"peer-data-[size=default]/menu-button:top-1.5",
|
||||||
|
"peer-data-[size=lg]/menu-button:top-2.5",
|
||||||
|
"group-data-[collapsible=icon]:hidden",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenuSkeleton({
|
||||||
|
className,
|
||||||
|
showIcon = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & {
|
||||||
|
showIcon?: boolean;
|
||||||
|
}) {
|
||||||
|
// Random width between 50 to 90%.
|
||||||
|
const width = React.useMemo(() => {
|
||||||
|
return `${Math.floor(Math.random() * 40) + 50}%`;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="sidebar-menu-skeleton"
|
||||||
|
data-sidebar="menu-skeleton"
|
||||||
|
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{showIcon && <Skeleton className="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
|
||||||
|
<Skeleton
|
||||||
|
className="h-4 max-w-(--skeleton-width) flex-1"
|
||||||
|
data-sidebar="menu-skeleton-text"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--skeleton-width": width,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
|
||||||
|
return (
|
||||||
|
<ul
|
||||||
|
data-slot="sidebar-menu-sub"
|
||||||
|
data-sidebar="menu-sub"
|
||||||
|
className={cn(
|
||||||
|
"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
|
||||||
|
"group-data-[collapsible=icon]:hidden",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
data-slot="sidebar-menu-sub-item"
|
||||||
|
data-sidebar="menu-sub-item"
|
||||||
|
className={cn("group/menu-sub-item relative", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarMenuSubButton({
|
||||||
|
asChild = false,
|
||||||
|
size = "md",
|
||||||
|
isActive = false,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"a"> & {
|
||||||
|
asChild?: boolean;
|
||||||
|
size?: "sm" | "md";
|
||||||
|
isActive?: boolean;
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot : "a";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="sidebar-menu-sub-button"
|
||||||
|
data-sidebar="menu-sub-button"
|
||||||
|
data-size={size}
|
||||||
|
data-active={isActive}
|
||||||
|
className={cn(
|
||||||
|
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||||
|
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
|
||||||
|
size === "sm" && "text-xs",
|
||||||
|
size === "md" && "text-sm",
|
||||||
|
"group-data-[collapsible=icon]:hidden",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Sidebar,
|
||||||
|
SidebarContent,
|
||||||
|
SidebarFooter,
|
||||||
|
SidebarGroup,
|
||||||
|
SidebarGroupAction,
|
||||||
|
SidebarGroupContent,
|
||||||
|
SidebarGroupLabel,
|
||||||
|
SidebarHeader,
|
||||||
|
SidebarInput,
|
||||||
|
SidebarInset,
|
||||||
|
SidebarMenu,
|
||||||
|
SidebarMenuAction,
|
||||||
|
SidebarMenuBadge,
|
||||||
|
SidebarMenuButton,
|
||||||
|
SidebarMenuItem,
|
||||||
|
SidebarMenuSkeleton,
|
||||||
|
SidebarMenuSub,
|
||||||
|
SidebarMenuSubButton,
|
||||||
|
SidebarMenuSubItem,
|
||||||
|
SidebarProvider,
|
||||||
|
SidebarRail,
|
||||||
|
SidebarSeparator,
|
||||||
|
SidebarTrigger,
|
||||||
|
useSidebar,
|
||||||
|
};
|
||||||
7
web/src/components/ui/skeleton.tsx
Normal file
7
web/src/components/ui/skeleton.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return <div data-slot="skeleton" className={cn("bg-accent animate-pulse rounded-md", className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Skeleton };
|
||||||
46
web/src/components/ui/tooltip.tsx
Normal file
46
web/src/components/ui/tooltip.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
function TooltipProvider({ delayDuration = 0, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
||||||
|
return <TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<TooltipProvider>
|
||||||
|
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
||||||
|
</TooltipProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
||||||
|
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TooltipContent({
|
||||||
|
className,
|
||||||
|
sideOffset = 0,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<TooltipPrimitive.Portal>
|
||||||
|
<TooltipPrimitive.Content
|
||||||
|
data-slot="tooltip-content"
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||||
|
</TooltipPrimitive.Content>
|
||||||
|
</TooltipPrimitive.Portal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
||||||
19
web/src/hooks/use-mobile.ts
Normal file
19
web/src/hooks/use-mobile.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
const MOBILE_BREAKPOINT = 768;
|
||||||
|
|
||||||
|
export function useIsMobile() {
|
||||||
|
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||||
|
const onChange = () => {
|
||||||
|
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||||
|
};
|
||||||
|
mql.addEventListener("change", onChange);
|
||||||
|
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||||
|
return () => mql.removeEventListener("change", onChange);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return !!isMobile;
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
const NotFound = () => <>NotFound</>;
|
|
||||||
export default NotFound;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/* Wouter */
|
|
||||||
import { Router, Switch, Route } from "wouter";
|
|
||||||
import { useHashLocation } from "wouter/use-hash-location";
|
|
||||||
|
|
||||||
/* Routes */
|
|
||||||
import NotFound from "@/routes/NotFound";
|
|
||||||
|
|
||||||
const RootRouter = () => (
|
|
||||||
<Router hook={useHashLocation}>
|
|
||||||
<Switch>
|
|
||||||
{/* Fallback (eg. 'Not Found / 404') */}
|
|
||||||
<Route component={NotFound} />
|
|
||||||
</Switch>
|
|
||||||
</Router>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default RootRouter;
|
|
||||||
14
web/src/routes/Routes.tsx
Normal file
14
web/src/routes/Routes.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* Wouter routing library */
|
||||||
|
import { Router, Switch, Route } from "wouter";
|
||||||
|
import { useHashLocation } from "wouter/use-hash-location";
|
||||||
|
|
||||||
|
const Routes = () => (
|
||||||
|
<Router hook={useHashLocation}>
|
||||||
|
<Switch>
|
||||||
|
<Route path="/" component={() => <>Home</>} />
|
||||||
|
<Route component={() => <>NotFound</>} />
|
||||||
|
</Switch>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Routes;
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
/* Styles */
|
/* Styles */
|
||||||
import "./styles/tailwind.css";
|
import "@/styles/tailwind.css";
|
||||||
|
|
||||||
/* React-related */
|
/* React-related */
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
|
||||||
/* Route-related */
|
/* Route-related */
|
||||||
import RootRouter from "@/routes/RootRouter";
|
import Routes from "@/routes/Routes";
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<RootRouter />
|
<Routes />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user