mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-26 23:21:39 +01:00
Begin switching from daisyui to shadcnui (#492)
* feat: add shadcn * feat: add themes * feat: make sidebar use shadcn * feat: sort bg * feat: lint fixes * feat: make daisyui toggleable, add tooltips to sidebar, add work in progress docs page * fix: theme switching for shadcn * Fix minor profile.vue issue * feat: update docs, enlarge SidebarMenuButton and refine profile layout * feat: add testing page * feat: update css and remove comments from template * fix: create dropdown not opening due to tooltip interference also lint * fix: correct CSS selector for homebox in main.css to ensure proper theming functionality * feat: make theme switching actually kinda work for shadcn * fix: sidebar colours * fix: remove unused router import, made sidebar indicate active page and sort tailwind config linting * style: update styles * chore: remove unused duplicate code * style: refine theme management, CSS variables, get styles closer to original * feat: implement suggested changes * feat: better button size --------- Co-authored-by: Matt Kilgore <tankerkiller125@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,778 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root,.homebox {
|
||||
--background: 0 0% 100%; /* base 100 */
|
||||
--foreground: 0 0% 20%; /* base content */
|
||||
|
||||
--muted: 0 0% 81%; /* base 300 */
|
||||
--muted-foreground: 0 0% 20%; /* base content */
|
||||
|
||||
--popover: 0 0% 100%; /* base 100 */
|
||||
--popover-foreground: 0 0% 20%; /* base content */
|
||||
|
||||
--card: 0 0% 100%; /* base 100 */
|
||||
--card-foreground: 0 0% 20%; /* base content */
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
|
||||
--primary: 139 16% 43%; /* primary */
|
||||
--primary-foreground: 139 100% 89%; /* primary text */
|
||||
|
||||
--secondary: 97 37% 93%; /* secondary */
|
||||
--secondary-foreground: 97 31% 19%; /* secondary text */
|
||||
|
||||
--accent: 47 100% 67%; /* accent */
|
||||
--accent-foreground: 47 100% 13%; /* accent text */
|
||||
|
||||
--destructive: 0 84.2% 60.2%; /* error */
|
||||
--destructive-foreground: 210 40% 98%; /* error text */
|
||||
|
||||
--ring: 222.2 84% 4.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
|
||||
|
||||
|
||||
--sidebar-background: var(--background);
|
||||
--sidebar-foreground: var(--foreground);
|
||||
--sidebar-primary: var(--primary);
|
||||
--sidebar-primary-foreground: var(--primary-foreground);
|
||||
--sidebar-accent: var(--accent);
|
||||
--sidebar-accent-foreground: var(--accent-foreground);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--ring);
|
||||
}
|
||||
|
||||
/*
|
||||
* The below themes are based on the daisyUI themes which are licensed under the MIT License.
|
||||
* Copyright (c) 2020 Pouya Saadeghi
|
||||
* The license can be found here https://github.com/saadeghi/daisyui
|
||||
*
|
||||
* The themes were converted to CSS variables by n0acar and licensed under the MIT License.
|
||||
* Copyright (c) 2024 n0acar
|
||||
* The license can be found here https://github.com/n0acar/tiny-projects
|
||||
*/
|
||||
|
||||
|
||||
.theme-aqua {
|
||||
--border: 219 11% 89%;
|
||||
--input: 219 11% 89%;
|
||||
--ring: 219 11% 89%;
|
||||
--background: 219 53% 43%;
|
||||
--foreground: 219 11% 89%;
|
||||
--primary: 182 93% 49%;
|
||||
--primary-foreground: 181 100% 17%;
|
||||
--secondary: 274 31% 57%;
|
||||
--secondary-foreground: 274 6% 11%;
|
||||
--destructive: 5 100% 70%;
|
||||
--destructive-foreground: 5 24% 15%;
|
||||
--muted: 219 45% 37%;
|
||||
--muted-foreground: 219 11% 89%;
|
||||
--accent: 47 100% 80%;
|
||||
--accent-foreground: 47 20% 16%;
|
||||
--popover: 219 53% 43%;
|
||||
--popover-foreground: 219 11% 89%;
|
||||
--card: 219 53% 43%;
|
||||
--card-foreground: 219 11% 89%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-black {
|
||||
--border: 224 0% 84%;
|
||||
--input: 224 0% 84%;
|
||||
--ring: 224 0% 84%;
|
||||
--background: 0 0% 0%;
|
||||
--foreground: 224 0% 84%;
|
||||
--primary: 224 0% 22%;
|
||||
--primary-foreground: 0 0% 84%;
|
||||
--secondary: 224 0% 22%;
|
||||
--secondary-foreground: 0 0% 84%;
|
||||
--destructive: 0 100% 50%;
|
||||
--destructive-foreground: 0 20% 10%;
|
||||
--muted: 224 0% 15%;
|
||||
--muted-foreground: 224 0% 84%;
|
||||
--accent: 224 0% 22%;
|
||||
--accent-foreground: 0 0% 84%;
|
||||
--popover: 0 0% 0%;
|
||||
--popover-foreground: 224 0% 84%;
|
||||
--card: 0 0% 0%;
|
||||
--card-foreground: 224 0% 84%;
|
||||
--radius: 0;
|
||||
}
|
||||
.theme-bumblebee {
|
||||
--border: 224 -35% 20%;
|
||||
--input: 224 -35% 20%;
|
||||
--ring: 224 -35% 20%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 -35% 20%;
|
||||
--primary: 51 100% 50%;
|
||||
--primary-foreground: 49 31% 23%;
|
||||
--secondary: 39 100% 50%;
|
||||
--secondary-foreground: 34 59% 23%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 224 -148% 86%;
|
||||
--muted-foreground: 224 -35% 20%;
|
||||
--accent: 28 100% 67%;
|
||||
--accent-foreground: 27 24% 14%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 224 -35% 20%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 224 -35% 20%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-cmyk {
|
||||
--border: 224 -35% 20%;
|
||||
--input: 224 -35% 20%;
|
||||
--ring: 224 -35% 20%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 -35% 20%;
|
||||
--primary: 203 83% 60%;
|
||||
--primary-foreground: 203 17% 12%;
|
||||
--secondary: 335 78% 60%;
|
||||
--secondary-foreground: 335 16% 12%;
|
||||
--destructive: 4 81% 56%;
|
||||
--destructive-foreground: 4 16% 11%;
|
||||
--muted: 224 -148% 86%;
|
||||
--muted-foreground: 224 -35% 20%;
|
||||
--accent: 56 100% 60%;
|
||||
--accent-foreground: 56 20% 12%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 224 -35% 20%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 224 -35% 20%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-corporate {
|
||||
--border: 233 27% 13%;
|
||||
--input: 233 27% 13%;
|
||||
--ring: 233 27% 13%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 233 27% 13%;
|
||||
--primary: 229 100% 65%;
|
||||
--primary-foreground: 229 22% 13%;
|
||||
--secondary: 215 26% 59%;
|
||||
--secondary-foreground: 215 5% 12%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 224 -148% 86%;
|
||||
--muted-foreground: 233 27% 13%;
|
||||
--accent: 154 49% 60%;
|
||||
--accent-foreground: 154 10% 12%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 233 27% 13%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 233 27% 13%;
|
||||
--radius: 0.125rem;
|
||||
}
|
||||
.theme-cupcake {
|
||||
--border: 280 46% 14%;
|
||||
--input: 280 46% 14%;
|
||||
--ring: 280 46% 14%;
|
||||
--background: 24 33% 97%;
|
||||
--foreground: 280 46% 14%;
|
||||
--primary: 183 47% 59%;
|
||||
--primary-foreground: 183 9% 12%;
|
||||
--secondary: 338 71% 78%;
|
||||
--secondary-foreground: 338 14% 16%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 23 14% 89%;
|
||||
--muted-foreground: 280 46% 14%;
|
||||
--accent: 39 84% 58%;
|
||||
--accent-foreground: 39 17% 12%;
|
||||
--popover: 24 33% 97%;
|
||||
--popover-foreground: 280 46% 14%;
|
||||
--card: 24 33% 97%;
|
||||
--card-foreground: 280 46% 14%;
|
||||
--radius: 1.9rem;
|
||||
}
|
||||
.theme-cyberpunk {
|
||||
--border: 55 20% 13%;
|
||||
--input: 55 20% 13%;
|
||||
--ring: 55 20% 13%;
|
||||
--background: 56 100% 64%;
|
||||
--foreground: 55 20% 13%;
|
||||
--primary: 343 100% 72%;
|
||||
--primary-foreground: 343 26% 15%;
|
||||
--secondary: 185 100% 49%;
|
||||
--secondary-foreground: 184 50% 6%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 55 88% 55%;
|
||||
--muted-foreground: 55 20% 13%;
|
||||
--accent: 279 100% 73%;
|
||||
--accent-foreground: 277 22% 15%;
|
||||
--popover: 56 100% 64%;
|
||||
--popover-foreground: 55 20% 13%;
|
||||
--card: 56 100% 64%;
|
||||
--card-foreground: 55 20% 13%;
|
||||
--radius: 0;
|
||||
}
|
||||
.theme-dark {
|
||||
--border: 220 13% 69%;
|
||||
--input: 220 13% 69%;
|
||||
--ring: 220 13% 69%;
|
||||
--background: 212 18% 14%;
|
||||
--foreground: 220 13% 69%;
|
||||
--primary: 235 100% 73%;
|
||||
--primary-foreground: 235 22% 15%;
|
||||
--secondary: 316 100% 69%;
|
||||
--secondary-foreground: 318 25% 14%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 213 18% 10%;
|
||||
--muted-foreground: 220 13% 69%;
|
||||
--accent: 174 100% 40%;
|
||||
--accent-foreground: 176 51% 5%;
|
||||
--popover: 212 18% 14%;
|
||||
--popover-foreground: 220 13% 69%;
|
||||
--card: 212 18% 14%;
|
||||
--card-foreground: 220 13% 69%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-dracula {
|
||||
--border: 60 30% 96%;
|
||||
--input: 60 30% 96%;
|
||||
--ring: 60 30% 96%;
|
||||
--background: 231 15% 18%;
|
||||
--foreground: 60 30% 96%;
|
||||
--primary: 326 100% 74%;
|
||||
--primary-foreground: 326 20% 15%;
|
||||
--secondary: 265 89% 78%;
|
||||
--secondary-foreground: 265 18% 16%;
|
||||
--destructive: 0 100% 67%;
|
||||
--destructive-foreground: 0 20% 13%;
|
||||
--muted: 231 13% 16%;
|
||||
--muted-foreground: 60 30% 96%;
|
||||
--accent: 31 100% 71%;
|
||||
--accent-foreground: 31 20% 14%;
|
||||
--popover: 231 15% 18%;
|
||||
--popover-foreground: 60 30% 96%;
|
||||
--card: 231 15% 18%;
|
||||
--card-foreground: 60 30% 96%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-emerald {
|
||||
--border: 219 20% 25%;
|
||||
--input: 219 20% 25%;
|
||||
--ring: 219 20% 25%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 219 20% 25%;
|
||||
--primary: 141 50% 60%;
|
||||
--primary-foreground: 151 28% 19%;
|
||||
--secondary: 219 96% 60%;
|
||||
--secondary-foreground: 180 100% 100%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 224 -148% 86%;
|
||||
--muted-foreground: 219 20% 25%;
|
||||
--accent: 10 89% 68%;
|
||||
--accent-foreground: 0 0% 0%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 219 20% 25%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 219 20% 25%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-fantasy {
|
||||
--border: 215 28% 17%;
|
||||
--input: 215 28% 17%;
|
||||
--ring: 215 28% 17%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 215 28% 17%;
|
||||
--primary: 296 100% 23%;
|
||||
--primary-foreground: 296 26% 84%;
|
||||
--secondary: 203 100% 37%;
|
||||
--secondary-foreground: 199 35% 86%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 224 -148% 86%;
|
||||
--muted-foreground: 215 28% 17%;
|
||||
--accent: 32 100% 50%;
|
||||
--accent-foreground: 35 29% 9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 215 28% 17%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 215 28% 17%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-forest {
|
||||
--border: 0 2% 82%;
|
||||
--input: 0 2% 82%;
|
||||
--ring: 0 2% 82%;
|
||||
--background: 0 12% 8%;
|
||||
--foreground: 0 2% 82%;
|
||||
--primary: 141 72% 42%;
|
||||
--primary-foreground: 0 0% 0%;
|
||||
--secondary: 164 73% 42%;
|
||||
--secondary-foreground: 164 15% 8%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 0 10% 7%;
|
||||
--muted-foreground: 0 2% 82%;
|
||||
--accent: 175 73% 42%;
|
||||
--accent-foreground: 175 15% 8%;
|
||||
--popover: 0 12% 8%;
|
||||
--popover-foreground: 0 2% 82%;
|
||||
--card: 0 12% 8%;
|
||||
--card-foreground: 0 2% 82%;
|
||||
--radius: 1.9rem;
|
||||
}
|
||||
.theme-garden {
|
||||
--border: 0 3% 6%;
|
||||
--input: 0 3% 6%;
|
||||
--ring: 0 3% 6%;
|
||||
--background: 0 4% 91%;
|
||||
--foreground: 0 3% 6%;
|
||||
--primary: 332 100% 49%;
|
||||
--primary-foreground: 180 100% 100%;
|
||||
--secondary: 334 37% 41%;
|
||||
--secondary-foreground: 334 7% 88%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 0 4% 78%;
|
||||
--muted-foreground: 0 3% 6%;
|
||||
--accent: 139 16% 43%;
|
||||
--accent-foreground: 139 3% 9%;
|
||||
--popover: 0 4% 91%;
|
||||
--popover-foreground: 0 3% 6%;
|
||||
--card: 0 4% 91%;
|
||||
--card-foreground: 0 3% 6%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-halloween {
|
||||
--border: 0 0% 83%;
|
||||
--input: 0 0% 83%;
|
||||
--ring: 0 0% 83%;
|
||||
--background: 224 0% 13%;
|
||||
--foreground: 0 0% 83%;
|
||||
--primary: 34 100% 50%;
|
||||
--primary-foreground: 180 7% 8%;
|
||||
--secondary: 278 100% 38%;
|
||||
--secondary-foreground: 279 24% 87%;
|
||||
--destructive: 3 87% 62%;
|
||||
--destructive-foreground: 3 17% 12%;
|
||||
--muted: 0 0% 11%;
|
||||
--muted-foreground: 0 0% 83%;
|
||||
--accent: 96 100% 33%;
|
||||
--accent-foreground: 0 0% 0%;
|
||||
--popover: 224 0% 13%;
|
||||
--popover-foreground: 0 0% 83%;
|
||||
--card: 224 0% 13%;
|
||||
--card-foreground: 0 0% 83%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-light {
|
||||
--border: 215 28% 17%;
|
||||
--input: 215 28% 17%;
|
||||
--ring: 215 28% 17%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 215 28% 17%;
|
||||
--primary: 257 100% 50%;
|
||||
--primary-foreground: 258 22% 90%;
|
||||
--secondary: 313 100% 56%;
|
||||
--secondary-foreground: 320 100% 99%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 180 2% 90%;
|
||||
--muted-foreground: 215 28% 17%;
|
||||
--accent: 174 100% 41%;
|
||||
--accent-foreground: 176 59% 4%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 215 28% 17%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 215 28% 17%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-lofi {
|
||||
--border: 0 0% 0%;
|
||||
--input: 0 0% 0%;
|
||||
--ring: 0 0% 0%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 0%;
|
||||
--primary: 224 0% 5%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 0 2% 10%;
|
||||
--secondary-foreground: 0 0% 100%;
|
||||
--destructive: 7 100% 76%;
|
||||
--destructive-foreground: 7 24% 16%;
|
||||
--muted: 0 2% 90%;
|
||||
--muted-foreground: 0 0% 0%;
|
||||
--accent: 224 0% 15%;
|
||||
--accent-foreground: 0 0% 100%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 0 0% 0%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 0%;
|
||||
--radius: 0.125rem;
|
||||
}
|
||||
.theme-luxury {
|
||||
--border: 37 67% 58%;
|
||||
--input: 37 67% 58%;
|
||||
--ring: 37 67% 58%;
|
||||
--background: 240 10% 4%;
|
||||
--foreground: 37 67% 58%;
|
||||
--primary: 0 0% 100%;
|
||||
--primary-foreground: 224 -35% 20%;
|
||||
--secondary: 218 54% 18%;
|
||||
--secondary-foreground: 218 11% 84%;
|
||||
--destructive: 0 100% 72%;
|
||||
--destructive-foreground: 0 20% 14%;
|
||||
--muted: 270 2% 18%;
|
||||
--muted-foreground: 37 67% 58%;
|
||||
--accent: 319 22% 26%;
|
||||
--accent-foreground: 319 4% 85%;
|
||||
--popover: 240 10% 4%;
|
||||
--popover-foreground: 37 67% 58%;
|
||||
--card: 240 10% 4%;
|
||||
--card-foreground: 37 67% 58%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-pastel {
|
||||
--border: 224 -35% 20%;
|
||||
--input: 224 -35% 20%;
|
||||
--ring: 224 -35% 20%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 -35% 20%;
|
||||
--primary: 284 22% 80%;
|
||||
--primary-foreground: 284 4% 16%;
|
||||
--secondary: 352 70% 88%;
|
||||
--secondary-foreground: 352 14% 18%;
|
||||
--destructive: 358.25 100% 69%;
|
||||
--destructive-foreground: 0 0% 0%;
|
||||
--muted: 216 12% 84%;
|
||||
--muted-foreground: 224 -35% 20%;
|
||||
--accent: 158 55% 81%;
|
||||
--accent-foreground: 158 11% 16%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 224 -35% 20%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 224 -35% 20%;
|
||||
--radius: 1.9rem;
|
||||
}
|
||||
.theme-retro {
|
||||
--border: 345 5% 15%;
|
||||
--input: 345 5% 15%;
|
||||
--ring: 345 5% 15%;
|
||||
--background: 44 47% 86%;
|
||||
--foreground: 345 5% 15%;
|
||||
--primary: 3 74% 76%;
|
||||
--primary-foreground: 345 5% 15%;
|
||||
--secondary: 145 27% 72%;
|
||||
--secondary-foreground: 345 5% 15%;
|
||||
--destructive: 3 87% 62%;
|
||||
--destructive-foreground: 3 17% 12%;
|
||||
--muted: 44 47% 73%;
|
||||
--muted-foreground: 345 5% 15%;
|
||||
--accent: 24 67% 59%;
|
||||
--accent-foreground: 345 5% 15%;
|
||||
--popover: 44 47% 86%;
|
||||
--popover-foreground: 345 5% 15%;
|
||||
--card: 44 47% 86%;
|
||||
--card-foreground: 345 5% 15%;
|
||||
--radius: 0.4rem;
|
||||
}
|
||||
.theme-synthwave {
|
||||
--border: 260 60% 98%;
|
||||
--input: 260 60% 98%;
|
||||
--ring: 260 60% 98%;
|
||||
--background: 253 58% 15%;
|
||||
--foreground: 260 60% 98%;
|
||||
--primary: 321 70% 69%;
|
||||
--primary-foreground: 321 14% 14%;
|
||||
--secondary: 197 87% 65%;
|
||||
--secondary-foreground: 197 17% 13%;
|
||||
--destructive: 10 75% 70%;
|
||||
--destructive-foreground: 257 63% 17%;
|
||||
--muted: 253 50% 13%;
|
||||
--muted-foreground: 260 60% 98%;
|
||||
--accent: 50 100% 50%;
|
||||
--accent-foreground: 51 35% 7%;
|
||||
--popover: 253 58% 15%;
|
||||
--popover-foreground: 260 60% 98%;
|
||||
--card: 253 58% 15%;
|
||||
--card-foreground: 260 60% 98%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-valentine {
|
||||
--border: 344 38% 28%;
|
||||
--input: 344 38% 28%;
|
||||
--ring: 344 38% 28%;
|
||||
--background: 319 66% 94%;
|
||||
--foreground: 344 38% 28%;
|
||||
--primary: 353 74% 67%;
|
||||
--primary-foreground: 353 15% 13%;
|
||||
--secondary: 254 86% 77%;
|
||||
--secondary-foreground: 254 17% 15%;
|
||||
--destructive: 5 100% 69%;
|
||||
--destructive-foreground: 4 25% 14%;
|
||||
--muted: 319 56% 81%;
|
||||
--muted-foreground: 344 38% 28%;
|
||||
--accent: 182 34% 55%;
|
||||
--accent-foreground: 182 7% 11%;
|
||||
--popover: 319 66% 94%;
|
||||
--popover-foreground: 344 38% 28%;
|
||||
--card: 319 66% 94%;
|
||||
--card-foreground: 344 38% 28%;
|
||||
--radius: 1.9rem;
|
||||
}
|
||||
.theme-wireframe {
|
||||
--border: 224 -35% 20%;
|
||||
--input: 224 -35% 20%;
|
||||
--ring: 224 -35% 20%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 -35% 20%;
|
||||
--primary: 224 0% 72%;
|
||||
--primary-foreground: 0 0% 14%;
|
||||
--secondary: 224 0% 72%;
|
||||
--secondary-foreground: 0 0% 14%;
|
||||
--destructive: 0 100% 50%;
|
||||
--destructive-foreground: 0 20% 10%;
|
||||
--muted: 224 0% 87%;
|
||||
--muted-foreground: 224 -35% 20%;
|
||||
--accent: 224 0% 72%;
|
||||
--accent-foreground: 0 0% 14%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 224 -35% 20%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 224 -35% 20%;
|
||||
--radius: 0.2rem;
|
||||
}
|
||||
.theme-autumn {
|
||||
--border: 0 0% 19%;
|
||||
--input: 0 0% 19%;
|
||||
--ring: 0 0% 19%;
|
||||
--background: 224 0% 95%;
|
||||
--foreground: 0 0% 19%;
|
||||
--primary: 344 96% 28%;
|
||||
--primary-foreground: 344 19% 86%;
|
||||
--secondary: 0 63% 58%;
|
||||
--secondary-foreground: 0 13% 12%;
|
||||
--destructive: 353 100% 41%;
|
||||
--destructive-foreground: 346 29% 87%;
|
||||
--muted: 0 0% 81%;
|
||||
--muted-foreground: 0 0% 19%;
|
||||
--accent: 27 56% 63%;
|
||||
--accent-foreground: 27 11% 13%;
|
||||
--popover: 224 0% 95%;
|
||||
--popover-foreground: 0 0% 19%;
|
||||
--card: 224 0% 95%;
|
||||
--card-foreground: 0 0% 19%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-business {
|
||||
--border: 0 0% 83%;
|
||||
--input: 0 0% 83%;
|
||||
--ring: 0 0% 83%;
|
||||
--background: 224 0% 13%;
|
||||
--foreground: 0 0% 83%;
|
||||
--primary: 210 64% 31%;
|
||||
--primary-foreground: 210 13% 86%;
|
||||
--secondary: 200 13% 55%;
|
||||
--secondary-foreground: 200 3% 11%;
|
||||
--destructive: 6 56% 43%;
|
||||
--destructive-foreground: 6 11% 89%;
|
||||
--muted: 0 0% 11%;
|
||||
--muted-foreground: 0 0% 83%;
|
||||
--accent: 13 80% 60%;
|
||||
--accent-foreground: 13 16% 12%;
|
||||
--popover: 224 0% 13%;
|
||||
--popover-foreground: 0 0% 83%;
|
||||
--card: 224 0% 13%;
|
||||
--card-foreground: 0 0% 83%;
|
||||
--radius: 0.125rem;
|
||||
}
|
||||
.theme-acid {
|
||||
--border: 0 0% 20%;
|
||||
--input: 0 0% 20%;
|
||||
--ring: 0 0% 20%;
|
||||
--background: 224 0% 98%;
|
||||
--foreground: 0 0% 20%;
|
||||
--primary: 300 100% 53%;
|
||||
--primary-foreground: 302 30% 9%;
|
||||
--secondary: 28 100% 50%;
|
||||
--secondary-foreground: 30 29% 9%;
|
||||
--destructive: 2 100% 51%;
|
||||
--destructive-foreground: 357 29% 9%;
|
||||
--muted: 0 0% 84%;
|
||||
--muted-foreground: 0 0% 20%;
|
||||
--accent: 73 100% 50%;
|
||||
--accent-foreground: 70 39% 7%;
|
||||
--popover: 224 0% 98%;
|
||||
--popover-foreground: 0 0% 20%;
|
||||
--card: 224 0% 98%;
|
||||
--card-foreground: 0 0% 20%;
|
||||
--radius: 1rem;
|
||||
}
|
||||
.theme-lemonade {
|
||||
--border: 83 16% 19%;
|
||||
--input: 83 16% 19%;
|
||||
--ring: 83 16% 19%;
|
||||
--background: 83 82% 97%;
|
||||
--foreground: 83 16% 19%;
|
||||
--primary: 93 100% 29%;
|
||||
--primary-foreground: 86 36% 4%;
|
||||
--secondary: 61 100% 38%;
|
||||
--secondary-foreground: 61 39% 5%;
|
||||
--destructive: 6 59% 85%;
|
||||
--destructive-foreground: 6 12% 17%;
|
||||
--muted: 83 70% 83%;
|
||||
--muted-foreground: 83 16% 19%;
|
||||
--accent: 53 100% 46%;
|
||||
--accent-foreground: 54 35% 7%;
|
||||
--popover: 83 82% 97%;
|
||||
--popover-foreground: 83 16% 19%;
|
||||
--card: 83 82% 97%;
|
||||
--card-foreground: 83 16% 19%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-night {
|
||||
--border: 222 9% 82%;
|
||||
--input: 222 9% 82%;
|
||||
--ring: 222 9% 82%;
|
||||
--background: 222 47% 11%;
|
||||
--foreground: 222 9% 82%;
|
||||
--primary: 198 93% 60%;
|
||||
--primary-foreground: 198 19% 12%;
|
||||
--secondary: 234 89% 74%;
|
||||
--secondary-foreground: 234 18% 15%;
|
||||
--destructive: 351 95% 71%;
|
||||
--destructive-foreground: 351 19% 14%;
|
||||
--muted: 222 41% 10%;
|
||||
--muted-foreground: 222 9% 82%;
|
||||
--accent: 329 86% 70%;
|
||||
--accent-foreground: 329 17% 14%;
|
||||
--popover: 222 47% 11%;
|
||||
--popover-foreground: 222 9% 82%;
|
||||
--card: 222 47% 11%;
|
||||
--card-foreground: 222 9% 82%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-coffee {
|
||||
--border: 37 47% 57%;
|
||||
--input: 37 47% 57%;
|
||||
--ring: 37 47% 57%;
|
||||
--background: 306 19% 11%;
|
||||
--foreground: 37 47% 57%;
|
||||
--primary: 30 67% 58%;
|
||||
--primary-foreground: 30 13% 12%;
|
||||
--secondary: 182 25% 20%;
|
||||
--secondary-foreground: 182 5% 84%;
|
||||
--destructive: 10 95% 75%;
|
||||
--destructive-foreground: 10 19% 15%;
|
||||
--muted: 306 16% 9%;
|
||||
--muted-foreground: 37 47% 57%;
|
||||
--accent: 194 74% 25%;
|
||||
--accent-foreground: 194 15% 85%;
|
||||
--popover: 306 19% 11%;
|
||||
--popover-foreground: 37 47% 57%;
|
||||
--card: 306 19% 11%;
|
||||
--card-foreground: 37 47% 57%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-winter {
|
||||
--border: 214 30% 32%;
|
||||
--input: 214 30% 32%;
|
||||
--ring: 214 30% 32%;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 214 30% 32%;
|
||||
--primary: 215 100% 50%;
|
||||
--primary-foreground: 211 28% 89%;
|
||||
--secondary: 247 47% 43%;
|
||||
--secondary-foreground: 247 9% 89%;
|
||||
--destructive: 0 63% 72%;
|
||||
--destructive-foreground: 0 13% 14%;
|
||||
--muted: 219 44% 92%;
|
||||
--muted-foreground: 214 30% 32%;
|
||||
--accent: 310 49% 52%;
|
||||
--accent-foreground: 310 10% 10%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 214 30% 32%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 214 30% 32%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-dim {
|
||||
--border: 197 31% 77%;
|
||||
--input: 197 31% 77%;
|
||||
--ring: 197 31% 77%;
|
||||
--background: 220 18% 20%;
|
||||
--foreground: 197 31% 77%;
|
||||
--primary: 108 66% 73%;
|
||||
--primary-foreground: 108 13% 15%;
|
||||
--secondary: 12 100% 68%;
|
||||
--secondary-foreground: 12 20% 14%;
|
||||
--destructive: 11 100% 80%;
|
||||
--destructive-foreground: 11 20% 16%;
|
||||
--muted: 219 18% 15%;
|
||||
--muted-foreground: 197 31% 77%;
|
||||
--accent: 277 66% 74%;
|
||||
--accent-foreground: 277 13% 15%;
|
||||
--popover: 220 18% 20%;
|
||||
--popover-foreground: 197 31% 77%;
|
||||
--card: 220 18% 20%;
|
||||
--card-foreground: 197 31% 77%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
.theme-nord {
|
||||
--border: 220 16% 22%;
|
||||
--input: 220 16% 22%;
|
||||
--ring: 220 16% 22%;
|
||||
--background: 217 27% 94%;
|
||||
--foreground: 220 16% 22%;
|
||||
--primary: 213 32% 52%;
|
||||
--primary-foreground: 213 6% 10%;
|
||||
--secondary: 210 34% 63%;
|
||||
--secondary-foreground: 210 7% 13%;
|
||||
--destructive: 354 42% 56%;
|
||||
--destructive-foreground: 354 8% 11%;
|
||||
--muted: 219 28% 88%;
|
||||
--muted-foreground: 220 16% 22%;
|
||||
--accent: 193 43% 67%;
|
||||
--accent-foreground: 193 9% 13%;
|
||||
--popover: 217 27% 94%;
|
||||
--popover-foreground: 220 16% 22%;
|
||||
--card: 217 27% 94%;
|
||||
--card-foreground: 220 16% 22%;
|
||||
--radius: 0.2rem;
|
||||
}
|
||||
.theme-sunset {
|
||||
--border: 208 34% 72%;
|
||||
--input: 208 34% 72%;
|
||||
--ring: 208 34% 72%;
|
||||
--background: 204 31% 10%;
|
||||
--foreground: 208 34% 72%;
|
||||
--primary: 16 100% 68%;
|
||||
--primary-foreground: 16 20% 14%;
|
||||
--secondary: 341 97% 71%;
|
||||
--secondary-foreground: 341 19% 14%;
|
||||
--destructive: 358 100% 87%;
|
||||
--destructive-foreground: 358 20% 17%;
|
||||
--muted: 204 45% 7%;
|
||||
--muted-foreground: 208 34% 72%;
|
||||
--accent: 263 92% 75%;
|
||||
--accent-foreground: 263 18% 15%;
|
||||
--popover: 204 31% 10%;
|
||||
--popover-foreground: 208 34% 72%;
|
||||
--card: 204 31% 10%;
|
||||
--card-foreground: 208 34% 72%;
|
||||
--radius: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
.text-no-transform {
|
||||
text-transform: none !important;
|
||||
}
|
||||
@@ -35,7 +810,7 @@
|
||||
|
||||
.scroll-bg::-webkit-scrollbar-thumb {
|
||||
border-radius: 0.25rem;
|
||||
@apply bg-base-300;
|
||||
@apply bg-muted;
|
||||
}
|
||||
|
||||
.markdown > :first-child {
|
||||
|
||||
18
frontend/components.json
Normal file
18
frontend/components.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://shadcn-vue.com/schema.json",
|
||||
"style": "default",
|
||||
"typescript": true,
|
||||
"tsConfigPath": "tsconfig.json",
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "assets/css/main.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"framework": "nuxt",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
||||
22
frontend/components/ui/button/Button.vue
Normal file
22
frontend/components/ui/button/Button.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Primitive, type PrimitiveProps } from "radix-vue";
|
||||
import { type ButtonVariants, buttonVariants } from ".";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
variant?: ButtonVariants["variant"];
|
||||
size?: ButtonVariants["size"];
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "button",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :as-child="asChild" :class="cn(buttonVariants({ variant, size }), props.class)">
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
31
frontend/components/ui/button/index.ts
Normal file
31
frontend/components/ui/button/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
export { default as Button } from "./Button.vue";
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "size-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>;
|
||||
19
frontend/components/ui/dropdown-menu/DropdownMenu.vue
Normal file
19
frontend/components/ui/dropdown-menu/DropdownMenu.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
DropdownMenuRoot,
|
||||
type DropdownMenuRootEmits,
|
||||
type DropdownMenuRootProps,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
|
||||
const props = defineProps<DropdownMenuRootProps>();
|
||||
const emits = defineEmits<DropdownMenuRootEmits>();
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</DropdownMenuRoot>
|
||||
</template>
|
||||
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import { Check } from "lucide-vue-next";
|
||||
import {
|
||||
DropdownMenuCheckboxItem,
|
||||
type DropdownMenuCheckboxItemEmits,
|
||||
type DropdownMenuCheckboxItemProps,
|
||||
DropdownMenuItemIndicator,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DropdownMenuCheckboxItemProps & { class?: HTMLAttributes["class"] }>();
|
||||
const emits = defineEmits<DropdownMenuCheckboxItemEmits>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuCheckboxItem
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuItemIndicator>
|
||||
<Check class="size-4" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
</DropdownMenuCheckboxItem>
|
||||
</template>
|
||||
40
frontend/components/ui/dropdown-menu/DropdownMenuContent.vue
Normal file
40
frontend/components/ui/dropdown-menu/DropdownMenuContent.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
DropdownMenuContent,
|
||||
type DropdownMenuContentEmits,
|
||||
type DropdownMenuContentProps,
|
||||
DropdownMenuPortal,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = withDefaults(defineProps<DropdownMenuContentProps & { class?: HTMLAttributes["class"] }>(), {
|
||||
sideOffset: 4,
|
||||
});
|
||||
const emits = defineEmits<DropdownMenuContentEmits>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</template>
|
||||
11
frontend/components/ui/dropdown-menu/DropdownMenuGroup.vue
Normal file
11
frontend/components/ui/dropdown-menu/DropdownMenuGroup.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { DropdownMenuGroup, type DropdownMenuGroupProps } from "radix-vue";
|
||||
|
||||
const props = defineProps<DropdownMenuGroupProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuGroup v-bind="props">
|
||||
<slot />
|
||||
</DropdownMenuGroup>
|
||||
</template>
|
||||
30
frontend/components/ui/dropdown-menu/DropdownMenuItem.vue
Normal file
30
frontend/components/ui/dropdown-menu/DropdownMenuItem.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { DropdownMenuItem, type DropdownMenuItemProps, useForwardProps } from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DropdownMenuItemProps & { class?: HTMLAttributes["class"]; inset?: boolean }>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuItem
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm gap-2 px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuItem>
|
||||
</template>
|
||||
24
frontend/components/ui/dropdown-menu/DropdownMenuLabel.vue
Normal file
24
frontend/components/ui/dropdown-menu/DropdownMenuLabel.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { DropdownMenuLabel, type DropdownMenuLabelProps, useForwardProps } from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DropdownMenuLabelProps & { class?: HTMLAttributes["class"]; inset?: boolean }>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuLabel
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuLabel>
|
||||
</template>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
DropdownMenuRadioGroup,
|
||||
type DropdownMenuRadioGroupEmits,
|
||||
type DropdownMenuRadioGroupProps,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
|
||||
const props = defineProps<DropdownMenuRadioGroupProps>();
|
||||
const emits = defineEmits<DropdownMenuRadioGroupEmits>();
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuRadioGroup v-bind="forwarded">
|
||||
<slot />
|
||||
</DropdownMenuRadioGroup>
|
||||
</template>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import { Circle } from "lucide-vue-next";
|
||||
import {
|
||||
DropdownMenuItemIndicator,
|
||||
DropdownMenuRadioItem,
|
||||
type DropdownMenuRadioItemEmits,
|
||||
type DropdownMenuRadioItemProps,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DropdownMenuRadioItemProps & { class?: HTMLAttributes["class"] }>();
|
||||
|
||||
const emits = defineEmits<DropdownMenuRadioItemEmits>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuRadioItem
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuItemIndicator>
|
||||
<Circle class="size-2 fill-current" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
</DropdownMenuRadioItem>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { DropdownMenuSeparator, type DropdownMenuSeparatorProps } from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<
|
||||
DropdownMenuSeparatorProps & {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-muted', props.class)" />
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="cn('ml-auto text-xs tracking-widest opacity-60', props.class)">
|
||||
<slot />
|
||||
</span>
|
||||
</template>
|
||||
19
frontend/components/ui/dropdown-menu/DropdownMenuSub.vue
Normal file
19
frontend/components/ui/dropdown-menu/DropdownMenuSub.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
DropdownMenuSub,
|
||||
type DropdownMenuSubEmits,
|
||||
type DropdownMenuSubProps,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
|
||||
const props = defineProps<DropdownMenuSubProps>();
|
||||
const emits = defineEmits<DropdownMenuSubEmits>();
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuSub v-bind="forwarded">
|
||||
<slot />
|
||||
</DropdownMenuSub>
|
||||
</template>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
DropdownMenuSubContent,
|
||||
type DropdownMenuSubContentEmits,
|
||||
type DropdownMenuSubContentProps,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DropdownMenuSubContentProps & { class?: HTMLAttributes["class"] }>();
|
||||
const emits = defineEmits<DropdownMenuSubContentEmits>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuSubContent
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuSubContent>
|
||||
</template>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { ChevronRight } from "lucide-vue-next";
|
||||
import { DropdownMenuSubTrigger, type DropdownMenuSubTriggerProps, useForwardProps } from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DropdownMenuSubTriggerProps & { class?: HTMLAttributes["class"] }>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuSubTrigger
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
<ChevronRight class="ml-auto size-4" />
|
||||
</DropdownMenuSubTrigger>
|
||||
</template>
|
||||
13
frontend/components/ui/dropdown-menu/DropdownMenuTrigger.vue
Normal file
13
frontend/components/ui/dropdown-menu/DropdownMenuTrigger.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { DropdownMenuTrigger, type DropdownMenuTriggerProps, useForwardProps } from "radix-vue";
|
||||
|
||||
const props = defineProps<DropdownMenuTriggerProps>();
|
||||
|
||||
const forwardedProps = useForwardProps(props);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuTrigger class="outline-none" v-bind="forwardedProps">
|
||||
<slot />
|
||||
</DropdownMenuTrigger>
|
||||
</template>
|
||||
16
frontend/components/ui/dropdown-menu/index.ts
Normal file
16
frontend/components/ui/dropdown-menu/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export { default as DropdownMenu } from "./DropdownMenu.vue";
|
||||
|
||||
export { default as DropdownMenuCheckboxItem } from "./DropdownMenuCheckboxItem.vue";
|
||||
export { default as DropdownMenuContent } from "./DropdownMenuContent.vue";
|
||||
export { default as DropdownMenuGroup } from "./DropdownMenuGroup.vue";
|
||||
export { default as DropdownMenuItem } from "./DropdownMenuItem.vue";
|
||||
export { default as DropdownMenuLabel } from "./DropdownMenuLabel.vue";
|
||||
export { default as DropdownMenuRadioGroup } from "./DropdownMenuRadioGroup.vue";
|
||||
export { default as DropdownMenuRadioItem } from "./DropdownMenuRadioItem.vue";
|
||||
export { default as DropdownMenuSeparator } from "./DropdownMenuSeparator.vue";
|
||||
export { default as DropdownMenuShortcut } from "./DropdownMenuShortcut.vue";
|
||||
export { default as DropdownMenuSub } from "./DropdownMenuSub.vue";
|
||||
export { default as DropdownMenuSubContent } from "./DropdownMenuSubContent.vue";
|
||||
export { default as DropdownMenuSubTrigger } from "./DropdownMenuSubTrigger.vue";
|
||||
export { default as DropdownMenuTrigger } from "./DropdownMenuTrigger.vue";
|
||||
export { DropdownMenuPortal } from "radix-vue";
|
||||
32
frontend/components/ui/input/Input.vue
Normal file
32
frontend/components/ui/input/Input.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { useVModel } from "@vueuse/core";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
defaultValue?: string | number;
|
||||
modelValue?: string | number;
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: "update:modelValue", payload: string | number): void;
|
||||
}>();
|
||||
|
||||
const modelValue = useVModel(props, "modelValue", emits, {
|
||||
passive: true,
|
||||
defaultValue: props.defaultValue,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input
|
||||
v-model="modelValue"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
1
frontend/components/ui/input/index.ts
Normal file
1
frontend/components/ui/input/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Input } from "./Input.vue";
|
||||
33
frontend/components/ui/separator/Separator.vue
Normal file
33
frontend/components/ui/separator/Separator.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { Separator, type SeparatorProps } from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<SeparatorProps & { class?: HTMLAttributes["class"]; label?: string }>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Separator
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn('shrink-0 bg-border relative', props.orientation === 'vertical' ? 'w-px h-full' : 'h-px w-full', props.class)
|
||||
"
|
||||
>
|
||||
<span
|
||||
v-if="props.label"
|
||||
:class="
|
||||
cn(
|
||||
'text-xs text-muted-foreground bg-background absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex justify-center items-center',
|
||||
props.orientation === 'vertical' ? 'w-[1px] px-1 py-2' : 'h-[1px] py-1 px-2'
|
||||
)
|
||||
"
|
||||
>{{ props.label }}</span
|
||||
>
|
||||
</Separator>
|
||||
</template>
|
||||
1
frontend/components/ui/separator/index.ts
Normal file
1
frontend/components/ui/separator/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Separator } from "./Separator.vue";
|
||||
14
frontend/components/ui/sheet/Sheet.vue
Normal file
14
frontend/components/ui/sheet/Sheet.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogRoot, type DialogRootEmits, type DialogRootProps, useForwardPropsEmits } from "radix-vue";
|
||||
|
||||
const props = defineProps<DialogRootProps>();
|
||||
const emits = defineEmits<DialogRootEmits>();
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</DialogRoot>
|
||||
</template>
|
||||
11
frontend/components/ui/sheet/SheetClose.vue
Normal file
11
frontend/components/ui/sheet/SheetClose.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogClose, type DialogCloseProps } from "radix-vue";
|
||||
|
||||
const props = defineProps<DialogCloseProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogClose v-bind="props">
|
||||
<slot />
|
||||
</DialogClose>
|
||||
</template>
|
||||
53
frontend/components/ui/sheet/SheetContent.vue
Normal file
53
frontend/components/ui/sheet/SheetContent.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { X } from "lucide-vue-next";
|
||||
import {
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
type DialogContentEmits,
|
||||
type DialogContentProps,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { type SheetVariants, sheetVariants } from ".";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface SheetContentProps extends DialogContentProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
side?: SheetVariants["side"];
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = defineProps<SheetContentProps>();
|
||||
|
||||
const emits = defineEmits<DialogContentEmits>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, side, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogPortal>
|
||||
<DialogOverlay
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
/>
|
||||
<DialogContent :class="cn(sheetVariants({ side }), props.class)" v-bind="{ ...forwarded, ...$attrs }">
|
||||
<slot />
|
||||
|
||||
<DialogClose
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
|
||||
>
|
||||
<X class="size-4 text-muted-foreground" />
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</template>
|
||||
19
frontend/components/ui/sheet/SheetDescription.vue
Normal file
19
frontend/components/ui/sheet/SheetDescription.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogDescription, type DialogDescriptionProps } from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes["class"] }>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogDescription :class="cn('text-sm text-muted-foreground', props.class)" v-bind="delegatedProps">
|
||||
<slot />
|
||||
</DialogDescription>
|
||||
</template>
|
||||
12
frontend/components/ui/sheet/SheetFooter.vue
Normal file
12
frontend/components/ui/sheet/SheetFooter.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{ class?: HTMLAttributes["class"] }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
12
frontend/components/ui/sheet/SheetHeader.vue
Normal file
12
frontend/components/ui/sheet/SheetHeader.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{ class?: HTMLAttributes["class"] }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
19
frontend/components/ui/sheet/SheetTitle.vue
Normal file
19
frontend/components/ui/sheet/SheetTitle.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogTitle, type DialogTitleProps } from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes["class"] }>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogTitle :class="cn('text-lg font-semibold text-foreground', props.class)" v-bind="delegatedProps">
|
||||
<slot />
|
||||
</DialogTitle>
|
||||
</template>
|
||||
11
frontend/components/ui/sheet/SheetTrigger.vue
Normal file
11
frontend/components/ui/sheet/SheetTrigger.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogTrigger, type DialogTriggerProps } from "radix-vue";
|
||||
|
||||
const props = defineProps<DialogTriggerProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogTrigger v-bind="props">
|
||||
<slot />
|
||||
</DialogTrigger>
|
||||
</template>
|
||||
31
frontend/components/ui/sheet/index.ts
Normal file
31
frontend/components/ui/sheet/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
export { default as Sheet } from "./Sheet.vue";
|
||||
export { default as SheetClose } from "./SheetClose.vue";
|
||||
export { default as SheetContent } from "./SheetContent.vue";
|
||||
export { default as SheetDescription } from "./SheetDescription.vue";
|
||||
export { default as SheetFooter } from "./SheetFooter.vue";
|
||||
export { default as SheetHeader } from "./SheetHeader.vue";
|
||||
export { default as SheetTitle } from "./SheetTitle.vue";
|
||||
export { default as SheetTrigger } from "./SheetTrigger.vue";
|
||||
|
||||
export const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||
bottom:
|
||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||
right:
|
||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type SheetVariants = VariantProps<typeof sheetVariants>;
|
||||
90
frontend/components/ui/sidebar/Sidebar.vue
Normal file
90
frontend/components/ui/sidebar/Sidebar.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
import { SIDEBAR_WIDTH_MOBILE, useSidebar } from "./utils";
|
||||
import type { SidebarProps } from ".";
|
||||
import { Sheet, SheetContent } from "@/components/ui/sheet";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<SidebarProps>(), {
|
||||
side: "left",
|
||||
variant: "sidebar",
|
||||
collapsible: "offcanvas",
|
||||
});
|
||||
|
||||
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="collapsible === 'none'"
|
||||
:class="cn('flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground', props.class)"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<Sheet v-else-if="isMobile" :open="openMobile" v-bind="$attrs" @update:open="setOpenMobile">
|
||||
<SheetContent
|
||||
data-sidebar="sidebar"
|
||||
data-mobile="true"
|
||||
:side="side"
|
||||
class="bg-sidebar text-sidebar-foreground w-[--sidebar-width] p-0 [&>button]:hidden"
|
||||
:style="{
|
||||
'--sidebar-width': SIDEBAR_WIDTH_MOBILE,
|
||||
}"
|
||||
>
|
||||
<div class="flex size-full flex-col">
|
||||
<slot />
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="group peer hidden md:block"
|
||||
:data-state="state"
|
||||
:data-collapsible="state === 'collapsed' ? collapsible : ''"
|
||||
:data-variant="variant"
|
||||
:data-side="side"
|
||||
>
|
||||
<!-- This is what handles the sidebar gap on desktop -->
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] 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)_+_theme(spacing.4))]'
|
||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]'
|
||||
)
|
||||
"
|
||||
/>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] 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)_+_theme(spacing.4)_+2px)]'
|
||||
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<div
|
||||
data-sidebar="sidebar"
|
||||
class="text-sidebar-foreground bg-sidebar group-data-[variant=floating]:border-sidebar-border flex size-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
19
frontend/components/ui/sidebar/SidebarContent.vue
Normal file
19
frontend/components/ui/sidebar/SidebarContent.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-sidebar="content"
|
||||
:class="
|
||||
cn('flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', props.class)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
frontend/components/ui/sidebar/SidebarFooter.vue
Normal file
14
frontend/components/ui/sidebar/SidebarFooter.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-sidebar="footer" :class="cn('flex flex-col gap-2 p-2', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
14
frontend/components/ui/sidebar/SidebarGroup.vue
Normal file
14
frontend/components/ui/sidebar/SidebarGroup.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-sidebar="group" :class="cn('relative flex w-full min-w-0 flex-col p-2', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
31
frontend/components/ui/sidebar/SidebarGroupAction.vue
Normal file
31
frontend/components/ui/sidebar/SidebarGroupAction.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import type { PrimitiveProps } from "radix-vue";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Primitive } from "radix-vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<
|
||||
PrimitiveProps & {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
data-sidebar="group-action"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="
|
||||
cn(
|
||||
'absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
// Increases the hit area of the button on mobile.
|
||||
'after:absolute after:-inset-2 after:md:hidden',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
14
frontend/components/ui/sidebar/SidebarGroupContent.vue
Normal file
14
frontend/components/ui/sidebar/SidebarGroupContent.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-sidebar="group-content" :class="cn('w-full text-sm', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
29
frontend/components/ui/sidebar/SidebarGroupLabel.vue
Normal file
29
frontend/components/ui/sidebar/SidebarGroupLabel.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import type { PrimitiveProps } from "radix-vue";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Primitive } from "radix-vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<
|
||||
PrimitiveProps & {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
data-sidebar="group-label"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="
|
||||
cn(
|
||||
'duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
14
frontend/components/ui/sidebar/SidebarHeader.vue
Normal file
14
frontend/components/ui/sidebar/SidebarHeader.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-sidebar="header" :class="cn('flex flex-col gap-2 p-2', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
20
frontend/components/ui/sidebar/SidebarInput.vue
Normal file
20
frontend/components/ui/sidebar/SidebarInput.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Input
|
||||
data-sidebar="input"
|
||||
:class="
|
||||
cn('h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring', props.class)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</Input>
|
||||
</template>
|
||||
22
frontend/components/ui/sidebar/SidebarInset.vue
Normal file
22
frontend/components/ui/sidebar/SidebarInset.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main
|
||||
:class="
|
||||
cn(
|
||||
'relative flex min-h-svh flex-1 flex-col bg-background',
|
||||
'peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</main>
|
||||
</template>
|
||||
14
frontend/components/ui/sidebar/SidebarMenu.vue
Normal file
14
frontend/components/ui/sidebar/SidebarMenu.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul data-sidebar="menu" :class="cn('flex w-full min-w-0 flex-col gap-1', props.class)">
|
||||
<slot />
|
||||
</ul>
|
||||
</template>
|
||||
41
frontend/components/ui/sidebar/SidebarMenuAction.vue
Normal file
41
frontend/components/ui/sidebar/SidebarMenuAction.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Primitive, type PrimitiveProps } from "radix-vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
PrimitiveProps & {
|
||||
showOnHover?: boolean;
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
>(),
|
||||
{
|
||||
as: "button",
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
data-sidebar="menu-action"
|
||||
:class="
|
||||
cn(
|
||||
'absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
// Increases the hit area of the button on mobile.
|
||||
'after:absolute after:-inset-2 after:md: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 &&
|
||||
'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
27
frontend/components/ui/sidebar/SidebarMenuBadge.vue
Normal file
27
frontend/components/ui/sidebar/SidebarMenuBadge.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-sidebar="menu-badge"
|
||||
:class="
|
||||
cn(
|
||||
'absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-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',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
54
frontend/components/ui/sidebar/SidebarMenuButton.vue
Normal file
54
frontend/components/ui/sidebar/SidebarMenuButton.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
import { type Component, computed } from "vue";
|
||||
import SidebarMenuButtonChild, { type SidebarMenuButtonProps } from "./SidebarMenuButtonChild.vue";
|
||||
import { useSidebar } from "./utils";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
SidebarMenuButtonProps & {
|
||||
tooltip?: string | Component;
|
||||
hotkey?: string;
|
||||
}
|
||||
>(),
|
||||
{
|
||||
as: "button",
|
||||
variant: "default",
|
||||
size: "default",
|
||||
}
|
||||
);
|
||||
|
||||
const { isMobile, state } = useSidebar();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { tooltip, hotkey, ...delegated } = props;
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SidebarMenuButtonChild v-if="!tooltip" v-bind="{ ...delegatedProps, ...$attrs }">
|
||||
<slot />
|
||||
</SidebarMenuButtonChild>
|
||||
|
||||
<Tooltip v-else>
|
||||
<TooltipTrigger as-child>
|
||||
<SidebarMenuButtonChild v-bind="{ ...delegatedProps, ...$attrs }" :size="state === 'collapsed' ? 'default' : 'lg'" :class="state === 'collapsed' ? '' : 'text-xl'">
|
||||
<slot />
|
||||
</SidebarMenuButtonChild>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" align="center" :hidden="state !== 'collapsed' || isMobile">
|
||||
<template v-if="typeof tooltip === 'string'">
|
||||
{{ tooltip }}
|
||||
</template>
|
||||
<component :is="tooltip" v-else />
|
||||
</TooltipContent>
|
||||
<TooltipContent v-if="hotkey" :hidden="isMobile">
|
||||
{{ hotkey }}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</template>
|
||||
33
frontend/components/ui/sidebar/SidebarMenuButtonChild.vue
Normal file
33
frontend/components/ui/sidebar/SidebarMenuButtonChild.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Primitive, type PrimitiveProps } from "radix-vue";
|
||||
import { type SidebarMenuButtonVariants, sidebarMenuButtonVariants } from ".";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface SidebarMenuButtonProps extends PrimitiveProps {
|
||||
variant?: SidebarMenuButtonVariants["variant"];
|
||||
size?: SidebarMenuButtonVariants["size"];
|
||||
isActive?: boolean;
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<SidebarMenuButtonProps>(), {
|
||||
as: "button",
|
||||
variant: "default",
|
||||
size: "default",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
data-sidebar="menu-button"
|
||||
:data-size="size"
|
||||
:data-active="isActive"
|
||||
:class="cn(sidebarMenuButtonVariants({ variant, size }), props.class)"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
14
frontend/components/ui/sidebar/SidebarMenuItem.vue
Normal file
14
frontend/components/ui/sidebar/SidebarMenuItem.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li data-sidebar="menu-item" :class="cn('group/menu-item relative', props.class)">
|
||||
<slot />
|
||||
</li>
|
||||
</template>
|
||||
18
frontend/components/ui/sidebar/SidebarMenuLink.vue
Normal file
18
frontend/components/ui/sidebar/SidebarMenuLink.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useSidebar } from "./utils";
|
||||
import SidebarMenuButton from "./SidebarMenuButton.vue";
|
||||
|
||||
const { setOpenMobile } = useSidebar();
|
||||
|
||||
const props = defineProps<{
|
||||
href: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SidebarMenuButton as-child>
|
||||
<NuxtLink :to="props.href" @click.prevent="setOpenMobile(false)">
|
||||
<slot />
|
||||
</NuxtLink>
|
||||
</SidebarMenuButton>
|
||||
</template>
|
||||
26
frontend/components/ui/sidebar/SidebarMenuSkeleton.vue
Normal file
26
frontend/components/ui/sidebar/SidebarMenuSkeleton.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
showIcon?: boolean;
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
|
||||
const width = computed(() => {
|
||||
return `${Math.floor(Math.random() * 40) + 50}%`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-sidebar="menu-skeleton" :class="cn('rounded-md h-8 flex gap-2 px-2 items-center', props.class)">
|
||||
<Skeleton v-if="showIcon" class="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />
|
||||
|
||||
<Skeleton
|
||||
class="h-4 max-w-[--skeleton-width] flex-1"
|
||||
data-sidebar="menu-skeleton-text"
|
||||
:style="{ '--skeleton-width': width }"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
23
frontend/components/ui/sidebar/SidebarMenuSub.vue
Normal file
23
frontend/components/ui/sidebar/SidebarMenuSub.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul
|
||||
data-sidebar="menu-badge"
|
||||
:class="
|
||||
cn(
|
||||
'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</ul>
|
||||
</template>
|
||||
42
frontend/components/ui/sidebar/SidebarMenuSubButton.vue
Normal file
42
frontend/components/ui/sidebar/SidebarMenuSubButton.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import type { PrimitiveProps } from "radix-vue";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Primitive } from "radix-vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
PrimitiveProps & {
|
||||
size?: "sm" | "md";
|
||||
isActive?: boolean;
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
>(),
|
||||
{
|
||||
as: "a",
|
||||
size: "md",
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
data-sidebar="menu-sub-button"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:data-size="size"
|
||||
:data-active="isActive"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring 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 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground',
|
||||
'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',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
7
frontend/components/ui/sidebar/SidebarMenuSubItem.vue
Normal file
7
frontend/components/ui/sidebar/SidebarMenuSubItem.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<li>
|
||||
<slot />
|
||||
</li>
|
||||
</template>
|
||||
90
frontend/components/ui/sidebar/SidebarProvider.vue
Normal file
90
frontend/components/ui/sidebar/SidebarProvider.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
import { useEventListener, useMediaQuery, useVModel } from "@vueuse/core";
|
||||
import { TooltipProvider } from "radix-vue";
|
||||
import { computed, type HTMLAttributes, type Ref, ref } from "vue";
|
||||
import {
|
||||
provideSidebarContext,
|
||||
SIDEBAR_COOKIE_MAX_AGE,
|
||||
SIDEBAR_COOKIE_NAME,
|
||||
SIDEBAR_KEYBOARD_SHORTCUT,
|
||||
SIDEBAR_WIDTH,
|
||||
SIDEBAR_WIDTH_ICON,
|
||||
} from "./utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
defaultOpen?: boolean;
|
||||
open?: boolean;
|
||||
class?: HTMLAttributes["class"];
|
||||
}>(),
|
||||
{
|
||||
defaultOpen: true,
|
||||
open: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
const emits = defineEmits<{
|
||||
"update:open": [open: boolean];
|
||||
}>();
|
||||
|
||||
const isMobile = useMediaQuery("(max-width: 768px)");
|
||||
const openMobile = ref(false);
|
||||
|
||||
const open = useVModel(props, "open", emits, {
|
||||
defaultValue: props.defaultOpen ?? false,
|
||||
passive: (props.open === undefined) as false,
|
||||
}) as Ref<boolean>;
|
||||
|
||||
function setOpen(value: boolean) {
|
||||
open.value = value; // emits('update:open', value)
|
||||
|
||||
// This sets the cookie to keep the sidebar state.
|
||||
document.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
||||
}
|
||||
|
||||
function setOpenMobile(value: boolean) {
|
||||
openMobile.value = value;
|
||||
}
|
||||
|
||||
// Helper to toggle the sidebar.
|
||||
function toggleSidebar() {
|
||||
return isMobile.value ? setOpenMobile(!openMobile.value) : setOpen(!open.value);
|
||||
}
|
||||
|
||||
useEventListener("keydown", (event: KeyboardEvent) => {
|
||||
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
|
||||
event.preventDefault();
|
||||
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 = computed(() => (open.value ? "expanded" : "collapsed"));
|
||||
|
||||
provideSidebarContext({
|
||||
state,
|
||||
open,
|
||||
setOpen,
|
||||
isMobile,
|
||||
openMobile,
|
||||
setOpenMobile,
|
||||
toggleSidebar,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipProvider :delay-duration="0">
|
||||
<div
|
||||
:style="{
|
||||
'--sidebar-width': SIDEBAR_WIDTH,
|
||||
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
|
||||
}"
|
||||
:class="cn('group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar', props.class)"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
34
frontend/components/ui/sidebar/SidebarRail.vue
Normal file
34
frontend/components/ui/sidebar/SidebarRail.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { useSidebar } from "./utils";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
|
||||
const { toggleSidebar } = useSidebar();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
data-sidebar="rail"
|
||||
aria-label="Toggle Sidebar"
|
||||
:tabindex="-1"
|
||||
title="Toggle Sidebar"
|
||||
:class="
|
||||
cn(
|
||||
'absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex',
|
||||
'[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize',
|
||||
'[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
|
||||
'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar',
|
||||
'[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
|
||||
'[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
@click="toggleSidebar"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
15
frontend/components/ui/sidebar/SidebarSeparator.vue
Normal file
15
frontend/components/ui/sidebar/SidebarSeparator.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Separator data-sidebar="separator" :class="cn('mx-2 w-auto bg-sidebar-border', props.class)">
|
||||
<slot />
|
||||
</Separator>
|
||||
</template>
|
||||
21
frontend/components/ui/sidebar/SidebarTrigger.vue
Normal file
21
frontend/components/ui/sidebar/SidebarTrigger.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { useSidebar } from "./utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import MdiMenu from "~icons/mdi/menu";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
variant?: "ghost" | "default";
|
||||
}>();
|
||||
|
||||
const { toggleSidebar } = useSidebar();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button data-sidebar="trigger" :variant="props.variant ?? 'ghost'" size="icon" :class="cn('size-9 [&_svg]:size-6', props.class)" @click="toggleSidebar">
|
||||
<MdiMenu class="text-primary-foreground" />
|
||||
<span class="sr-only">Toggle Sidebar</span>
|
||||
</Button>
|
||||
</template>
|
||||
60
frontend/components/ui/sidebar/index.ts
Normal file
60
frontend/components/ui/sidebar/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
export interface SidebarProps {
|
||||
side?: "left" | "right";
|
||||
variant?: "sidebar" | "floating" | "inset";
|
||||
collapsible?: "offcanvas" | "icon" | "none";
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
export { default as Sidebar } from "./Sidebar.vue";
|
||||
export { default as SidebarContent } from "./SidebarContent.vue";
|
||||
export { default as SidebarFooter } from "./SidebarFooter.vue";
|
||||
export { default as SidebarGroup } from "./SidebarGroup.vue";
|
||||
export { default as SidebarGroupAction } from "./SidebarGroupAction.vue";
|
||||
export { default as SidebarGroupContent } from "./SidebarGroupContent.vue";
|
||||
export { default as SidebarGroupLabel } from "./SidebarGroupLabel.vue";
|
||||
export { default as SidebarHeader } from "./SidebarHeader.vue";
|
||||
export { default as SidebarInput } from "./SidebarInput.vue";
|
||||
export { default as SidebarInset } from "./SidebarInset.vue";
|
||||
export { default as SidebarMenu } from "./SidebarMenu.vue";
|
||||
export { default as SidebarMenuAction } from "./SidebarMenuAction.vue";
|
||||
export { default as SidebarMenuBadge } from "./SidebarMenuBadge.vue";
|
||||
export { default as SidebarMenuButton } from "./SidebarMenuButton.vue";
|
||||
export { default as SidebarMenuLink } from "./SidebarMenuLink.vue";
|
||||
export { default as SidebarMenuItem } from "./SidebarMenuItem.vue";
|
||||
export { default as SidebarMenuSkeleton } from "./SidebarMenuSkeleton.vue";
|
||||
export { default as SidebarMenuSub } from "./SidebarMenuSub.vue";
|
||||
export { default as SidebarMenuSubButton } from "./SidebarMenuSubButton.vue";
|
||||
export { default as SidebarMenuSubItem } from "./SidebarMenuSubItem.vue";
|
||||
export { default as SidebarProvider } from "./SidebarProvider.vue";
|
||||
export { default as SidebarRail } from "./SidebarRail.vue";
|
||||
export { default as SidebarSeparator } from "./SidebarSeparator.vue";
|
||||
export { default as SidebarTrigger } from "./SidebarTrigger.vue";
|
||||
|
||||
export { useSidebar } from "./utils";
|
||||
|
||||
export const sidebarMenuButtonVariants = cva(
|
||||
"peer/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none transition-[width,height,padding] focus-visible:ring-2 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]:font-medium group-data-[collapsible=icon]:!size-10 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-6 [&>svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||
outline:
|
||||
"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] 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",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type SidebarMenuButtonVariants = VariantProps<typeof sidebarMenuButtonVariants>;
|
||||
19
frontend/components/ui/sidebar/utils.ts
Normal file
19
frontend/components/ui/sidebar/utils.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { ComputedRef, Ref } from "vue";
|
||||
import { createContext } from "radix-vue";
|
||||
|
||||
export const SIDEBAR_COOKIE_NAME = "sidebar:state";
|
||||
export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
||||
export const SIDEBAR_WIDTH = "16rem";
|
||||
export const SIDEBAR_WIDTH_MOBILE = "18rem";
|
||||
export const SIDEBAR_WIDTH_ICON = "3.5rem";
|
||||
export const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
||||
|
||||
export const [useSidebar, provideSidebarContext] = createContext<{
|
||||
state: ComputedRef<"expanded" | "collapsed">;
|
||||
open: Ref<boolean>;
|
||||
setOpen: (value: boolean) => void;
|
||||
isMobile: Ref<boolean>;
|
||||
openMobile: Ref<boolean>;
|
||||
setOpenMobile: (value: boolean) => void;
|
||||
toggleSidebar: () => void;
|
||||
}>("Sidebar");
|
||||
14
frontend/components/ui/skeleton/Skeleton.vue
Normal file
14
frontend/components/ui/skeleton/Skeleton.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface SkeletonProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = defineProps<SkeletonProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('animate-pulse rounded-md bg-muted', props.class)" />
|
||||
</template>
|
||||
1
frontend/components/ui/skeleton/index.ts
Normal file
1
frontend/components/ui/skeleton/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Skeleton } from "./Skeleton.vue";
|
||||
14
frontend/components/ui/tooltip/Tooltip.vue
Normal file
14
frontend/components/ui/tooltip/Tooltip.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipRoot, type TooltipRootEmits, type TooltipRootProps, useForwardPropsEmits } from "radix-vue";
|
||||
|
||||
const props = defineProps<TooltipRootProps>();
|
||||
const emits = defineEmits<TooltipRootEmits>();
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</TooltipRoot>
|
||||
</template>
|
||||
45
frontend/components/ui/tooltip/TooltipContent.vue
Normal file
45
frontend/components/ui/tooltip/TooltipContent.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
TooltipContent,
|
||||
type TooltipContentEmits,
|
||||
type TooltipContentProps,
|
||||
TooltipPortal,
|
||||
useForwardPropsEmits,
|
||||
} from "radix-vue";
|
||||
import { computed, type HTMLAttributes } from "vue";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<TooltipContentProps & { class?: HTMLAttributes["class"] }>(), {
|
||||
sideOffset: 4,
|
||||
});
|
||||
|
||||
const emits = defineEmits<TooltipContentEmits>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipPortal>
|
||||
<TooltipContent
|
||||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md 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',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</template>
|
||||
11
frontend/components/ui/tooltip/TooltipProvider.vue
Normal file
11
frontend/components/ui/tooltip/TooltipProvider.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipProvider, type TooltipProviderProps } from "radix-vue";
|
||||
|
||||
const props = defineProps<TooltipProviderProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipProvider v-bind="props">
|
||||
<slot />
|
||||
</TooltipProvider>
|
||||
</template>
|
||||
11
frontend/components/ui/tooltip/TooltipTrigger.vue
Normal file
11
frontend/components/ui/tooltip/TooltipTrigger.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipTrigger, type TooltipTriggerProps } from "radix-vue";
|
||||
|
||||
const props = defineProps<TooltipTriggerProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipTrigger v-bind="props">
|
||||
<slot />
|
||||
</TooltipTrigger>
|
||||
</template>
|
||||
4
frontend/components/ui/tooltip/index.ts
Normal file
4
frontend/components/ui/tooltip/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as Tooltip } from "./Tooltip.vue";
|
||||
export { default as TooltipContent } from "./TooltipContent.vue";
|
||||
export { default as TooltipProvider } from "./TooltipProvider.vue";
|
||||
export { default as TooltipTrigger } from "./TooltipTrigger.vue";
|
||||
@@ -17,6 +17,9 @@ export function useTheme(): UseTheme {
|
||||
|
||||
if (htmlEl) {
|
||||
htmlEl.value?.setAttribute("data-theme", newTheme);
|
||||
// FIXME: this is a hack to remove the theme class from the html element
|
||||
htmlEl.value?.classList.remove(...themes);
|
||||
htmlEl.value?.classList.add("theme-" + newTheme);
|
||||
}
|
||||
|
||||
themeRef.value = newTheme;
|
||||
@@ -62,3 +65,39 @@ export function useIsDark() {
|
||||
return darkthemes.includes(theme.theme.value);
|
||||
});
|
||||
}
|
||||
|
||||
export const themes = [
|
||||
"dark",
|
||||
"theme-aqua",
|
||||
"theme-black",
|
||||
"theme-bumblebee",
|
||||
"theme-cmyk",
|
||||
"theme-corporate",
|
||||
"theme-cupcake",
|
||||
"theme-cyberpunk",
|
||||
"theme-dark",
|
||||
"theme-dracula",
|
||||
"theme-emerald",
|
||||
"theme-fantasy",
|
||||
"theme-forest",
|
||||
"theme-garden",
|
||||
"theme-halloween",
|
||||
"theme-light",
|
||||
"theme-lofi",
|
||||
"theme-luxury",
|
||||
"theme-pastel",
|
||||
"theme-retro",
|
||||
"theme-synthwave",
|
||||
"theme-valentine",
|
||||
"theme-wireframe",
|
||||
"theme-autumn",
|
||||
"theme-business",
|
||||
"theme-acid",
|
||||
"theme-lemonade",
|
||||
"theme-night",
|
||||
"theme-coffee",
|
||||
"theme-winter",
|
||||
"theme-dim",
|
||||
"theme-nord",
|
||||
"theme-sunset",
|
||||
];
|
||||
|
||||
@@ -12,104 +12,112 @@
|
||||
<LocationCreateModal v-model="modals.location" />
|
||||
<QuickMenuModal v-model="modals.quickMenu" :actions="quickMenuActions" />
|
||||
<AppToast />
|
||||
<div class="drawer drawer-mobile">
|
||||
<input id="my-drawer-2" v-model="drawerToggle" type="checkbox" class="drawer-toggle" />
|
||||
<div class="drawer-content justify-center bg-base-300 pt-20 lg:pt-0">
|
||||
<AppHeaderDecor v-if="preferences.displayHeaderDecor" class="-mt-10 hidden lg:block" />
|
||||
<!-- Button -->
|
||||
<div class="navbar drawer-button fixed top-0 z-[99] bg-primary shadow-md lg:hidden">
|
||||
<label for="my-drawer-2" class="btn btn-square btn-ghost drawer-button text-base-100 lg:hidden">
|
||||
<MdiMenu class="size-6" />
|
||||
</label>
|
||||
<NuxtLink to="/home">
|
||||
<h2 class="flex text-3xl font-bold tracking-tight text-base-100">
|
||||
HomeB
|
||||
<AppLogo class="-mb-3 w-8" />
|
||||
x
|
||||
</h2>
|
||||
<SidebarProvider>
|
||||
<Sidebar collapsible="icon">
|
||||
<SidebarHeader class="items-center bg-base-200">
|
||||
<SidebarGroupLabel class="text-base">{{ $t("global.welcome", { username: username }) }}</SidebarGroupLabel>
|
||||
<NuxtLink class="avatar placeholder group-data-[collapsible=icon]:hidden" to="/home">
|
||||
<div class="w-24 rounded-full bg-base-300 p-4 text-neutral-content">
|
||||
<AppLogo />
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<SidebarMenuButton
|
||||
class="flex justify-center bg-primary text-primary-foreground shadow hover:bg-primary/90 group-data-[collapsible=icon]:justify-start"
|
||||
:tooltip="$t('global.create')"
|
||||
hotkey="Shortcut: Ctrl+`"
|
||||
>
|
||||
<MdiPlus />
|
||||
<span>
|
||||
{{ $t("global.create") }}
|
||||
</span>
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent class="min-w-[var(--radix-dropdown-menu-trigger-width)]">
|
||||
<DropdownMenuItem
|
||||
v-for="btn in dropdown"
|
||||
:key="btn.id"
|
||||
class="group cursor-pointer text-lg"
|
||||
@click="btn.action"
|
||||
>
|
||||
{{ btn.name.value }}
|
||||
<kbd v-if="btn.shortcut" class="kbd kbd-sm ml-auto hidden text-primary group-hover:inline">{{
|
||||
btn.shortcut.replaceAll("Shift+", "⇧")
|
||||
}}</kbd>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</SidebarHeader>
|
||||
|
||||
<slot></slot>
|
||||
<footer v-if="status" class="bottom-0 w-full bg-base-300 pb-4 text-center text-secondary-content">
|
||||
<p class="text-center text-sm">
|
||||
<a href="https://github.com/sysadminsmedia/homebox/releases/tag/{{ status.build.version }}" target="_blank">
|
||||
{{ $t("global.version", { version: status.build.version }) }} ~
|
||||
{{ $t("global.build", { build: status.build.commit }) }}</a
|
||||
>
|
||||
~
|
||||
<a href="https://homebox.software/en/api.html" target="_blank">API</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
<SidebarContent class="bg-base-200">
|
||||
<SidebarGroup>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem v-for="n in nav" :key="n.id">
|
||||
<SidebarMenuLink
|
||||
:href="n.to"
|
||||
:class="{
|
||||
'bg-secondary text-secondary-foreground': n.active?.value,
|
||||
'text-nowrap': typeof locale === 'string' && locale.startsWith('zh-'),
|
||||
'hover:bg-base-300': !n.active?.value,
|
||||
}"
|
||||
:tooltip="n.name.value"
|
||||
>
|
||||
<component :is="n.icon" />
|
||||
<span>{{ n.name.value }}</span>
|
||||
</SidebarMenuLink>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu></SidebarGroup
|
||||
>
|
||||
</SidebarContent>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="drawer-side shadow-lg">
|
||||
<label for="my-drawer-2" class="drawer-overlay"></label>
|
||||
|
||||
<!-- Top Section -->
|
||||
<div class="flex min-w-40 max-w-min flex-col bg-base-200 p-5 md:py-10">
|
||||
<div class="space-y-8">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<p>{{ $t("global.welcome", { username: username }) }}</p>
|
||||
<NuxtLink class="avatar placeholder" to="/home">
|
||||
<div class="w-24 rounded-full bg-base-300 p-4 text-neutral-content">
|
||||
<AppLogo />
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="flex flex-col bg-base-200">
|
||||
<div class="mb-6">
|
||||
<div class="dropdown tooltip visible w-full" data-tip="Shortcut: Ctrl+`">
|
||||
<label tabindex="0" class="text-no-transform btn btn-primary btn-block text-lg">
|
||||
<span>
|
||||
<MdiPlus class="-ml-1 mr-1" />
|
||||
</span>
|
||||
{{ $t("global.create") }}
|
||||
</label>
|
||||
<ul tabindex="0" class="dropdown-content menu rounded-box w-full bg-base-100 p-2 shadow">
|
||||
<li v-for="btn in dropdown" :key="btn.id">
|
||||
<button class="group" @click="btn.action">
|
||||
{{ btn.name.value }}
|
||||
|
||||
<kbd
|
||||
v-if="btn.shortcut"
|
||||
class="kbd kbd-sm ml-auto hidden text-neutral-400 group-hover:inline"
|
||||
>{{ btn.shortcut.replaceAll("Shift+", "⇧") }}</kbd
|
||||
>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="menu mx-auto flex flex-col gap-2">
|
||||
<li v-for="n in nav" :key="n.id" class="text-xl" @click="unfocus">
|
||||
<NuxtLink
|
||||
v-if="n.to"
|
||||
class="rounded-btn"
|
||||
:to="n.to"
|
||||
:class="{
|
||||
'bg-secondary text-secondary-content': n.active?.value,
|
||||
'text-nowrap': typeof locale === 'string' && locale.startsWith('zh-'),
|
||||
}"
|
||||
>
|
||||
<component :is="n.icon" class="mr-4 size-6" />
|
||||
{{ n.name.value }}
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom -->
|
||||
<div class="mx-2 mt-auto flex flex-col">
|
||||
<button class="rounded-btn p-3 transition-colors hover:bg-base-300" @click="logout">
|
||||
<SidebarFooter class="bg-base-200">
|
||||
<SidebarMenuButton
|
||||
class="flex justify-center hover:bg-base-300 group-data-[collapsible=icon]:justify-start group-data-[collapsible=icon]:bg-destructive group-data-[collapsible=icon]:text-destructive-foreground group-data-[collapsible=icon]:shadow-sm group-data-[collapsible=icon]:hover:bg-destructive/90"
|
||||
:tooltip="$t('global.sign_out')"
|
||||
@click="logout"
|
||||
>
|
||||
<MdiLogout />
|
||||
<span>
|
||||
{{ $t("global.sign_out") }}
|
||||
</button>
|
||||
</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarFooter>
|
||||
|
||||
<SidebarRail />
|
||||
</Sidebar>
|
||||
<SidebarInset class="min-h-screen bg-base-300">
|
||||
<div class="justify-center pt-20 lg:pt-0">
|
||||
<AppHeaderDecor v-if="preferences.displayHeaderDecor" class="-mt-10 hidden lg:block" />
|
||||
<SidebarTrigger class="absolute left-2 top-2 hidden lg:flex" variant="default" />
|
||||
<div class="fixed top-0 z-20 flex h-16 w-full items-center gap-2 bg-primary p-2 shadow-md lg:hidden">
|
||||
<SidebarTrigger />
|
||||
<NuxtLink to="/home">
|
||||
<h2 class="flex text-3xl font-bold tracking-tight text-base-100">
|
||||
HomeB
|
||||
<AppLogo class="-mb-3 w-8" />
|
||||
x
|
||||
</h2>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
<footer v-if="status" class="bottom-0 w-full bg-base-300 pb-4 text-center text-secondary-content">
|
||||
<p class="text-center text-sm">
|
||||
<a
|
||||
href="https://github.com/sysadminsmedia/homebox/releases/tag/{{ status.build.version }}"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t("global.version", { version: status.build.version }) }} ~
|
||||
{{ $t("global.build", { build: status.build.commit }) }}</a
|
||||
>
|
||||
~
|
||||
<a href="https://homebox.software/en/api.html" target="_blank">API</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SidebarInset>
|
||||
</SidebarProvider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -125,8 +133,30 @@
|
||||
import MdiAccount from "~icons/mdi/account";
|
||||
import MdiCog from "~icons/mdi/cog";
|
||||
import MdiWrench from "~icons/mdi/wrench";
|
||||
import MdiMenu from "~icons/mdi/menu";
|
||||
import MdiPlus from "~icons/mdi/plus";
|
||||
import MdiLogout from "~icons/mdi/logout";
|
||||
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
SidebarInset,
|
||||
SidebarRail,
|
||||
SidebarTrigger,
|
||||
SidebarGroup,
|
||||
SidebarGroupLabel,
|
||||
SidebarMenu,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuLink,
|
||||
} from "@/components/ui/sidebar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const username = computed(() => authCtx.user?.name || "User");
|
||||
@@ -218,13 +248,6 @@
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const drawerToggle = ref();
|
||||
|
||||
function unfocus() {
|
||||
// unfocus current element
|
||||
drawerToggle.value = false;
|
||||
}
|
||||
|
||||
const nav = [
|
||||
{
|
||||
icon: MdiHome,
|
||||
|
||||
6
frontend/lib/utils.ts
Normal file
6
frontend/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -8,7 +8,14 @@ export default defineNuxtConfig({
|
||||
transpile: ["vue-i18n"],
|
||||
},
|
||||
|
||||
modules: ["@nuxtjs/tailwindcss", "@pinia/nuxt", "@vueuse/nuxt", "@vite-pwa/nuxt", "unplugin-icons/nuxt"],
|
||||
modules: [
|
||||
"@nuxtjs/tailwindcss",
|
||||
"@pinia/nuxt",
|
||||
"@vueuse/nuxt",
|
||||
"@vite-pwa/nuxt",
|
||||
"unplugin-icons/nuxt",
|
||||
"shadcn-nuxt",
|
||||
],
|
||||
|
||||
nitro: {
|
||||
devProxy: {
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
"dev": "nuxt dev",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare",
|
||||
"lint": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore .",
|
||||
"lint:fix": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore . --fix",
|
||||
"lint:ci": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore . --max-warnings 1",
|
||||
"lint": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore --ignore-pattern \"components/ui\" .",
|
||||
"lint:fix": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore --ignore-pattern \"components/ui\" . --fix",
|
||||
"lint:ci": "eslint --ext \".ts,.js,.vue\" --ignore-path ../.gitignore --ignore-pattern \"components/ui\" . --max-warnings 1",
|
||||
"typecheck": "pnpm vue-tsc --noEmit",
|
||||
"test:ci": "TEST_SHUTDOWN_API_SERVER=true vitest --run --config ./test/vitest.config.ts",
|
||||
"test:local": "TEST_SHUTDOWN_API_SERVER=false && vitest --run --config ./test/vitest.config.ts",
|
||||
@@ -33,6 +33,7 @@
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"nuxt": "3.12.4",
|
||||
"prettier": "^3.4.2",
|
||||
"shadcn-nuxt": "0.11.3",
|
||||
"typescript": "5.6.2",
|
||||
"unplugin-icons": "^0.18.5",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
@@ -42,6 +43,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@nuxtjs/tailwindcss": "^6.12.2",
|
||||
"@pinia/nuxt": "^0.5.5",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
@@ -49,20 +51,27 @@
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/lunr": "^2.3.7",
|
||||
"@vuepic/vue-datepicker": "^8.8.1",
|
||||
"@vueuse/core": "^12.5.0",
|
||||
"@vueuse/nuxt": "^10.11.1",
|
||||
"@vueuse/router": "^10.11.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"daisyui": "^2.52.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"dompurify": "^3.2.3",
|
||||
"h3": "^1.13.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"lucide-vue-next": "^0.474.0",
|
||||
"lunr": "^2.3.9",
|
||||
"markdown-it": "^14.1.0",
|
||||
"pinia": "^2.3.0",
|
||||
"postcss": "^8.4.49",
|
||||
"radix-vue": "^1.9.12",
|
||||
"semver": "^7.6.3",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vue": "3.4.8",
|
||||
"vue-router": "^4.5.0"
|
||||
}
|
||||
|
||||
@@ -478,10 +478,11 @@
|
||||
{{ $t("profile.display_header", { currentValue: preferences.displayHeaderDecor }) }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
<div class="rounded-box grid grid-cols-1 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
|
||||
<div class="homebox rounded-box grid grid-cols-1 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
|
||||
<div
|
||||
v-for="theme in themes"
|
||||
:key="theme.value"
|
||||
:class="'theme-' + theme.value"
|
||||
class="overflow-hidden rounded-lg border border-base-content/20 outline-2 outline-offset-2 outline-base-content hover:border-base-content/40"
|
||||
:data-theme="theme.value"
|
||||
:data-set-theme="theme.value"
|
||||
|
||||
5
frontend/pages/testing.vue
Normal file
5
frontend/pages/testing.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div>testing</div>
|
||||
</template>
|
||||
229
frontend/pnpm-lock.yaml
generated
229
frontend/pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ importers:
|
||||
'@headlessui/vue':
|
||||
specifier: ^1.7.23
|
||||
version: 1.7.23(vue@3.4.8(typescript@5.6.2))
|
||||
'@nuxtjs/color-mode':
|
||||
specifier: ^3.5.2
|
||||
version: 3.5.2(magicast@0.3.5)(rollup@4.29.1)
|
||||
'@nuxtjs/tailwindcss':
|
||||
specifier: ^6.12.2
|
||||
version: 6.12.2(magicast@0.3.5)(rollup@4.29.1)
|
||||
@@ -32,6 +35,9 @@ importers:
|
||||
'@vuepic/vue-datepicker':
|
||||
specifier: ^8.8.1
|
||||
version: 8.8.1(vue@3.4.8(typescript@5.6.2))
|
||||
'@vueuse/core':
|
||||
specifier: ^12.5.0
|
||||
version: 12.5.0(typescript@5.6.2)
|
||||
'@vueuse/nuxt':
|
||||
specifier: ^10.11.1
|
||||
version: 10.11.1(magicast@0.3.5)(nuxt@3.12.4(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(eslint@8.57.1)(ioredis@5.4.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.29.1)(terser@5.37.0)(typescript@5.6.2)(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0))(vue-tsc@2.1.6(typescript@5.6.2)))(rollup@4.29.1)(vue@3.4.8(typescript@5.6.2))
|
||||
@@ -41,6 +47,12 @@ importers:
|
||||
autoprefixer:
|
||||
specifier: ^10.4.20
|
||||
version: 10.4.20(postcss@8.4.49)
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
daisyui:
|
||||
specifier: ^2.52.0
|
||||
version: 2.52.0(autoprefixer@10.4.20(postcss@8.4.49))(postcss@8.4.49)
|
||||
@@ -56,6 +68,9 @@ importers:
|
||||
http-proxy:
|
||||
specifier: ^1.18.1
|
||||
version: 1.18.1
|
||||
lucide-vue-next:
|
||||
specifier: ^0.474.0
|
||||
version: 0.474.0(vue@3.4.8(typescript@5.6.2))
|
||||
lunr:
|
||||
specifier: ^2.3.9
|
||||
version: 2.3.9
|
||||
@@ -68,12 +83,21 @@ importers:
|
||||
postcss:
|
||||
specifier: ^8.4.49
|
||||
version: 8.4.49
|
||||
radix-vue:
|
||||
specifier: ^1.9.12
|
||||
version: 1.9.12(vue@3.4.8(typescript@5.6.2))
|
||||
semver:
|
||||
specifier: ^7.6.3
|
||||
version: 7.6.3
|
||||
tailwind-merge:
|
||||
specifier: ^2.6.0
|
||||
version: 2.6.0
|
||||
tailwindcss:
|
||||
specifier: ^3.4.17
|
||||
version: 3.4.17
|
||||
tailwindcss-animate:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(tailwindcss@3.4.17)
|
||||
vue:
|
||||
specifier: 3.4.8
|
||||
version: 3.4.8(typescript@5.6.2)
|
||||
@@ -135,6 +159,9 @@ importers:
|
||||
prettier:
|
||||
specifier: ^3.4.2
|
||||
version: 3.4.2
|
||||
shadcn-nuxt:
|
||||
specifier: 0.11.3
|
||||
version: 0.11.3(magicast@0.3.5)(rollup@4.29.1)
|
||||
typescript:
|
||||
specifier: 5.6.2
|
||||
version: 5.6.2
|
||||
@@ -1173,6 +1200,18 @@ packages:
|
||||
resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'}
|
||||
|
||||
'@floating-ui/core@1.6.9':
|
||||
resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==}
|
||||
|
||||
'@floating-ui/dom@1.6.13':
|
||||
resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==}
|
||||
|
||||
'@floating-ui/utils@0.2.9':
|
||||
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
|
||||
|
||||
'@floating-ui/vue@1.1.6':
|
||||
resolution: {integrity: sha512-XFlUzGHGv12zbgHNk5FN2mUB7ROul3oG2ENdTpWdE+qMFxyNxWSRmsoyhiEnpmabNm6WnUvR1OvJfUfN4ojC1A==}
|
||||
|
||||
'@formatjs/ecma402-abstract@2.3.2':
|
||||
resolution: {integrity: sha512-6sE5nyvDloULiyOMbOTJEEgWL32w+VHkZQs8S02Lnn8Y/O5aQhjOEXwWzvR7SsBE/exxlSpY2EsWZgqHbtLatg==}
|
||||
|
||||
@@ -1216,6 +1255,12 @@ packages:
|
||||
'@iconify/utils@2.2.1':
|
||||
resolution: {integrity: sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==}
|
||||
|
||||
'@internationalized/date@3.7.0':
|
||||
resolution: {integrity: sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==}
|
||||
|
||||
'@internationalized/number@3.6.0':
|
||||
resolution: {integrity: sha512-PtrRcJVy7nw++wn4W2OuePQQfTqDzfusSuY1QTtui4wa7r+rGVtR75pO8CyKvHvzyQYi3Q1uO5sY0AsB4e65Bw==}
|
||||
|
||||
'@intlify/bundle-utils@8.0.0':
|
||||
resolution: {integrity: sha512-1B++zykRnMwQ+20SpsZI1JCnV/YJt9Oq7AGlEurzkWJOFtFAVqaGc/oV36PBRYeiKnTbY9VYfjBimr2Vt42wLQ==}
|
||||
engines: {node: '>= 14.16'}
|
||||
@@ -1383,6 +1428,9 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.3.4
|
||||
|
||||
'@nuxtjs/color-mode@3.5.2':
|
||||
resolution: {integrity: sha512-cC6RfgZh3guHBMLLjrBB2Uti5eUoGM9KyauOaYS9ETmxNWBMTvpgjvSiSJp1OFljIXPIqVTJ3xtJpSNZiO3ZaA==}
|
||||
|
||||
'@nuxtjs/eslint-config-typescript@12.1.0':
|
||||
resolution: {integrity: sha512-l2fLouDYwdAvCZEEw7wGxOBj+i8TQcHFu3zMPTLqKuv1qu6WcZIr0uztkbaa8ND1uKZ9YPqKx6UlSOjM4Le69Q==}
|
||||
peerDependencies:
|
||||
@@ -1396,6 +1444,9 @@ packages:
|
||||
'@nuxtjs/tailwindcss@6.12.2':
|
||||
resolution: {integrity: sha512-qPJiFH67CkTj/2kBGBzqXihOD1rQXMsbVS4vdQvfBxOBLPfGhU1yw7AATdhPl2BBjO2krjJLuZj39t7dnDYOwg==}
|
||||
|
||||
'@oxc-parser/wasm@0.29.0':
|
||||
resolution: {integrity: sha512-Ks5yFtJHypJZUdSNLImwtfkDt0/8ll9CDPyfmldhudtKB/1o6F/WQGWA4Oo+bCskDIp2MPKc3HfHccN3ALhtSg==}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.0':
|
||||
resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -1723,6 +1774,9 @@ packages:
|
||||
'@surma/rollup-plugin-off-main-thread@2.2.3':
|
||||
resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
|
||||
|
||||
'@tailwindcss/aspect-ratio@0.4.2':
|
||||
resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==}
|
||||
peerDependencies:
|
||||
@@ -2041,9 +2095,15 @@ packages:
|
||||
'@vueuse/core@10.11.1':
|
||||
resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
|
||||
|
||||
'@vueuse/core@12.5.0':
|
||||
resolution: {integrity: sha512-GVyH1iYqNANwcahAx8JBm6awaNgvR/SwZ1fjr10b8l1HIgDp82ngNbfzJUgOgWEoxjL+URAggnlilAEXwCOZtg==}
|
||||
|
||||
'@vueuse/metadata@10.11.1':
|
||||
resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==}
|
||||
|
||||
'@vueuse/metadata@12.5.0':
|
||||
resolution: {integrity: sha512-Ui7Lo2a7AxrMAXRF+fAp9QsXuwTeeZ8fIB9wsLHqzq9MQk+2gMYE2IGJW48VMJ8ecvCB3z3GsGLKLbSasQ5Qlg==}
|
||||
|
||||
'@vueuse/nuxt@10.11.1':
|
||||
resolution: {integrity: sha512-UiaYSIwOkmUVn8Gl1AqtLWYR12flO+8sEu9X0Y1fNjSR7EWy9jMuiCvOGqwtoeTsqfHrivl0d5HfMzr11GFnMA==}
|
||||
peerDependencies:
|
||||
@@ -2057,6 +2117,9 @@ packages:
|
||||
'@vueuse/shared@10.11.1':
|
||||
resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
|
||||
|
||||
'@vueuse/shared@12.5.0':
|
||||
resolution: {integrity: sha512-vMpcL1lStUU6O+kdj6YdHDixh0odjPAUM15uJ9f7MY781jcYkIwFA4iv2EfoIPO6vBmvutI1HxxAwmf0cx5ISQ==}
|
||||
|
||||
abbrev@2.0.0:
|
||||
resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
@@ -2152,6 +2215,10 @@ packages:
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
aria-hidden@1.2.4:
|
||||
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
array-buffer-byte-length@1.0.2:
|
||||
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2381,6 +2448,9 @@ packages:
|
||||
citty@0.1.6:
|
||||
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||
|
||||
clean-regexp@1.0.0:
|
||||
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -2396,6 +2466,10 @@ packages:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cluster-key-slot@1.1.2:
|
||||
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3896,6 +3970,11 @@ packages:
|
||||
lru-cache@5.1.1:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
|
||||
|
||||
lucide-vue-next@0.474.0:
|
||||
resolution: {integrity: sha512-bQaSBjfJ33xiPQCxCf4JD3rcUgZFgWZzxSY8SScNa4Mcq2vWGlbvQx6icTL1UXRqsxzfoT13RXawePSmgg4iWw==}
|
||||
peerDependencies:
|
||||
vue: '>=3.0.1'
|
||||
|
||||
lunr@2.3.9:
|
||||
resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
|
||||
|
||||
@@ -4659,6 +4738,11 @@ packages:
|
||||
queue-tick@1.0.1:
|
||||
resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
|
||||
|
||||
radix-vue@1.9.12:
|
||||
resolution: {integrity: sha512-zkr66Jqxbej4+oR6O/pZRzyM/VZi66ndbyIBZQjJKAXa1lIoYReZJse6W1EEDZKXknD7rXhpS+jM9Sr23lIqfg==}
|
||||
peerDependencies:
|
||||
vue: '>= 3.2.0'
|
||||
|
||||
radix3@1.1.2:
|
||||
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
|
||||
|
||||
@@ -4903,6 +4987,9 @@ packages:
|
||||
setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
shadcn-nuxt@0.11.3:
|
||||
resolution: {integrity: sha512-Q0OxqTEbTmtbm/4wV9jd/7DTQYn65AGHkVJZjjHQdHGHTHCwhAbZ2jlCPnMuzJ8kgSsJyXFGNcImAphDCjjV5w==}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5146,6 +5233,14 @@ packages:
|
||||
peerDependencies:
|
||||
tailwindcss: 1 || 2 || 2.0.1-compat || 3
|
||||
|
||||
tailwind-merge@2.6.0:
|
||||
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
|
||||
|
||||
tailwindcss-animate@1.0.7:
|
||||
resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
|
||||
peerDependencies:
|
||||
tailwindcss: '>=3.0.0 || insiders'
|
||||
|
||||
tailwindcss@3.4.17:
|
||||
resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -5307,6 +5402,11 @@ packages:
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
typescript@5.7.3:
|
||||
resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
uc.micro@2.1.0:
|
||||
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
|
||||
|
||||
@@ -6869,6 +6969,26 @@ snapshots:
|
||||
|
||||
'@faker-js/faker@8.4.1': {}
|
||||
|
||||
'@floating-ui/core@1.6.9':
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.2.9
|
||||
|
||||
'@floating-ui/dom@1.6.13':
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.6.9
|
||||
'@floating-ui/utils': 0.2.9
|
||||
|
||||
'@floating-ui/utils@0.2.9': {}
|
||||
|
||||
'@floating-ui/vue@1.1.6(vue@3.4.8(typescript@5.6.2))':
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@floating-ui/utils': 0.2.9
|
||||
vue-demi: 0.14.10(vue@3.4.8(typescript@5.6.2))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@formatjs/ecma402-abstract@2.3.2':
|
||||
dependencies:
|
||||
'@formatjs/fast-memoize': 2.2.6
|
||||
@@ -6931,6 +7051,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@internationalized/date@3.7.0':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.15
|
||||
|
||||
'@internationalized/number@3.6.0':
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.15
|
||||
|
||||
'@intlify/bundle-utils@8.0.0(vue-i18n@9.14.2(vue@3.4.8(typescript@5.6.2)))':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 9.14.2
|
||||
@@ -7326,9 +7454,20 @@ snapshots:
|
||||
- vti
|
||||
- vue-tsc
|
||||
|
||||
'@nuxtjs/color-mode@3.5.2(magicast@0.3.5)(rollup@4.29.1)':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.15.0(magicast@0.3.5)(rollup@4.29.1)
|
||||
pathe: 1.1.2
|
||||
pkg-types: 1.3.0
|
||||
semver: 7.6.3
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
'@nuxtjs/eslint-config-typescript@12.1.0(eslint@8.57.1)(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@nuxtjs/eslint-config': 12.0.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||
'@nuxtjs/eslint-config': 12.0.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)
|
||||
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)
|
||||
'@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.6.2)
|
||||
eslint: 8.57.1
|
||||
@@ -7341,10 +7480,10 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@nuxtjs/eslint-config@12.0.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)':
|
||||
'@nuxtjs/eslint-config@12.0.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)':
|
||||
dependencies:
|
||||
eslint: 8.57.1
|
||||
eslint-config-standard: 17.1.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-config-standard: 17.1.0(eslint-plugin-import@2.31.0)(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)
|
||||
eslint-plugin-n: 15.7.0(eslint@8.57.1)
|
||||
eslint-plugin-node: 11.1.0(eslint@8.57.1)
|
||||
@@ -7379,6 +7518,8 @@ snapshots:
|
||||
- supports-color
|
||||
- ts-node
|
||||
|
||||
'@oxc-parser/wasm@0.29.0': {}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.0':
|
||||
optional: true
|
||||
|
||||
@@ -7681,6 +7822,10 @@ snapshots:
|
||||
magic-string: 0.25.9
|
||||
string.prototype.matchall: 4.0.12
|
||||
|
||||
'@swc/helpers@0.5.15':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@tailwindcss/aspect-ratio@0.4.2(tailwindcss@3.4.17)':
|
||||
dependencies:
|
||||
tailwindcss: 3.4.17
|
||||
@@ -8168,8 +8313,19 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/core@12.5.0(typescript@5.6.2)':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.20
|
||||
'@vueuse/metadata': 12.5.0
|
||||
'@vueuse/shared': 12.5.0(typescript@5.6.2)
|
||||
vue: 3.5.13(typescript@5.6.2)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
'@vueuse/metadata@10.11.1': {}
|
||||
|
||||
'@vueuse/metadata@12.5.0': {}
|
||||
|
||||
'@vueuse/nuxt@10.11.1(magicast@0.3.5)(nuxt@3.12.4(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(eslint@8.57.1)(ioredis@5.4.2)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.29.1)(terser@5.37.0)(typescript@5.6.2)(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0))(vue-tsc@2.1.6(typescript@5.6.2)))(rollup@4.29.1)(vue@3.4.8(typescript@5.6.2))':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.15.0(magicast@0.3.5)(rollup@4.29.1)
|
||||
@@ -8201,6 +8357,12 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/shared@12.5.0(typescript@5.6.2)':
|
||||
dependencies:
|
||||
vue: 3.5.13(typescript@5.6.2)
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
|
||||
abbrev@2.0.0: {}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
@@ -8293,6 +8455,10 @@ snapshots:
|
||||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
aria-hidden@1.2.4:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
array-buffer-byte-length@1.0.2:
|
||||
dependencies:
|
||||
call-bound: 1.0.3
|
||||
@@ -8580,6 +8746,10 @@ snapshots:
|
||||
dependencies:
|
||||
consola: 3.3.3
|
||||
|
||||
class-variance-authority@0.7.1:
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
|
||||
clean-regexp@1.0.0:
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
@@ -8598,6 +8768,8 @@ snapshots:
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
cluster-key-slot@1.1.2: {}
|
||||
|
||||
co@4.6.0: {}
|
||||
@@ -9160,7 +9332,7 @@ snapshots:
|
||||
dependencies:
|
||||
eslint: 8.57.1
|
||||
|
||||
eslint-config-standard@17.1.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-config-standard@17.1.0(eslint-plugin-import@2.31.0)(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1):
|
||||
dependencies:
|
||||
eslint: 8.57.1
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)
|
||||
@@ -9191,7 +9363,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1):
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
@@ -9225,7 +9397,7 @@ snapshots:
|
||||
doctrine: 2.1.0
|
||||
eslint: 8.57.1
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1)
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1)
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
@@ -10259,6 +10431,10 @@ snapshots:
|
||||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
lucide-vue-next@0.474.0(vue@3.4.8(typescript@5.6.2)):
|
||||
dependencies:
|
||||
vue: 3.4.8(typescript@5.6.2)
|
||||
|
||||
lunr@2.3.9: {}
|
||||
|
||||
magic-string-ast@0.6.3:
|
||||
@@ -10596,7 +10772,7 @@ snapshots:
|
||||
unenv: 1.10.0
|
||||
unimport: 3.14.5(rollup@4.29.1)
|
||||
unplugin: 1.16.0
|
||||
unplugin-vue-router: 0.10.9(rollup@4.29.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.2)))(vue@3.5.13(typescript@5.6.2))
|
||||
unplugin-vue-router: 0.10.9(rollup@4.29.1)(vue-router@4.5.0(vue@3.4.8(typescript@5.6.2)))(vue@3.5.13(typescript@5.6.2))
|
||||
unstorage: 1.14.4(db0@0.2.1)(ioredis@5.4.2)
|
||||
untyped: 1.5.2
|
||||
vue: 3.5.13(typescript@5.6.2)
|
||||
@@ -11144,6 +11320,23 @@ snapshots:
|
||||
|
||||
queue-tick@1.0.1: {}
|
||||
|
||||
radix-vue@1.9.12(vue@3.4.8(typescript@5.6.2)):
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.6.13
|
||||
'@floating-ui/vue': 1.1.6(vue@3.4.8(typescript@5.6.2))
|
||||
'@internationalized/date': 3.7.0
|
||||
'@internationalized/number': 3.6.0
|
||||
'@tanstack/vue-virtual': 3.11.2(vue@3.4.8(typescript@5.6.2))
|
||||
'@vueuse/core': 10.11.1(vue@3.4.8(typescript@5.6.2))
|
||||
'@vueuse/shared': 10.11.1(vue@3.4.8(typescript@5.6.2))
|
||||
aria-hidden: 1.2.4
|
||||
defu: 6.1.4
|
||||
fast-deep-equal: 3.1.3
|
||||
nanoid: 5.0.9
|
||||
vue: 3.4.8(typescript@5.6.2)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
||||
radix3@1.1.2: {}
|
||||
|
||||
randombytes@2.1.0:
|
||||
@@ -11442,6 +11635,16 @@ snapshots:
|
||||
|
||||
setprototypeof@1.2.0: {}
|
||||
|
||||
shadcn-nuxt@0.11.3(magicast@0.3.5)(rollup@4.29.1):
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.15.0(magicast@0.3.5)(rollup@4.29.1)
|
||||
'@oxc-parser/wasm': 0.29.0
|
||||
typescript: 5.7.3
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
shebang-command@2.0.0:
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
@@ -11715,6 +11918,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
tailwind-merge@2.6.0: {}
|
||||
|
||||
tailwindcss-animate@1.0.7(tailwindcss@3.4.17):
|
||||
dependencies:
|
||||
tailwindcss: 3.4.17
|
||||
|
||||
tailwindcss@3.4.17:
|
||||
dependencies:
|
||||
'@alloc/quick-lru': 5.2.0
|
||||
@@ -11902,6 +12111,8 @@ snapshots:
|
||||
|
||||
typescript@5.6.2: {}
|
||||
|
||||
typescript@5.7.3: {}
|
||||
|
||||
uc.micro@2.1.0: {}
|
||||
|
||||
ufo@1.5.4: {}
|
||||
@@ -11993,7 +12204,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
unplugin-vue-router@0.10.9(rollup@4.29.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.2)))(vue@3.5.13(typescript@5.6.2)):
|
||||
unplugin-vue-router@0.10.9(rollup@4.29.1)(vue-router@4.5.0(vue@3.4.8(typescript@5.6.2)))(vue@3.5.13(typescript@5.6.2)):
|
||||
dependencies:
|
||||
'@babel/types': 7.26.3
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.29.1)
|
||||
@@ -12010,7 +12221,7 @@ snapshots:
|
||||
unplugin: 2.0.0-beta.1
|
||||
yaml: 2.7.0
|
||||
optionalDependencies:
|
||||
vue-router: 4.5.0(vue@3.5.13(typescript@5.6.2))
|
||||
vue-router: 4.5.0(vue@3.4.8(typescript@5.6.2))
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- vue
|
||||
|
||||
@@ -3,7 +3,10 @@ try {
|
||||
const theme = JSON.parse(
|
||||
localStorage.getItem('homebox/preferences/location')
|
||||
).theme;
|
||||
if (theme) document.documentElement.setAttribute('data-theme', theme);
|
||||
if (theme) {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
document.documentElement.classList.add('theme-' + theme);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to set theme', e);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,128 @@
|
||||
import { config } from "dotenv";
|
||||
config();
|
||||
|
||||
// check if DISABLE_DAISYUI is set to true in the environment
|
||||
const isDisabled = process.env.DISABLE_DAISYUI === "true";
|
||||
|
||||
if (isDisabled) {
|
||||
console.log("DAISYUI DISABLED");
|
||||
}
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./app.vue", "./{components,pages,layouts}/**/*.{vue,js,ts,jsx,tsx}"],
|
||||
darkMode: "class", // or 'media' or 'class'
|
||||
darkMode: ["class"],
|
||||
safelist: [
|
||||
"dark",
|
||||
"theme-aqua",
|
||||
"theme-black",
|
||||
"theme-bumblebee",
|
||||
"theme-cmyk",
|
||||
"theme-corporate",
|
||||
"theme-cupcake",
|
||||
"theme-cyberpunk",
|
||||
"theme-dark",
|
||||
"theme-dracula",
|
||||
"theme-emerald",
|
||||
"theme-fantasy",
|
||||
"theme-forest",
|
||||
"theme-garden",
|
||||
"theme-halloween",
|
||||
"theme-light",
|
||||
"theme-lofi",
|
||||
"theme-luxury",
|
||||
"theme-pastel",
|
||||
"theme-retro",
|
||||
"theme-synthwave",
|
||||
"theme-valentine",
|
||||
"theme-wireframe",
|
||||
"theme-autumn",
|
||||
"theme-business",
|
||||
"theme-acid",
|
||||
"theme-lemonade",
|
||||
"theme-night",
|
||||
"theme-coffee",
|
||||
"theme-winter",
|
||||
"theme-dim",
|
||||
"theme-nord",
|
||||
"theme-sunset",
|
||||
],
|
||||
prefix: "",
|
||||
|
||||
theme: {
|
||||
extend: {},
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: {
|
||||
DEFAULT: "hsl(var(--primary))",
|
||||
foreground: "hsl(var(--primary-foreground))",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
foreground: "hsl(var(--secondary-foreground))",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "hsl(var(--destructive))",
|
||||
foreground: "hsl(var(--destructive-foreground))",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "hsl(var(--popover))",
|
||||
foreground: "hsl(var(--popover-foreground))",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
xl: "calc(var(--radius) + 4px)",
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: 0 },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: 0 },
|
||||
},
|
||||
"collapsible-down": {
|
||||
from: { height: 0 },
|
||||
to: { height: "var(--radix-collapsible-content-height)" },
|
||||
},
|
||||
"collapsible-up": {
|
||||
from: { height: "var(--radix-collapsible-content-height)" },
|
||||
to: { height: 0 },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"collapsible-down": "collapsible-down 0.2s ease-in-out",
|
||||
"collapsible-up": "collapsible-up 0.2s ease-in-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
daisyui: {
|
||||
themes: [
|
||||
@@ -50,8 +170,12 @@ module.exports = {
|
||||
"winter",
|
||||
],
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require("@tailwindcss/aspect-ratio"), require("@tailwindcss/typography"), require("daisyui")],
|
||||
plugins: isDisabled
|
||||
? [require("@tailwindcss/aspect-ratio"), require("@tailwindcss/typography"), require("tailwindcss-animate")]
|
||||
: [
|
||||
require("@tailwindcss/aspect-ratio"),
|
||||
require("@tailwindcss/typography"),
|
||||
require("daisyui"),
|
||||
require("tailwindcss-animate"),
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user