diff --git a/components.json b/components.json new file mode 100644 index 0000000..737ca29 --- /dev/null +++ b/components.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://shadcn-svelte.com/schema.json", + "tailwind": { + "css": "src/app.css", + "baseColor": "neutral" + }, + "aliases": { + "components": "$lib/components", + "utils": "$lib/utils", + "ui": "$lib/components/ui", + "hooks": "$lib/hooks", + "lib": "$lib" + }, + "typescript": true, + "registry": "https://shadcn-svelte.com/registry" +} diff --git a/package-lock.json b/package-lock.json index aafa63a..0ba7306 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,13 +16,17 @@ "devDependencies": { "@eslint/compat": "^1.4.0", "@eslint/js": "^9.36.0", + "@internationalized/date": "^3.10.1", + "@lucide/svelte": "^0.561.0", "@sveltejs/adapter-node": "^5.3.2", - "@sveltejs/kit": "^2.43.2", + "@sveltejs/kit": "2.49.5", "@sveltejs/vite-plugin-svelte": "^6.2.0", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/vite": "^4.1.13", "@types/node": "^22", "@vitest/browser": "^3.2.4", + "bits-ui": "^2.15.4", + "clsx": "^2.1.1", "eslint": "^9.36.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.12.4", @@ -33,10 +37,13 @@ "prettier-plugin-tailwindcss": "^0.6.14", "svelte": "^5.39.5", "svelte-check": "^4.3.2", + "tailwind-merge": "^3.4.0", + "tailwind-variants": "^3.2.2", "tailwindcss": "^4.1.13", + "tw-animate-css": "^1.4.0", "typescript": "^5.9.2", "typescript-eslint": "^8.44.1", - "vite": "^7.1.7", + "vite": "7.1.11", "vitest": "^3.2.4", "vitest-browser-svelte": "^1.1.0" } @@ -696,6 +703,34 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -748,6 +783,16 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@internationalized/date": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.1.tgz", + "integrity": "sha512-oJrXtQiAXLvT9clCf1K4kxp3eKsQhIaZqxEyowkBcsvZDdZkbWrVmnGknxs5flTD0VGsxrxKgBCZty1EzoiMzA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -811,6 +856,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lucide/svelte": { + "version": "0.561.0", + "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.561.0.tgz", + "integrity": "sha512-vofKV2UFVrKE6I4ewKJ3dfCXSV6iP6nWVmiM83MLjsU91EeJcEg7LoWUABLp/aOTxj1HQNbJD1f3g3L0JQgH9A==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "svelte": "^5" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1294,9 +1349,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.46.5", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.46.5.tgz", - "integrity": "sha512-7TSvMrCdmig5TMyYDW876C5FljhA0wlGixtvASCiqUqtLfmyEEpaysXjC7GhR5mWcGRrCGF+L2Bl1eEaW1wTCA==", + "version": "2.49.5", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.49.5.tgz", + "integrity": "sha512-dCYqelr2RVnWUuxc+Dk/dB/SjV/8JBndp1UovCyCZdIQezd8TRwFLNZctYkzgHxRJtaNvseCSRsuuHPeUgIN/A==", "dev": true, "license": "MIT", "dependencies": { @@ -1305,7 +1360,7 @@ "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", - "devalue": "^5.3.2", + "devalue": "^5.6.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", @@ -1324,11 +1379,15 @@ "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "peerDependenciesMeta": { "@opentelemetry/api": { "optional": true + }, + "typescript": { + "optional": true } } }, @@ -1371,6 +1430,16 @@ "vite": "^6.3.0 || ^7.0.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/forms": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", @@ -2300,6 +2369,31 @@ "node": ">= 18" } }, + "node_modules/bits-ui": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.15.4.tgz", + "integrity": "sha512-7H9YUfp03KOk1LVDh8wPYSRPxlZgG/GRWLNSA8QC73/8Z8ytun+DWJhIuibyFyz7A0cP/RANVcB4iDrbY8q+Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.1", + "@floating-ui/dom": "^1.7.1", + "esm-env": "^1.1.2", + "runed": "^0.35.1", + "svelte-toolbelt": "^0.10.6", + "tabbable": "^6.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/huntabyte" + }, + "peerDependencies": { + "@internationalized/date": "^3.8.1", + "svelte": "^5.33.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2568,9 +2662,9 @@ } }, "node_modules/devalue": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.3.2.tgz", - "integrity": "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz", + "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==", "dev": true, "license": "MIT" }, @@ -3185,6 +3279,13 @@ "node": ">=0.8.19" } }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "dev": true, + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -4517,6 +4618,31 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/runed": { + "version": "0.35.1", + "resolved": "https://registry.npmjs.org/runed/-/runed-0.35.1.tgz", + "integrity": "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte", + "https://github.com/sponsors/tglide" + ], + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "esm-env": "^1.0.0", + "lz-string": "^1.5.0" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.21.0", + "svelte": "^5.7.0" + }, + "peerDependenciesMeta": { + "@sveltejs/kit": { + "optional": true + } + } + }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -4671,6 +4797,16 @@ "dev": true, "license": "MIT" }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4776,6 +4912,27 @@ } } }, + "node_modules/svelte-toolbelt": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.10.6.tgz", + "integrity": "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/huntabyte" + ], + "dependencies": { + "clsx": "^2.1.1", + "runed": "^0.35.1", + "style-to-object": "^1.0.8" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8.7.0" + }, + "peerDependencies": { + "svelte": "^5.30.2" + } + }, "node_modules/svelte/node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -4796,6 +4953,44 @@ "@types/estree": "^1.0.6" } }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwind-merge": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", + "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwind-variants": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz", + "integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.x", + "pnpm": ">=7.x" + }, + "peerDependencies": { + "tailwind-merge": ">=3.0.0", + "tailwindcss": "*" + }, + "peerDependenciesMeta": { + "tailwind-merge": { + "optional": true + } + } + }, "node_modules/tailwindcss": { "version": "4.1.14", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", @@ -4931,6 +5126,23 @@ "typescript": ">=4.8.4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5007,9 +5219,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", - "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 4a714f4..2b121ab 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,17 @@ "devDependencies": { "@eslint/compat": "^1.4.0", "@eslint/js": "^9.36.0", + "@internationalized/date": "^3.10.1", + "@lucide/svelte": "^0.561.0", "@sveltejs/adapter-node": "^5.3.2", - "@sveltejs/kit": "^2.43.2", + "@sveltejs/kit": "2.49.5", "@sveltejs/vite-plugin-svelte": "^6.2.0", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/vite": "^4.1.13", "@types/node": "^22", "@vitest/browser": "^3.2.4", + "bits-ui": "^2.15.4", + "clsx": "^2.1.1", "eslint": "^9.36.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.12.4", @@ -35,10 +39,13 @@ "prettier-plugin-tailwindcss": "^0.6.14", "svelte": "^5.39.5", "svelte-check": "^4.3.2", + "tailwind-merge": "^3.4.0", + "tailwind-variants": "^3.2.2", "tailwindcss": "^4.1.13", + "tw-animate-css": "^1.4.0", "typescript": "^5.9.2", "typescript-eslint": "^8.44.1", - "vite": "^7.1.7", + "vite": "7.1.11", "vitest": "^3.2.4", "vitest-browser-svelte": "^1.1.0" }, diff --git a/src/app.css b/src/app.css index a3e426a..5d4eb59 100644 --- a/src/app.css +++ b/src/app.css @@ -1,317 +1,129 @@ -@import 'tailwindcss'; -@plugin '@tailwindcss/forms'; +@import "tailwindcss"; + +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); -/* defaults */ :root { - --text-color: #000000; - --text-box-bg: #00000010; - --bg-color: #e9e9e9; - --hover-bg-color: #d0d0d0; - --separator-line-color: #a0a0a0; - --low-emphasis-text-color: #6b6b6b; - --primary-color: #1f96f3; - --dull-primary-color: #51aaf0; - --elevated-bg-color: #ffffff; - --bg-accent-color: #f4f4f4; - --danger-color: #ff2d2f; - --hyperlink-color: #3b82f6; - --nav-bg-color: #f0f0f0; - - background: var(--bg-color); - color: var(--text-color); + --radius: 0.65rem; + --background: oklch(1 0 0); + --foreground: oklch(0.141 0.005 285.823); + --card: oklch(1 0 0); + --card-foreground: oklch(0.141 0.005 285.823); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.141 0.005 285.823); + --primary: oklch(0.606 0.25 292.717); + --primary-foreground: oklch(0.969 0.016 293.756); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.967 0.001 286.375); + --muted-foreground: oklch(0.552 0.016 285.938); + --accent: oklch(0.967 0.001 286.375); + --accent-foreground: oklch(0.21 0.006 285.885); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.92 0.004 286.32); + --input: oklch(0.92 0.004 286.32); + --ring: oklch(0.606 0.25 292.717); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.141 0.005 285.823); + --sidebar-primary: oklch(0.606 0.25 292.717); + --sidebar-primary-foreground: oklch(0.969 0.016 293.756); + --sidebar-accent: oklch(0.967 0.001 286.375); + --sidebar-accent-foreground: oklch(0.21 0.006 285.885); + --sidebar-border: oklch(0.92 0.004 286.32); + --sidebar-ring: oklch(0.606 0.25 292.717); + --warning: oklch(0.84 0.16 84); + --error: oklch(0.577 0.245 27.325); } -.bg-color { - background-color: var(--bg-color); +.dark { + --background: oklch(0.141 0.005 285.823); + --foreground: oklch(0.985 0 0); + --card: oklch(0.21 0.006 285.885); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.21 0.006 285.885); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.541 0.281 293.009); + --primary-foreground: oklch(0.969 0.016 293.756); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.274 0.006 286.033); + --muted-foreground: oklch(0.705 0.015 286.067); + --accent: oklch(0.274 0.006 286.033); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.541 0.281 293.009); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.21 0.006 285.885); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.541 0.281 293.009); + --sidebar-primary-foreground: oklch(0.969 0.016 293.756); + --sidebar-accent: oklch(0.274 0.006 286.033); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.541 0.281 293.009); + --warning: oklch(0.84 0.16 84); + --error: oklch(0.704 0.191 22.216); } -.hover-bg-color:hover { - background-color: var(--hover-bg-color); +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + --color-warning: var(--warning); + --color-error: var(--error); } -.low-emphasis-text { - color: var(--low-emphasis-text-color); +@layer base { + * { + @apply border-border outline-ring/50; + } + + body { + @apply bg-background text-foreground; + } } -.low-emphasis-text-button { - color: var(--low-emphasis-text-color); -} - -.low-emphasis-text-button:hover { - color: var(--text-color); -} - -.primary-underline { - border-bottom: 2px solid var(--primary-color); -} - -.top-border { - border-top: 1px solid var(--separator-line-color); -} - -.bottom-border { - border-bottom: 1px solid var(--separator-line-color); -} - -.left-border { - border-left: 1px solid var(--separator-line-color); -} - -.right-border { - border-right: 1px solid var(--separator-line-color); -} - -input[type='search'], -input[type='text'], -input[type='password'], -input[type='email'], -input[type='tel'], -input[type='number'], -textarea, -select { - @apply rounded-t; - background-color: var(--text-box-bg); - color: var(--text-color); - border: 0; - border-bottom: 2px solid var(--separator-line-color); - caret-color: var(--text-color); -} - -input[type='search']:focus, -input[type='text']:focus, -input[type='password']:focus, -input[type='email']:focus, -input[type='tel']:focus, -input[type='number']:focus, -textarea:focus, -select:focus { - outline: 0 solid var(--text-color); - border-bottom: 2px solid var(--primary-color); - box-shadow: 0 0 0 0 var(--primary-color); - caret-color: var(--text-color); -} - -input[type='search']:-webkit-autofill, -input[type='text']:-webkit-autofill, -input[type='password']:-webkit-autofill, -input[type='email']:-webkit-autofill, -input[type='tel']:-webkit-autofill, -input[type='number']:-webkit-autofill, -textarea:-webkit-autofill, -select:-webkit-autofill { - -webkit-text-fill-color: var(--text-color) !important; - background-clip: text !important; - caret-color: var(--text-color); -} - -input[type='search'].outlined, -input[type='text'].outlined, -input[type='password'].outlined, -input[type='email'].outlined, -input[type='tel'].outlined, -input[type='number'].outlined, -textarea.outlined, -select.outlined { - @apply rounded; - border: 1px solid var(--separator-line-color); -} - -input[type='search'].outlined:focus, -input[type='text'].outlined:focus, -input[type='password'].outlined:focus, -input[type='email'].outlined:focus, -input[type='tel'].outlined:focus, -input[type='number'].outlined:focus, -textarea.outlined:focus, -select.outlined:focus { - border: 1px solid var(--primary-color); -} - -input[type='checkbox'] { - background-color: var(--text-box-bg); - border: 1px solid var(--separator-line-color); - border-radius: 4px; -} - -input[type='checkbox']:focus { - box-shadow: none; - outline: none; -} - -.text-box-bg { - background-color: var(--text-box-bg) !important; -} - -.separator-borders { - border: 1px solid var(--separator-line-color); -} - -.elevated { - background-color: var(--elevated-bg-color); - @apply shadow-lg; -} - -.elevated-bg { - background-color: var(--elevated-bg-color); -} - -.card { - @apply rounded-lg p-4 block shadow-lg; - background-color: var(--elevated-bg-color); -} - -.nav-bg { - background-color: var(--nav-bg-color); -} - -.primary-bg-color { - background-color: var(--primary-color); -} - -.accent-bg-color { - background-color: var(--bg-accent-color); -} - -.dull-primary-bg-color { - background-color: var(--dull-primary-color); -} - -.dull-primary-text-color { - color: var(--dull-primary-color); -} - -.dull-primary-border-color { - border-color: var(--dull-primary-color); -} - -.primary-text-color { - color: var(--primary-color); -} - -.danger-bg-color { - background-color: var(--danger-color); -} - -.danger-color { - color: var(--danger-color); -} - -.danger-border-color { - border-color: var(--danger-color); -} - -.hover-hyperlink:hover { - color: var(--hyperlink-color); - text-decoration: underline var(--hyperlink-color); -} - -.hyperlink-color { - color: var(--hyperlink-color); -} - -.hyperlink-underline { - text-decoration: underline var(--hyperlink-color); -} - -.center { - margin: 0 auto; -} - -h1 { - font-size: xx-large; -} - -h2 { - font-size: x-large; -} - -h3 { - font-size: large; -} - -button { - cursor: pointer; -} - -button.normal, a.normal { - @apply px-2 py-1 leading-tight rounded-lg; - border-width: 1px; - border-color: var(--dull-primary-color); -} - -button.large, a.large { - @apply px-4 py-3 leading-tight rounded-xl; - border-width: 1px; - border-color: var(--dull-primary-color); -} - -button.wide { - @apply px-4 py-2 leading-tight rounded w-full; -} - -button.text { - color: var(--dull-primary-color); -} - -button.filled, a.filled { - background-color: var(--dull-primary-color); -} - -button.danger { - background-color: var(--danger-color); - border-width: 0; -} - -button.outlined-danger { - border-color: var(--danger-color); -} - -button.text-danger { - color: var(--danger-color); - border-width: 0; -} - -.switch { - display: flex; - border: 1px solid #aaa; - border-radius: 4px; - overflow: hidden; -} -.switch button { - flex: 1; - padding: 0.5rem 1rem; - border: none; - background: #f0f0f0; - cursor: pointer; -} -.switch button.selected { - background: #007acc; - color: white; -} - - -.modal { - display: none; - position: fixed; - z-index: 1; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgb(0, 0, 0); /* Fallback color */ - background-color: rgba(0, 0, 0, 0.6); /* Darken background */ -} - -.modal-content { - background-color: var(--bg-color); - margin: 10% auto; - padding: 16px; - border: 1px solid var(--separator-line-color); - max-width: 420px; - border-radius: 8px; -} - -tr:nth-child(even) { - background-color: rgb(from var(--primary-color) r g b / 10%); -} diff --git a/src/lib/components/login-form.svelte b/src/lib/components/login-form.svelte new file mode 100644 index 0000000..550cb69 --- /dev/null +++ b/src/lib/components/login-form.svelte @@ -0,0 +1,64 @@ + + +
+ +
+

Login to your account

+

+ Enter your email below to login to your account +

+
+ + Email + + + + + + + + + + Or continue with + + + + Don't have an account? + Sign up + + +
+
diff --git a/src/lib/components/signup-form.svelte b/src/lib/components/signup-form.svelte new file mode 100644 index 0000000..9277292 --- /dev/null +++ b/src/lib/components/signup-form.svelte @@ -0,0 +1,59 @@ + + +
+ +
+

Create your account

+

+ Fill in the form below to create your account +

+
+ + Full Name + + + + Email + + + We'll use this to contact you. We will not share your email with anyone else. + + + + Password + + Must be at least 8 characters long. + + + Confirm Password + + Please confirm your password. + + + + + Or continue with + + + + Already have an account? Sign in + + +
+
diff --git a/src/lib/components/ui/button/button.svelte b/src/lib/components/ui/button/button.svelte new file mode 100644 index 0000000..a8296ae --- /dev/null +++ b/src/lib/components/ui/button/button.svelte @@ -0,0 +1,82 @@ + + + + +{#if href} + + {@render children?.()} + +{:else} + +{/if} diff --git a/src/lib/components/ui/button/index.ts b/src/lib/components/ui/button/index.ts new file mode 100644 index 0000000..fb585d7 --- /dev/null +++ b/src/lib/components/ui/button/index.ts @@ -0,0 +1,17 @@ +import Root, { + type ButtonProps, + type ButtonSize, + type ButtonVariant, + buttonVariants, +} from "./button.svelte"; + +export { + Root, + type ButtonProps as Props, + // + Root as Button, + buttonVariants, + type ButtonProps, + type ButtonSize, + type ButtonVariant, +}; diff --git a/src/lib/components/ui/card/card-action.svelte b/src/lib/components/ui/card/card-action.svelte new file mode 100644 index 0000000..cc36c56 --- /dev/null +++ b/src/lib/components/ui/card/card-action.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card-content.svelte b/src/lib/components/ui/card/card-content.svelte new file mode 100644 index 0000000..bc90b83 --- /dev/null +++ b/src/lib/components/ui/card/card-content.svelte @@ -0,0 +1,15 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card-description.svelte b/src/lib/components/ui/card/card-description.svelte new file mode 100644 index 0000000..9b20ac7 --- /dev/null +++ b/src/lib/components/ui/card/card-description.svelte @@ -0,0 +1,20 @@ + + +

+ {@render children?.()} +

diff --git a/src/lib/components/ui/card/card-footer.svelte b/src/lib/components/ui/card/card-footer.svelte new file mode 100644 index 0000000..2d4d0f2 --- /dev/null +++ b/src/lib/components/ui/card/card-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card-header.svelte b/src/lib/components/ui/card/card-header.svelte new file mode 100644 index 0000000..2501788 --- /dev/null +++ b/src/lib/components/ui/card/card-header.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card-title.svelte b/src/lib/components/ui/card/card-title.svelte new file mode 100644 index 0000000..7447231 --- /dev/null +++ b/src/lib/components/ui/card/card-title.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/card.svelte b/src/lib/components/ui/card/card.svelte new file mode 100644 index 0000000..99448cc --- /dev/null +++ b/src/lib/components/ui/card/card.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/card/index.ts b/src/lib/components/ui/card/index.ts new file mode 100644 index 0000000..4d3fce4 --- /dev/null +++ b/src/lib/components/ui/card/index.ts @@ -0,0 +1,25 @@ +import Root from "./card.svelte"; +import Content from "./card-content.svelte"; +import Description from "./card-description.svelte"; +import Footer from "./card-footer.svelte"; +import Header from "./card-header.svelte"; +import Title from "./card-title.svelte"; +import Action from "./card-action.svelte"; + +export { + Root, + Content, + Description, + Footer, + Header, + Title, + Action, + // + Root as Card, + Content as CardContent, + Description as CardDescription, + Footer as CardFooter, + Header as CardHeader, + Title as CardTitle, + Action as CardAction, +}; diff --git a/src/lib/components/ui/field/field-content.svelte b/src/lib/components/ui/field/field-content.svelte new file mode 100644 index 0000000..1b6535b --- /dev/null +++ b/src/lib/components/ui/field/field-content.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/field/field-description.svelte b/src/lib/components/ui/field/field-description.svelte new file mode 100644 index 0000000..a0c9f06 --- /dev/null +++ b/src/lib/components/ui/field/field-description.svelte @@ -0,0 +1,25 @@ + + +

a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", + className + )} + {...restProps} +> + {@render children?.()} +

diff --git a/src/lib/components/ui/field/field-error.svelte b/src/lib/components/ui/field/field-error.svelte new file mode 100644 index 0000000..7460b31 --- /dev/null +++ b/src/lib/components/ui/field/field-error.svelte @@ -0,0 +1,58 @@ + + +{#if hasContent} + +{/if} diff --git a/src/lib/components/ui/field/field-group.svelte b/src/lib/components/ui/field/field-group.svelte new file mode 100644 index 0000000..e685427 --- /dev/null +++ b/src/lib/components/ui/field/field-group.svelte @@ -0,0 +1,23 @@ + + +
[data-slot=field-group]]:gap-4", + className + )} + {...restProps} +> + {@render children?.()} +
diff --git a/src/lib/components/ui/field/field-label.svelte b/src/lib/components/ui/field/field-label.svelte new file mode 100644 index 0000000..2ee431a --- /dev/null +++ b/src/lib/components/ui/field/field-label.svelte @@ -0,0 +1,26 @@ + + + diff --git a/src/lib/components/ui/field/field-legend.svelte b/src/lib/components/ui/field/field-legend.svelte new file mode 100644 index 0000000..3f1c50f --- /dev/null +++ b/src/lib/components/ui/field/field-legend.svelte @@ -0,0 +1,29 @@ + + + + {@render children?.()} + diff --git a/src/lib/components/ui/field/field-separator.svelte b/src/lib/components/ui/field/field-separator.svelte new file mode 100644 index 0000000..12bcb77 --- /dev/null +++ b/src/lib/components/ui/field/field-separator.svelte @@ -0,0 +1,38 @@ + + +
+ + {#if children} + + {@render children()} + + {/if} +
diff --git a/src/lib/components/ui/field/field-set.svelte b/src/lib/components/ui/field/field-set.svelte new file mode 100644 index 0000000..1d8e233 --- /dev/null +++ b/src/lib/components/ui/field/field-set.svelte @@ -0,0 +1,24 @@ + + +
[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", + className + )} + {...restProps} +> + {@render children?.()} +
diff --git a/src/lib/components/ui/field/field-title.svelte b/src/lib/components/ui/field/field-title.svelte new file mode 100644 index 0000000..5906044 --- /dev/null +++ b/src/lib/components/ui/field/field-title.svelte @@ -0,0 +1,23 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/field/field.svelte b/src/lib/components/ui/field/field.svelte new file mode 100644 index 0000000..3284203 --- /dev/null +++ b/src/lib/components/ui/field/field.svelte @@ -0,0 +1,53 @@ + + + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/field/index.ts b/src/lib/components/ui/field/index.ts new file mode 100644 index 0000000..a644a95 --- /dev/null +++ b/src/lib/components/ui/field/index.ts @@ -0,0 +1,33 @@ +import Field from "./field.svelte"; +import Set from "./field-set.svelte"; +import Legend from "./field-legend.svelte"; +import Group from "./field-group.svelte"; +import Content from "./field-content.svelte"; +import Label from "./field-label.svelte"; +import Title from "./field-title.svelte"; +import Description from "./field-description.svelte"; +import Separator from "./field-separator.svelte"; +import Error from "./field-error.svelte"; + +export { + Field, + Set, + Legend, + Group, + Content, + Label, + Title, + Description, + Separator, + Error, + // + Set as FieldSet, + Legend as FieldLegend, + Group as FieldGroup, + Content as FieldContent, + Label as FieldLabel, + Title as FieldTitle, + Description as FieldDescription, + Separator as FieldSeparator, + Error as FieldError, +}; diff --git a/src/lib/components/ui/input/index.ts b/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..f47b6d3 --- /dev/null +++ b/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from "./input.svelte"; + +export { + Root, + // + Root as Input, +}; diff --git a/src/lib/components/ui/input/input.svelte b/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..ff1a4c8 --- /dev/null +++ b/src/lib/components/ui/input/input.svelte @@ -0,0 +1,52 @@ + + +{#if type === "file"} + +{:else} + +{/if} diff --git a/src/lib/components/ui/label/index.ts b/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..8bfca0b --- /dev/null +++ b/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from "./label.svelte"; + +export { + Root, + // + Root as Label, +}; diff --git a/src/lib/components/ui/label/label.svelte b/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..d71afbc --- /dev/null +++ b/src/lib/components/ui/label/label.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/separator/index.ts b/src/lib/components/ui/separator/index.ts new file mode 100644 index 0000000..82442d2 --- /dev/null +++ b/src/lib/components/ui/separator/index.ts @@ -0,0 +1,7 @@ +import Root from "./separator.svelte"; + +export { + Root, + // + Root as Separator, +}; diff --git a/src/lib/components/ui/separator/separator.svelte b/src/lib/components/ui/separator/separator.svelte new file mode 100644 index 0000000..f40999f --- /dev/null +++ b/src/lib/components/ui/separator/separator.svelte @@ -0,0 +1,21 @@ + + + diff --git a/src/lib/shared.ts b/src/lib/shared.ts new file mode 100644 index 0000000..3fbc28a --- /dev/null +++ b/src/lib/shared.ts @@ -0,0 +1,41 @@ +import type { User } from '$lib/types'; + +export const getCookieValue = (name: string): string => + document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || ''; + +export const getUserFromJWT = (jwt: string): User => JSON.parse(atob(jwt.split('.')[1])); + +// export const userData = () => { +// let userData: User | null = null; +// const cookieValue = getCookieValue('jwt'); +// if (cookieValue) { +// userData = getUserFromJWT(cookieValue); +// } +// return userData; +// }; + +export function getFormString(data: FormData, key: string): string | undefined { + const value = data.get(key); + if (value === null) { + return undefined; + } + + if (typeof value !== 'string') { + throw Error(`Incorrect input in field ${key}.`); + } + return value.trim(); +} + +export function getRequiredFormString(data: FormData, key: string) { + const value = data.get(key); + if (typeof value !== 'string') { + throw Error(`Missing required field ${key}.`); + } + return value.trim(); +} + +export const dateFormatOptions: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: 'short', + day: 'numeric' +}; diff --git a/src/lib/types.ts b/src/lib/types.ts index 84c1af1..8be041a 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -18,4 +18,5 @@ export interface Item { title: string; description: string; transferred: boolean; // to L&F location + keywords?: string[]; } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 3fbc28a..97525cc 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,41 +1,13 @@ -import type { User } from '$lib/types'; +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; -export const getCookieValue = (name: string): string => - document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)')?.pop() || ''; - -export const getUserFromJWT = (jwt: string): User => JSON.parse(atob(jwt.split('.')[1])); - -// export const userData = () => { -// let userData: User | null = null; -// const cookieValue = getCookieValue('jwt'); -// if (cookieValue) { -// userData = getUserFromJWT(cookieValue); -// } -// return userData; -// }; - -export function getFormString(data: FormData, key: string): string | undefined { - const value = data.get(key); - if (value === null) { - return undefined; - } - - if (typeof value !== 'string') { - throw Error(`Incorrect input in field ${key}.`); - } - return value.trim(); +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); } -export function getRequiredFormString(data: FormData, key: string) { - const value = data.get(key); - if (typeof value !== 'string') { - throw Error(`Missing required field ${key}.`); - } - return value.trim(); -} - -export const dateFormatOptions: Intl.DateTimeFormatOptions = { - year: 'numeric', - month: 'short', - day: 'numeric' -}; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChild = T extends { child?: any } ? Omit : T; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChildren = T extends { children?: any } ? Omit : T; +export type WithoutChildrenOrChild = WithoutChildren>; +export type WithElementRef = T & { ref?: U | null }; diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 2317225..ff05451 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,5 +1,5 @@ import type { LayoutServerLoad } from './$types'; -import { getUserFromJWT } from '$lib/utils'; +import { getUserFromJWT } from '$lib/shared'; export const load: LayoutServerLoad = ({ cookies }) => { const jwt = cookies.get('jwt'); diff --git a/src/routes/signin/+page.server.ts b/src/routes/login/+page.server.ts similarity index 100% rename from src/routes/signin/+page.server.ts rename to src/routes/login/+page.server.ts diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte new file mode 100644 index 0000000..8956b39 --- /dev/null +++ b/src/routes/login/+page.svelte @@ -0,0 +1,31 @@ + + +
+
+ +
+
+ +
+
+
+ +
diff --git a/src/routes/signin/+page.svelte b/src/routes/signup/+page.server.ts similarity index 100% rename from src/routes/signin/+page.svelte rename to src/routes/signup/+page.server.ts diff --git a/src/routes/signup/+page.svelte b/src/routes/signup/+page.svelte new file mode 100644 index 0000000..8332ff5 --- /dev/null +++ b/src/routes/signup/+page.svelte @@ -0,0 +1,31 @@ + + +
+
+ +
+
+ +
+
+
+ +