List components #31

Merged
stne3960 merged 68 commits from list_item into main 2025-12-18 12:41:13 +01:00
17 changed files with 775 additions and 457 deletions
Showing only changes of commit 30e4920da3 - Show all commits

6
.gitattributes vendored
View File

@ -8,3 +8,9 @@
*.jpg binary *.jpg binary
*.png binary *.png binary
*.jar binary *.jar binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.eot binary

View File

@ -8,7 +8,7 @@
"name": "frontend", "name": "frontend",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.16", "clsx": "^2.1.1",
"openapi-fetch": "^0.13.5", "openapi-fetch": "^0.13.5",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
@ -17,6 +17,7 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",
"@tailwindcss/vite": "^4.1.16",
"@types/react": "^19.0.10", "@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@vitejs/plugin-react-swc": "^3.8.0", "@vitejs/plugin-react-swc": "^3.8.0",
@ -27,6 +28,7 @@
"globals": "^15.15.0", "globals": "^15.15.0",
"openapi-typescript": "^7.6.1", "openapi-typescript": "^7.6.1",
"prettier": "3.5.3", "prettier": "3.5.3",
"tailwindcss": "^4.1.16",
"typescript": "~5.7.2", "typescript": "~5.7.2",
"typescript-eslint": "^8.24.1", "typescript-eslint": "^8.24.1",
"vite": "^6.2.0" "vite": "^6.2.0"
@ -64,6 +66,7 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -80,6 +83,7 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -96,6 +100,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -112,6 +117,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -128,6 +134,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -144,6 +151,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -160,6 +168,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -176,6 +185,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -192,6 +202,7 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -208,6 +219,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -224,6 +236,7 @@
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -240,6 +253,7 @@
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -256,6 +270,7 @@
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -272,6 +287,7 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -288,6 +304,7 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -304,6 +321,7 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -320,6 +338,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -336,6 +355,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -352,6 +372,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -368,6 +389,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -384,6 +406,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -400,6 +423,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -416,6 +440,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -432,6 +457,7 @@
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -448,6 +474,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -681,6 +708,7 @@
"version": "0.3.13", "version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/sourcemap-codec": "^1.5.0",
@ -691,6 +719,7 @@
"version": "2.3.5", "version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/gen-mapping": "^0.3.5",
@ -701,6 +730,7 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
@ -710,12 +740,14 @@
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31", "version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
@ -843,6 +875,7 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -856,6 +889,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -869,6 +903,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -882,6 +917,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -895,6 +931,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -908,6 +945,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -921,6 +959,7 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -934,6 +973,7 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -947,6 +987,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -960,6 +1001,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -973,6 +1015,7 @@
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -986,6 +1029,7 @@
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -999,6 +1043,7 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1012,6 +1057,7 @@
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1025,6 +1071,7 @@
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1038,6 +1085,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1051,6 +1099,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1064,6 +1113,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1077,6 +1127,7 @@
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1090,6 +1141,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1326,6 +1378,7 @@
"version": "4.1.16", "version": "4.1.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.16.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.16.tgz",
"integrity": "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==", "integrity": "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/remapping": "^2.3.4", "@jridgewell/remapping": "^2.3.4",
@ -1341,6 +1394,7 @@
"version": "4.1.16", "version": "4.1.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.16.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.16.tgz",
"integrity": "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==", "integrity": "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
@ -1367,6 +1421,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1383,6 +1438,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1399,6 +1455,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1415,6 +1472,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1431,6 +1489,7 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1447,6 +1506,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1463,6 +1523,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1479,6 +1540,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1495,6 +1557,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1519,6 +1582,7 @@
"cpu": [ "cpu": [
"wasm32" "wasm32"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
@ -1535,6 +1599,7 @@
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.5.0", "version": "1.5.0",
"dev": true,
"inBundle": true, "inBundle": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -1545,6 +1610,7 @@
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.5.0", "version": "1.5.0",
"dev": true,
"inBundle": true, "inBundle": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -1554,6 +1620,7 @@
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.1.0", "version": "1.1.0",
"dev": true,
"inBundle": true, "inBundle": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -1563,6 +1630,7 @@
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "1.0.7", "version": "1.0.7",
"dev": true,
"inBundle": true, "inBundle": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -1574,6 +1642,7 @@
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.10.1", "version": "0.10.1",
"dev": true,
"inBundle": true, "inBundle": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -1583,6 +1652,7 @@
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"dev": true,
"inBundle": true, "inBundle": true,
"license": "0BSD", "license": "0BSD",
"optional": true "optional": true
@ -1594,6 +1664,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1610,6 +1681,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@ -1623,6 +1695,7 @@
"version": "4.1.16", "version": "4.1.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.16.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.16.tgz",
"integrity": "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==", "integrity": "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tailwindcss/node": "4.1.16", "@tailwindcss/node": "4.1.16",
@ -1637,6 +1710,7 @@
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
@ -2033,6 +2107,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -2127,6 +2210,7 @@
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -2136,6 +2220,7 @@
"version": "5.18.3", "version": "5.18.3",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
@ -2149,6 +2234,7 @@
"version": "0.25.1", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
"integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@ -2518,6 +2604,7 @@
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -2558,6 +2645,7 @@
"version": "4.2.11", "version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/graphemer": { "node_modules/graphemer": {
@ -2685,6 +2773,7 @@
"version": "2.6.1", "version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"jiti": "lib/jiti-cli.mjs" "jiti": "lib/jiti-cli.mjs"
@ -2769,6 +2858,7 @@
"version": "1.30.2", "version": "1.30.2",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"detect-libc": "^2.0.3" "detect-libc": "^2.0.3"
@ -2801,6 +2891,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2821,6 +2912,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2841,6 +2933,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2861,6 +2954,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2881,6 +2975,7 @@
"cpu": [ "cpu": [
"arm" "arm"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2901,6 +2996,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2921,6 +3017,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2941,6 +3038,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2961,6 +3059,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -2981,6 +3080,7 @@
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -3001,6 +3101,7 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@ -3041,6 +3142,7 @@
"version": "0.30.21", "version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
@ -3094,6 +3196,7 @@
"version": "3.3.11", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -3269,6 +3372,7 @@
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
@ -3298,6 +3402,7 @@
"version": "8.5.3", "version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -3457,6 +3562,7 @@
"version": "4.38.0", "version": "4.38.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz",
"integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==", "integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.7" "@types/estree": "1.0.7"
@ -3568,6 +3674,7 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -3603,12 +3710,14 @@
"version": "4.1.16", "version": "4.1.16",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
"integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==", "integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -3622,6 +3731,7 @@
"version": "0.2.14", "version": "0.2.14",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fdir": "^6.4.4", "fdir": "^6.4.4",
@ -3638,6 +3748,7 @@
"version": "6.4.5", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
"dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"picomatch": "^3 || ^4" "picomatch": "^3 || ^4"
@ -3652,6 +3763,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -3770,6 +3882,7 @@
"version": "6.3.5", "version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
@ -3844,6 +3957,7 @@
"version": "6.4.5", "version": "6.4.5",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
"dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"picomatch": "^3 || ^4" "picomatch": "^3 || ^4"
@ -3858,6 +3972,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"

View File

@ -12,7 +12,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.16", "clsx": "^2.1.1",
"openapi-fetch": "^0.13.5", "openapi-fetch": "^0.13.5",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
@ -21,6 +21,7 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",
"@tailwindcss/vite": "^4.1.16",
"@types/react": "^19.0.10", "@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@vitejs/plugin-react-swc": "^3.8.0", "@vitejs/plugin-react-swc": "^3.8.0",
@ -31,6 +32,7 @@
"globals": "^15.15.0", "globals": "^15.15.0",
"openapi-typescript": "^7.6.1", "openapi-typescript": "^7.6.1",
"prettier": "3.5.3", "prettier": "3.5.3",
"tailwindcss": "^4.1.16",
"typescript": "~5.7.2", "typescript": "~5.7.2",
"typescript-eslint": "^8.24.1", "typescript-eslint": "^8.24.1",
"vite": "^6.2.0" "vite": "^6.2.0"

View File

@ -1,4 +1,5 @@
import type { ButtonHTMLAttributes, ReactNode } from "react"; import type { ButtonHTMLAttributes, ReactNode } from "react";
import clsx from "clsx";
export type ButtonVariant = "primary" | "secondary" | "red" | "green"; export type ButtonVariant = "primary" | "secondary" | "red" | "green";
export type ButtonSize = "sm" | "md" | "lg"; export type ButtonSize = "sm" | "md" | "lg";
@ -10,13 +11,36 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
} }
const variantClasses: Record<ButtonVariant, string> = { const variantClasses: Record<ButtonVariant, string> = {
primary: primary: clsx(
"bg-primary text-base-canvas border border-primary hover:bg-secondary hover:text-base-canvas hover:border hover:border-primary focus-visible:bg-base-canvas focus-visible:text-base-ink-strong focus-visible:border focus-visible:border-primary focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100", "bg-primary text-base-canvas",
secondary: "border border-primary",
"bg-base-canvas text-base-ink-strong border-solid [border-width:var(--border-width-sm)] border-base-ink-soft hover:bg-base-canvas hover:text-base-ink-strong hover:border-base-ink-medium focus-visible:bg-base-canvas focus-visible:text-base-ink-strong focus-visible:border-primary focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100", "hover:bg-secondary hover:text-base-canvas",
red: "bg-other-red-100 text-su-white focus-visible:bg-base-canvas focus-visible:text-base-ink-strong focus-visible:border-primary focus-visible:border focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100", "hover:border hover:border-primary",
green: "focus-visible:bg-base-canvas focus-visible:text-base-ink-strong",
"bg-other-green text-su-white focus-visible:bg-base-canvas focus-visible:text-base-ink-strong focus-visible:border-primary focus-visible:border focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100", "focus-visible:border focus-visible:border-primary",
"focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100",
),
secondary: clsx(
"bg-base-canvas text-base-ink-strong",
"border-solid [border-width:var(--border-width-sm)] border-base-ink-soft",
"hover:bg-base-canvas hover:text-base-ink-strong",
"hover:border-base-ink-medium",
"focus-visible:bg-base-canvas focus-visible:text-base-ink-strong",
"focus-visible:border-primary",
"focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100",
),
red: clsx(
"bg-other-red-100 text-su-white",
"focus-visible:bg-base-canvas focus-visible:text-base-ink-strong",
"focus-visible:border-primary focus-visible:border",
"focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100",
),
green: clsx(
"bg-other-green text-su-white",
"focus-visible:bg-base-canvas focus-visible:text-base-ink-strong",
"focus-visible:border-primary focus-visible:border",
"focus-visible:outline focus-visible:outline-[length:var(--border-width-lg)] focus-visible:outline-sky-100",
),
}; };
const sizeClasses: Record<ButtonSize, string> = { const sizeClasses: Record<ButtonSize, string> = {
@ -40,14 +64,12 @@ export default function Button({
}: ButtonProps) { }: ButtonProps) {
const baseClasses = "inline-flex items-center justify-center cursor-pointer"; const baseClasses = "inline-flex items-center justify-center cursor-pointer";
const classes = [ const classes = clsx(
baseClasses, baseClasses,
variantClasses[variant], variantClasses[variant],
sizeClasses[size], sizeClasses[size],
className, className,
] );
.filter(Boolean)
.join(" ");
return ( return (
<button className={classes} {...props}> <button className={classes} {...props}>

View File

@ -0,0 +1,96 @@
import type { SVGAttributes } from "react";
import clsx from "clsx";
/**
* Icon sizes matching the design system control sizes.
* "inherit" (default) uses 1em to scale with the parent's font-size.
*/
export type IconSize = "inherit" | "sm" | "md" | "lg";
/**
* Base props for all icon components.
* Extends SVG attributes but replaces `size` with our design system size.
*/
export interface IconProps extends Omit<SVGAttributes<SVGSVGElement>, "size"> {
size?: IconSize;
}
/**
* Type for icon components that can be passed to controls like TextInput.
* Usage: `<TextInput Icon={SearchIcon} />`
*/
export type IconComponent = React.ComponentType<IconProps>;
/**
* Size classes using design system font-size tokens.
* "inherit" uses 1em so icons scale with the parent's font-size.
*/
const iconSizeClasses: Record<IconSize, string> = {
inherit: "w-[1em] h-[1em]",
sm: "w-(--font-size-body-md) h-(--font-size-body-md)",
md: "w-(--font-size-body-md) h-(--font-size-body-md)",
lg: "w-(--font-size-body-lg) h-(--font-size-body-lg)",
};
/**
* Shared SVG attributes applied to all icons.
* Uses stroke (not fill) so icons inherit text color via currentColor.
*/
const baseSvgProps: SVGAttributes<SVGSVGElement> = {
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: 2,
strokeLinecap: "round",
strokeLinejoin: "round",
};
export function SearchIcon({
size = "inherit",
className,
...props
}: IconProps) {
return (
<svg
className={clsx(iconSizeClasses[size], className)}
{...baseSvgProps}
{...props}
>
<circle cx="11" cy="11" r="8" />
<path d="M21 21l-4.35-4.35" />
</svg>
);
}
export function RemoveIcon({
size = "inherit",
className,
...props
}: IconProps) {
return (
<svg
className={clsx(iconSizeClasses[size], className)}
{...baseSvgProps}
{...props}
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
);
}
export function CheckmarkIcon({
size = "inherit",
className,
...props
}: IconProps) {
return (
<svg
className={clsx(iconSizeClasses[size], className)}
{...baseSvgProps}
{...props}
>
<polyline points="20 6 9 17 4 12" />
</svg>
);
}

View File

@ -1,113 +1,138 @@
import type { InputHTMLAttributes, ReactNode, CSSProperties } from 'react'; import { useId, type InputHTMLAttributes } from "react";
// isValidElement: checks if something is a React element (e.g., <svg>, <MyComponent />) import clsx from "clsx";
// cloneElement: creates a copy of a React element with modified/additional props import type { IconComponent } from "../Icon/Icon";
import { cloneElement, isValidElement } from 'react';
export type TextInputSize = 'sm' | 'md' | 'lg'; export type TextInputSize = "sm" | "md" | "lg";
// Omit<... 'size'> removes the native 'size' attribute from input elements so we can use our own // Omit<... 'size'> removes the native 'size' attribute from input elements so we can use our own
export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> { export interface TextInputProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
size?: TextInputSize; size?: TextInputSize;
icon?: ReactNode; Icon?: IconComponent;
error?: boolean; error?: boolean;
fullWidth?: boolean; fullWidth?: boolean;
customWidth?: string; customWidth?: string;
label?: string; label: string;
hideLabel?: boolean;
message?: string; message?: string;
} }
const heightClasses: Record<TextInputSize, string> = { const wrapperSizeClasses: Record<TextInputSize, string> = {
sm: 'h-(--control-height-sm)', sm: clsx(
md: 'h-(--control-height-md)', "h-(--control-height-sm)",
lg: 'h-(--control-height-lg)', "rounded-(--border-radius-sm)",
"w-(--text-input-default-width-md)",
),
md: clsx(
"h-(--control-height-md)",
"rounded-(--border-radius-sm)",
"w-(--text-input-default-width-md)",
),
lg: clsx(
"h-(--control-height-lg)",
"rounded-(--border-radius-md)",
"w-(--text-input-default-width-lg)",
),
}; };
const widthClasses: Record<TextInputSize, string> = { const inputSizeClasses: Record<TextInputSize, string> = {
sm: 'w-(--text-input-default-width-md)', sm: "body-normal-md",
md: 'w-(--text-input-default-width-md)', md: "body-normal-md",
lg: 'w-(--text-input-default-width-lg)', lg: "body-normal-lg",
}; };
const radiusClasses: Record<TextInputSize, string> = { const iconContainerSizeClasses: Record<TextInputSize, string> = {
sm: 'rounded-(--border-radius-sm)', sm: clsx("w-(--control-height-sm) h-(--control-height-sm)"),
md: 'rounded-(--border-radius-sm)', md: clsx("w-(--control-height-md) h-(--control-height-md)"),
lg: 'rounded-(--border-radius-md)', lg: clsx("w-(--control-height-lg) h-(--control-height-lg)"),
}; };
const textClasses: Record<TextInputSize, string> = { const baseClasses =
sm: 'body-normal-md', "bg-base-canvas border-[length:var(--border-width-sm)] border-base-ink-medium";
md: 'body-normal-md',
lg: 'body-normal-lg',
};
const iconContainerStyles: Record<TextInputSize, CSSProperties> = {
sm: { width: 'var(--control-height-sm)', height: 'var(--control-height-sm)' },
md: { width: 'var(--control-height-md)', height: 'var(--control-height-md)' },
lg: { width: 'var(--control-height-lg)', height: 'var(--control-height-lg)' },
};
const iconStyles: Record<TextInputSize, CSSProperties> = {
sm: { width: 'var(--font-size-body-md)', height: 'var(--font-size-body-md)' },
md: { width: 'var(--font-size-body-md)', height: 'var(--font-size-body-md)' },
lg: { width: 'var(--font-size-body-lg)', height: 'var(--font-size-body-lg)' },
};
const baseClasses = 'bg-base-canvas border-[length:var(--border-width-sm)] border-base-ink-medium';
// focus-within: applies styles when any child element (the input) has focus // focus-within: applies styles when any child element (the input) has focus
const defaultStateClasses = const defaultStateClasses = clsx(
'hover:border-base-ink-placeholder focus-within:border-primary focus-within:outline focus-within:outline-sky-100 focus-within:outline-[length:var(--border-width-lg)]'; "hover:border-base-ink-placeholder",
"focus-within:border-primary",
"focus-within:outline focus-within:outline-sky-100 focus-within:outline-[length:var(--border-width-lg)]",
);
const errorStateClasses = const errorStateClasses = clsx(
'border-fire-100 outline outline-fire-100 outline-[length:var(--border-width-sm)] focus-within:border-primary focus-within:outline focus-within:outline-[length:var(--border-width-lg)] focus-within:outline-sky-100'; "border-fire-100",
"outline outline-fire-100 outline-[length:var(--border-width-sm)]",
"focus-within:border-primary",
"focus-within:outline focus-within:outline-sky-100 focus-within:outline-[length:var(--border-width-lg)]",
);
export default function TextInput({ export default function TextInput({
size = 'md', size = "md",
icon, Icon,
error = false, error = false,
fullWidth = false, fullWidth = false,
customWidth, customWidth,
label, label,
hideLabel = false,
message, message,
className = '', className = "",
...props ...props
}: TextInputProps) { }: TextInputProps) {
const widthClass = fullWidth ? 'w-full' : widthClasses[size]; const inputId = useId();
const widthStyle = customWidth ? { width: customWidth } : undefined; const widthStyle = customWidth ? { width: customWidth } : undefined;
const stateClasses = error ? errorStateClasses : defaultStateClasses; const stateClasses = error ? errorStateClasses : defaultStateClasses;
const inputPadding = icon ? 'pr-(--padding-md)' : 'px-(--padding-md)'; const inputPadding = Icon ? "pr-(--padding-md)" : "px-(--padding-md)";
const showVisibleLabel = !hideLabel;
const inputField = ( const inputField = (
<div <div
className={`flex items-center ${baseClasses} ${heightClasses[size]} ${widthClass} ${radiusClasses[size]} ${stateClasses} ${className}`} className={clsx(
"flex items-center",
baseClasses,
wrapperSizeClasses[size],
stateClasses,
fullWidth && "w-full",
className,
)}
style={widthStyle} style={widthStyle}
> >
{icon && ( {Icon && (
<div <div
className="flex items-center justify-center shrink-0 text-base-ink-placeholder" className={clsx(
style={iconContainerStyles[size]} "flex items-center justify-center shrink-0 text-base-ink-placeholder",
iconContainerSizeClasses[size],
)}
> >
{isValidElement<{ style?: CSSProperties }>(icon) <Icon size={size} />
? cloneElement(icon, { style: iconStyles[size] })
: icon}
</div> </div>
)} )}
<input <input
className={`flex-1 min-w-0 h-full bg-transparent border-none outline-none text-base-ink-max placeholder:text-base-ink-placeholder ${inputPadding} ${textClasses[size]}`} id={inputId}
aria-label={hideLabel ? label : undefined}
className={clsx(
"flex-1 min-w-0 h-full bg-transparent border-none outline-none",
"text-base-ink-max placeholder:text-base-ink-placeholder",
inputPadding,
inputSizeClasses[size],
)}
{...props} {...props}
/> />
</div> </div>
); );
if (!label && !message) { if (!showVisibleLabel && !message) {
return inputField; return inputField;
} }
return ( return (
<div className="flex flex-col gap-(--spacing-sm)"> <div className="flex flex-col gap-(--spacing-sm)">
{label && <label className="body-bold-md text-base-ink-strong">{label}</label>} {showVisibleLabel && (
<label htmlFor={inputId} className="body-bold-md text-base-ink-strong">
{label}
</label>
)}
{inputField} {inputField}
{message && <span className="body-light-sm text-base-ink-strong">{message}</span>} {message && (
<span className="body-light-sm text-base-ink-strong">{message}</span>
)}
</div> </div>
); );
} }

View File

@ -2,65 +2,101 @@
/* TheSans Font Family */ /* TheSans Font Family */
@font-face { @font-face {
font-family: "TheSans"; font-family: "TheSansB W2 ExtraLight";
src: url("./assets/TheSansB-W5Plain.woff2") format("woff2"); src: url("./assets/TheSansB-W2ExtraLight.woff2") format("woff2");
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "TheSans"; font-family: "TheSansB W2 ExtraLight";
src: url("./assets/TheSansB-W5PlainItalic.woff2") format("woff2"); src: url("./assets/TheSansB-W2ExtraLightItalic.woff2") format("woff2");
font-style: italic; font-style: italic;
} }
@font-face { @font-face {
font-family: "TheSans Light"; font-family: "TheSansB W3 Light";
src: url("./assets/TheSansB-W3Light.woff2") format("woff2"); src: url("./assets/TheSansB-W3Light.woff2") format("woff2");
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "TheSans Light"; font-family: "TheSansB W3 Light";
src: url("./assets/TheSansB-W3LightItalic.woff2") format("woff2"); src: url("./assets/TheSansB-W3LightItalic.woff2") format("woff2");
font-style: italic; font-style: italic;
} }
@font-face { @font-face {
font-family: "TheSans SemiLight"; font-family: "TheSansB W4 SemiLight";
src: url("./assets/TheSansB-W4SemiLight.woff2") format("woff2"); src: url("./assets/TheSansB-W4SemiLight.woff2") format("woff2");
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "TheSans SemiLight"; font-family: "TheSansB W4 SemiLight";
src: url("./assets/TheSansB-W4SemiLightItalic.woff2") format("woff2"); src: url("./assets/TheSansB-W4SemiLightItalic.woff2") format("woff2");
font-style: italic; font-style: italic;
} }
@font-face { @font-face {
font-family: "TheSans Plain"; font-family: "TheSansB W5 Plain";
src: url("./assets/TheSansB-W5Plain.woff2") format("woff2"); src: url("./assets/TheSansB-W5Plain.woff2") format("woff2");
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "TheSans Plain"; font-family: "TheSansB W5 Plain";
src: url("./assets/TheSansB-W5PlainItalic.woff2") format("woff2"); src: url("./assets/TheSansB-W5PlainItalic.woff2") format("woff2");
font-style: italic; font-style: italic;
} }
@font-face { @font-face {
font-family: "TheSans SemiBold"; font-family: "TheSansB W6 SemiBold";
src: url("./assets/TheSansB-W6SemiBold.woff2") format("woff2"); src: url("./assets/TheSansB-W6SemiBold.woff2") format("woff2");
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "TheSans SemiBold"; font-family: "TheSansB W6 SemiBold";
src: url("./assets/TheSansB-W6SemiBoldItalic.woff2") format("woff2"); src: url("./assets/TheSansB-W6SemiBoldItalic.woff2") format("woff2");
font-style: italic; font-style: italic;
} }
@font-face {
font-family: "TheSansB W7 Bold";
src: url("./assets/TheSansB-W7Bold.woff2") format("woff2");
font-style: normal;
}
@font-face {
font-family: "TheSansB W7 Bold";
src: url("./assets/TheSansB-W7BoldItalic.woff2") format("woff2");
font-style: italic;
}
@font-face {
font-family: "TheSansB W8 ExtraBold";
src: url("./assets/TheSansB-W8ExtraBold.woff2") format("woff2");
font-style: normal;
}
@font-face {
font-family: "TheSansB W8 ExtraBold";
src: url("./assets/TheSansB-W8ExtraBoldItalic.woff2") format("woff2");
font-style: italic;
}
@font-face {
font-family: "TheSansB W9 Black";
src: url("./assets/TheSansB-W9Black.woff2") format("woff2");
font-style: normal;
}
@font-face {
font-family: "TheSansB W9 Black";
src: url("./assets/TheSansB-W9BlackItalic.woff2") format("woff2");
font-style: italic;
}
@theme { @theme {
/* Colors */ /* Colors */
--color-primary: #05305d; --color-primary: #05305d;
@ -161,50 +197,50 @@
--color-su-primary: #002f5f; --color-su-primary: #002f5f;
--color-su-primary-80: #33587f; --color-su-primary-80: #33587f;
--bottom-nav-height: 4.5rem; --bottom-nav-height: 4.5rem;
font-family: 'TheSans', system-ui, Avenir, Helvetica, Arial, sans-serif; font-family: "TheSans", system-ui, Avenir, Helvetica, Arial, sans-serif;
background-color: #ffffff; background-color: #ffffff;
color: #000000; color: #000000;
} }
/* Text styles - Body */ /* Text styles - Body */
.body-light-sm { .body-light-sm {
font-family: "TheSans Light", "TheSans", system-ui, sans-serif; font-family: "TheSansB W3 Light", system-ui, sans-serif;
font-size: 14px; font-size: 14px;
} }
.body-normal-md { .body-normal-md {
font-family: "TheSans SemiLight", "TheSans", system-ui, sans-serif; font-family: "TheSansB W4 SemiLight", system-ui, sans-serif;
font-size: 16px; font-size: 16px;
} }
.body-normal-lg { .body-normal-lg {
font-family: 'TheSans SemiLight', 'TheSans', system-ui, sans-serif; font-family: "TheSansB W4 SemiLight", system-ui, sans-serif;
font-size: 18px; font-size: 18px;
} }
.body-semibold-md { .body-semibold-md {
font-family: 'TheSans Plain', 'TheSans', system-ui, sans-serif; font-family: "TheSansB W5 Plain", system-ui, sans-serif;
font-size: 16px; font-size: 16px;
} }
.body-semibold-lg { .body-semibold-lg {
font-family: 'TheSans Plain', 'TheSans', system-ui, sans-serif; font-family: "TheSansB W5 Plain", system-ui, sans-serif;
font-size: 18px; font-size: 18px;
} }
.body-bold-md { .body-bold-md {
font-family: 'TheSans SemiBold', 'TheSans', system-ui, sans-serif; font-family: "TheSansB W6 SemiBold", system-ui, sans-serif;
font-size: 16px; font-size: 16px;
} }
.body-bold-lg { .body-bold-lg {
font-family: 'TheSans SemiBold', 'TheSans', system-ui, sans-serif; font-family: "TheSansB W6 SemiBold", system-ui, sans-serif;
font-size: 18px; font-size: 18px;
} }
/* Text styles - Heading */ /* Text styles - Heading */
.heading-semibold-lg { .heading-semibold-lg {
font-family: 'TheSans SemiBold', 'TheSans', system-ui, sans-serif; font-family: "TheSansB W6 SemiBold", system-ui, sans-serif;
font-size: 32px; font-size: 32px;
} }

View File

@ -1,34 +1,35 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from "react";
import Button from '../components/Button/Button'; import Button from "../components/Button/Button";
import TextInput from '../components/TextInput/TextInput'; import TextInput from "../components/TextInput/TextInput";
import ListItem from '../components/ListItem/ListItem'; import { SearchIcon } from "../components/Icon/Icon";
import SearchResultList from '../components/SearchResultList/SearchResultList'; import ListItem from "../components/ListItem/ListItem";
import Combobox from '../components/Combobox/Combobox'; import SearchResultList from "../components/SearchResultList/SearchResultList";
import ListCard from '../components/ListCard/ListCard'; import Combobox from "../components/Combobox/Combobox";
import ParticipantPicker from '../components/ParticipantPicker/ParticipantPicker'; import ListCard from "../components/ListCard/ListCard";
import ParticipantPicker from "../components/ParticipantPicker/ParticipantPicker";
const peopleOptions = [ const peopleOptions = [
{ value: '1', label: 'Lennart Johansson', subtitle: 'lejo1891' }, { value: "1", label: "Lennart Johansson", subtitle: "lejo1891" },
{ value: '2', label: 'Mats Rubarth', subtitle: 'matsrub1891' }, { value: "2", label: "Mats Rubarth", subtitle: "matsrub1891" },
{ value: '3', label: 'Daniel Tjernström', subtitle: 'datj1891' }, { value: "3", label: "Daniel Tjernström", subtitle: "datj1891" },
{ value: '4', label: 'Johan Mjällby', subtitle: 'jomj1891' }, { value: "4", label: "Johan Mjällby", subtitle: "jomj1891" },
{ value: '5', label: 'Krister Nordin', subtitle: 'krno1891' }, { value: "5", label: "Krister Nordin", subtitle: "krno1891" },
{ value: '6', label: 'Kurre Hamrin', subtitle: 'kuha1891' }, { value: "6", label: "Kurre Hamrin", subtitle: "kuha1891" },
]; ];
export default function ComponentLibrary() { export default function ComponentLibrary() {
const [darkMode, setDarkMode] = useState(() => { const [darkMode, setDarkMode] = useState(() => {
return document.documentElement.classList.contains('dark'); return document.documentElement.classList.contains("dark");
}); });
const [selectedPerson, setSelectedPerson] = useState<string>(''); const [selectedPerson, setSelectedPerson] = useState<string>("");
const [selectedPeople, setSelectedPeople] = useState<string[]>([]); const [selectedPeople, setSelectedPeople] = useState<string[]>([]);
const [participants, setParticipants] = useState<string[]>([]); const [participants, setParticipants] = useState<string[]>([]);
useEffect(() => { useEffect(() => {
if (darkMode) { if (darkMode) {
document.documentElement.classList.add('dark'); document.documentElement.classList.add("dark");
} else { } else {
document.documentElement.classList.remove('dark'); document.documentElement.classList.remove("dark");
} }
}, [darkMode]); }, [darkMode]);
@ -39,7 +40,7 @@ export default function ComponentLibrary() {
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Dark Mode</h2> <h2 className="mb-md">Dark Mode</h2>
<Button variant="primary" onClick={() => setDarkMode(!darkMode)}> <Button variant="primary" onClick={() => setDarkMode(!darkMode)}>
{darkMode ? 'Light Mode' : 'Dark Mode'} {darkMode ? "Light Mode" : "Dark Mode"}
</Button> </Button>
</section> </section>
@ -61,13 +62,27 @@ export default function ComponentLibrary() {
<Button size="lg">Large</Button> <Button size="lg">Large</Button>
</div> </div>
</section> </section>
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input Sizes</h2> <h2 className="mb-md">Text Input Sizes</h2>
<div className="flex flex-wrap items-center gap-md"> <div className="flex flex-wrap items-center gap-md">
<TextInput size="sm" placeholder="Small" /> <TextInput
<TextInput size="md" placeholder="Medium" /> size="sm"
<TextInput size="lg" placeholder="Large" /> placeholder="Small"
label="Small input"
hideLabel
/>
<TextInput
size="md"
placeholder="Medium"
label="Medium input"
hideLabel
/>
<TextInput
size="lg"
placeholder="Large"
label="Large input"
hideLabel
/>
</div> </div>
</section> </section>
@ -77,44 +92,23 @@ export default function ComponentLibrary() {
<TextInput <TextInput
size="sm" size="sm"
placeholder="Small with icon" placeholder="Small with icon"
icon={ Icon={SearchIcon}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> label="Small search"
<path hideLabel
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
}
/> />
<TextInput <TextInput
size="md" size="md"
placeholder="Medium with icon" placeholder="Medium with icon"
icon={ Icon={SearchIcon}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> label="Medium search"
<path hideLabel
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
}
/> />
<TextInput <TextInput
size="lg" size="lg"
placeholder="Large with icon" placeholder="Large with icon"
icon={ Icon={SearchIcon}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> label="Large search"
<path hideLabel
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
}
/> />
</div> </div>
</section> </section>
@ -122,24 +116,43 @@ export default function ComponentLibrary() {
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input States</h2> <h2 className="mb-md">Text Input States</h2>
<div className="flex flex-wrap items-center gap-md"> <div className="flex flex-wrap items-center gap-md">
<TextInput placeholder="Default" /> <TextInput placeholder="Default" label="Default state" hideLabel />
<TextInput placeholder="Error state" error /> <TextInput
placeholder="Error state"
error
label="Error state"
hideLabel
/>
</div> </div>
</section> </section>
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input With/Without Placeholder</h2> <h2 className="mb-md">Text Input With/Without Placeholder</h2>
<div className="flex flex-wrap items-center gap-md"> <div className="flex flex-wrap items-center gap-md">
<TextInput placeholder="Placeholder" /> <TextInput
<TextInput /> placeholder="Placeholder"
label="With placeholder"
hideLabel
/>
<TextInput label="Without placeholder" hideLabel />
</div> </div>
</section> </section>
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input Width Options</h2> <h2 className="mb-md">Text Input Width Options</h2>
<div className="flex flex-col gap-md"> <div className="flex flex-col gap-md">
<TextInput placeholder="Full width" fullWidth /> <TextInput
<TextInput placeholder="Custom width" customWidth="300px" /> placeholder="Full width"
fullWidth
label="Full width input"
hideLabel
/>
<TextInput
placeholder="Custom width"
customWidth="300px"
label="Custom width input"
hideLabel
/>
</div> </div>
</section> </section>
@ -154,8 +167,18 @@ export default function ComponentLibrary() {
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input with Label and Message</h2> <h2 className="mb-md">Text Input with Label and Message</h2>
<div className="flex flex-wrap items-start gap-md"> <div className="flex flex-wrap items-start gap-md">
<TextInput label="Email" placeholder="Enter your email" error message="This field is required" /> <TextInput
<TextInput label="Username" placeholder="Choose a username" error message="Must be at least 3 characters" /> label="Email"
placeholder="Enter your email"
error
message="This field is required"
/>
<TextInput
label="Username"
placeholder="Choose a username"
error
message="Must be at least 3 characters"
/>
</div> </div>
</section> </section>
@ -183,7 +206,7 @@ export default function ComponentLibrary() {
<div className="max-w-96"> <div className="max-w-96">
<SearchResultList <SearchResultList
options={peopleOptions} options={peopleOptions}
selectedValues={['3']} selectedValues={["3"]}
focusedIndex={1} focusedIndex={1}
noResultsText="Inga resultat" noResultsText="Inga resultat"
/> />
@ -193,10 +216,7 @@ export default function ComponentLibrary() {
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">SearchResultList - Empty</h2> <h2 className="mb-md">SearchResultList - Empty</h2>
<div className="max-w-96"> <div className="max-w-96">
<SearchResultList <SearchResultList options={[]} noResultsText="Inga resultat" />
options={[]}
noResultsText="Inga resultat"
/>
</div> </div>
</section> </section>
@ -211,7 +231,10 @@ export default function ComponentLibrary() {
label="Välj person" label="Välj person"
/> />
<p className="body-light-sm text-base-ink-placeholder"> <p className="body-light-sm text-base-ink-placeholder">
Selected: {selectedPerson ? peopleOptions.find((p) => p.value === selectedPerson)?.label : 'None'} Selected:{" "}
{selectedPerson
? peopleOptions.find((p) => p.value === selectedPerson)?.label
: "None"}
</p> </p>
</div> </div>
</section> </section>
@ -228,7 +251,12 @@ export default function ComponentLibrary() {
multiple multiple
/> />
<p className="body-light-sm text-base-ink-placeholder"> <p className="body-light-sm text-base-ink-placeholder">
Selected: {selectedPeople.length > 0 ? selectedPeople.map((v) => peopleOptions.find((p) => p.value === v)?.label).join(', ') : 'None'} Selected:{" "}
{selectedPeople.length > 0
? selectedPeople
.map((v) => peopleOptions.find((p) => p.value === v)?.label)
.join(", ")
: "None"}
</p> </p>
</div> </div>
</section> </section>
@ -236,21 +264,9 @@ export default function ComponentLibrary() {
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Combobox - Sizes</h2> <h2 className="mb-md">Combobox - Sizes</h2>
<div className="flex flex-wrap items-start gap-md"> <div className="flex flex-wrap items-start gap-md">
<Combobox <Combobox options={peopleOptions} placeholder="Small" size="sm" />
options={peopleOptions} <Combobox options={peopleOptions} placeholder="Medium" size="md" />
placeholder="Small" <Combobox options={peopleOptions} placeholder="Large" size="lg" />
size="sm"
/>
<Combobox
options={peopleOptions}
placeholder="Medium"
size="md"
/>
<Combobox
options={peopleOptions}
placeholder="Large"
size="lg"
/>
</div> </div>
</section> </section>

View File

@ -1,6 +1,6 @@
import { defineConfig } from 'vite'; import { defineConfig } from "vite";
import react from '@vitejs/plugin-react-swc'; import react from "@vitejs/plugin-react-swc";
import tailwindcss from '@tailwindcss/vite'; import tailwindcss from "@tailwindcss/vite";
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({