List components #31
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -8,3 +8,9 @@
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.jar binary
|
||||
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.ttf binary
|
||||
*.otf binary
|
||||
*.eot binary
|
||||
|
||||
117
frontend/package-lock.json
generated
117
frontend/package-lock.json
generated
@ -8,7 +8,7 @@
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"clsx": "^2.1.1",
|
||||
"openapi-fetch": "^0.13.5",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
@ -17,6 +17,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react-swc": "^3.8.0",
|
||||
@ -27,6 +28,7 @@
|
||||
"globals": "^15.15.0",
|
||||
"openapi-typescript": "^7.6.1",
|
||||
"prettier": "3.5.3",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.2.0"
|
||||
@ -64,6 +66,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -80,6 +83,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -96,6 +100,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -112,6 +117,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -128,6 +134,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -144,6 +151,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -160,6 +168,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -176,6 +185,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -192,6 +202,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -208,6 +219,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -224,6 +236,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -240,6 +253,7 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -256,6 +270,7 @@
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -272,6 +287,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -288,6 +304,7 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -304,6 +321,7 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -320,6 +338,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -336,6 +355,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -352,6 +372,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -368,6 +389,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -384,6 +406,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -400,6 +423,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -416,6 +440,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -432,6 +457,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -448,6 +474,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -681,6 +708,7 @@
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
@ -691,6 +719,7 @@
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
@ -701,6 +730,7 @@
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@ -710,12 +740,14 @@
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
@ -843,6 +875,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -856,6 +889,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -869,6 +903,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -882,6 +917,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -895,6 +931,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -908,6 +945,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -921,6 +959,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -934,6 +973,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -947,6 +987,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -960,6 +1001,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -973,6 +1015,7 @@
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -986,6 +1029,7 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -999,6 +1043,7 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1012,6 +1057,7 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1025,6 +1071,7 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1038,6 +1085,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1051,6 +1099,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1064,6 +1113,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1077,6 +1127,7 @@
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1090,6 +1141,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1326,6 +1378,7 @@
|
||||
"version": "4.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.16.tgz",
|
||||
"integrity": "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.4",
|
||||
@ -1341,6 +1394,7 @@
|
||||
"version": "4.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.16.tgz",
|
||||
"integrity": "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
@ -1367,6 +1421,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1383,6 +1438,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1399,6 +1455,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1415,6 +1472,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1431,6 +1489,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1447,6 +1506,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1463,6 +1523,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1479,6 +1540,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1495,6 +1557,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1519,6 +1582,7 @@
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@ -1535,6 +1599,7 @@
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.5.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -1545,6 +1610,7 @@
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.5.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -1554,6 +1620,7 @@
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.1.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -1563,6 +1630,7 @@
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.0.7",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -1574,6 +1642,7 @@
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -1583,6 +1652,7 @@
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
@ -1594,6 +1664,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1610,6 +1681,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -1623,6 +1695,7 @@
|
||||
"version": "4.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.16.tgz",
|
||||
"integrity": "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tailwindcss/node": "4.1.16",
|
||||
@ -1637,6 +1710,7 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
@ -2033,6 +2107,15 @@
|
||||
"dev": true,
|
||||
"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": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@ -2127,6 +2210,7 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -2136,6 +2220,7 @@
|
||||
"version": "5.18.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
|
||||
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
@ -2149,6 +2234,7 @@
|
||||
"version": "0.25.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
|
||||
"integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@ -2518,6 +2604,7 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -2558,6 +2645,7 @@
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/graphemer": {
|
||||
@ -2685,6 +2773,7 @@
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
|
||||
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
@ -2769,6 +2858,7 @@
|
||||
"version": "1.30.2",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
|
||||
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3"
|
||||
@ -2801,6 +2891,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2821,6 +2912,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2841,6 +2933,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2861,6 +2954,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2881,6 +2975,7 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2901,6 +2996,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2921,6 +3017,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2941,6 +3038,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2961,6 +3059,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -2981,6 +3080,7 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -3001,6 +3101,7 @@
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@ -3041,6 +3142,7 @@
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
@ -3094,6 +3196,7 @@
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -3269,6 +3372,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
@ -3298,6 +3402,7 @@
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -3457,6 +3562,7 @@
|
||||
"version": "4.38.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz",
|
||||
"integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.7"
|
||||
@ -3568,6 +3674,7 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -3603,12 +3710,14 @@
|
||||
"version": "4.1.16",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
|
||||
"integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
|
||||
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@ -3622,6 +3731,7 @@
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.4",
|
||||
@ -3638,6 +3748,7 @@
|
||||
"version": "6.4.5",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
|
||||
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
@ -3652,6 +3763,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@ -3770,6 +3882,7 @@
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
@ -3844,6 +3957,7 @@
|
||||
"version": "6.4.5",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz",
|
||||
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
@ -3858,6 +3972,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"clsx": "^2.1.1",
|
||||
"openapi-fetch": "^0.13.5",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
@ -21,6 +21,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@tailwindcss/vite": "^4.1.16",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@vitejs/plugin-react-swc": "^3.8.0",
|
||||
@ -31,6 +32,7 @@
|
||||
"globals": "^15.15.0",
|
||||
"openapi-typescript": "^7.6.1",
|
||||
"prettier": "3.5.3",
|
||||
"tailwindcss": "^4.1.16",
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.2.0"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
import type { ButtonHTMLAttributes, ReactNode } from "react";
|
||||
import clsx from "clsx";
|
||||
|
||||
export type ButtonVariant = "primary" | "secondary" | "red" | "green";
|
||||
export type ButtonSize = "sm" | "md" | "lg";
|
||||
@ -10,13 +11,36 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
}
|
||||
|
||||
const variantClasses: Record<ButtonVariant, string> = {
|
||||
primary:
|
||||
"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",
|
||||
secondary:
|
||||
"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: "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:
|
||||
"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",
|
||||
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",
|
||||
),
|
||||
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> = {
|
||||
@ -40,14 +64,12 @@ export default function Button({
|
||||
}: ButtonProps) {
|
||||
const baseClasses = "inline-flex items-center justify-center cursor-pointer";
|
||||
|
||||
const classes = [
|
||||
const classes = clsx(
|
||||
baseClasses,
|
||||
variantClasses[variant],
|
||||
sizeClasses[size],
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
);
|
||||
|
||||
return (
|
||||
<button className={classes} {...props}>
|
||||
|
||||
96
frontend/src/components/Icon/Icon.tsx
Normal file
96
frontend/src/components/Icon/Icon.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -1,113 +1,138 @@
|
||||
import type { InputHTMLAttributes, ReactNode, CSSProperties } from 'react';
|
||||
// isValidElement: checks if something is a React element (e.g., <svg>, <MyComponent />)
|
||||
// cloneElement: creates a copy of a React element with modified/additional props
|
||||
import { cloneElement, isValidElement } from 'react';
|
||||
import { useId, type InputHTMLAttributes } from "react";
|
||||
import clsx from "clsx";
|
||||
import type { IconComponent } from "../Icon/Icon";
|
||||
|
||||
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
|
||||
export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
||||
size?: TextInputSize;
|
||||
icon?: ReactNode;
|
||||
error?: boolean;
|
||||
fullWidth?: boolean;
|
||||
customWidth?: string;
|
||||
label?: string;
|
||||
message?: string;
|
||||
export interface TextInputProps
|
||||
extends Omit<InputHTMLAttributes<HTMLInputElement>, "size"> {
|
||||
size?: TextInputSize;
|
||||
Icon?: IconComponent;
|
||||
error?: boolean;
|
||||
fullWidth?: boolean;
|
||||
customWidth?: string;
|
||||
label: string;
|
||||
hideLabel?: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const heightClasses: Record<TextInputSize, string> = {
|
||||
sm: 'h-(--control-height-sm)',
|
||||
md: 'h-(--control-height-md)',
|
||||
lg: 'h-(--control-height-lg)',
|
||||
const wrapperSizeClasses: Record<TextInputSize, string> = {
|
||||
sm: clsx(
|
||||
"h-(--control-height-sm)",
|
||||
"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> = {
|
||||
sm: 'w-(--text-input-default-width-md)',
|
||||
md: 'w-(--text-input-default-width-md)',
|
||||
lg: 'w-(--text-input-default-width-lg)',
|
||||
const inputSizeClasses: Record<TextInputSize, string> = {
|
||||
sm: "body-normal-md",
|
||||
md: "body-normal-md",
|
||||
lg: "body-normal-lg",
|
||||
};
|
||||
|
||||
const radiusClasses: Record<TextInputSize, string> = {
|
||||
sm: 'rounded-(--border-radius-sm)',
|
||||
md: 'rounded-(--border-radius-sm)',
|
||||
lg: 'rounded-(--border-radius-md)',
|
||||
const iconContainerSizeClasses: Record<TextInputSize, string> = {
|
||||
sm: clsx("w-(--control-height-sm) h-(--control-height-sm)"),
|
||||
md: clsx("w-(--control-height-md) h-(--control-height-md)"),
|
||||
lg: clsx("w-(--control-height-lg) h-(--control-height-lg)"),
|
||||
};
|
||||
|
||||
const textClasses: Record<TextInputSize, string> = {
|
||||
sm: 'body-normal-md',
|
||||
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';
|
||||
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
|
||||
const defaultStateClasses =
|
||||
'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 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)]",
|
||||
);
|
||||
|
||||
const errorStateClasses =
|
||||
'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';
|
||||
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-sky-100 focus-within:outline-[length:var(--border-width-lg)]",
|
||||
);
|
||||
|
||||
export default function TextInput({
|
||||
size = 'md',
|
||||
icon,
|
||||
error = false,
|
||||
fullWidth = false,
|
||||
customWidth,
|
||||
label,
|
||||
message,
|
||||
className = '',
|
||||
...props
|
||||
size = "md",
|
||||
Icon,
|
||||
error = false,
|
||||
fullWidth = false,
|
||||
customWidth,
|
||||
label,
|
||||
hideLabel = false,
|
||||
message,
|
||||
className = "",
|
||||
...props
|
||||
}: TextInputProps) {
|
||||
const widthClass = fullWidth ? 'w-full' : widthClasses[size];
|
||||
const widthStyle = customWidth ? { width: customWidth } : undefined;
|
||||
const stateClasses = error ? errorStateClasses : defaultStateClasses;
|
||||
const inputPadding = icon ? 'pr-(--padding-md)' : 'px-(--padding-md)';
|
||||
const inputId = useId();
|
||||
const widthStyle = customWidth ? { width: customWidth } : undefined;
|
||||
const stateClasses = error ? errorStateClasses : defaultStateClasses;
|
||||
const inputPadding = Icon ? "pr-(--padding-md)" : "px-(--padding-md)";
|
||||
const showVisibleLabel = !hideLabel;
|
||||
|
||||
const inputField = (
|
||||
const inputField = (
|
||||
<div
|
||||
className={clsx(
|
||||
"flex items-center",
|
||||
baseClasses,
|
||||
wrapperSizeClasses[size],
|
||||
stateClasses,
|
||||
fullWidth && "w-full",
|
||||
className,
|
||||
)}
|
||||
style={widthStyle}
|
||||
>
|
||||
{Icon && (
|
||||
<div
|
||||
className={`flex items-center ${baseClasses} ${heightClasses[size]} ${widthClass} ${radiusClasses[size]} ${stateClasses} ${className}`}
|
||||
style={widthStyle}
|
||||
className={clsx(
|
||||
"flex items-center justify-center shrink-0 text-base-ink-placeholder",
|
||||
iconContainerSizeClasses[size],
|
||||
)}
|
||||
>
|
||||
{icon && (
|
||||
<div
|
||||
className="flex items-center justify-center shrink-0 text-base-ink-placeholder"
|
||||
style={iconContainerStyles[size]}
|
||||
>
|
||||
{isValidElement<{ style?: CSSProperties }>(icon)
|
||||
? cloneElement(icon, { style: iconStyles[size] })
|
||||
: icon}
|
||||
</div>
|
||||
)}
|
||||
<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]}`}
|
||||
{...props}
|
||||
/>
|
||||
<Icon size={size} />
|
||||
</div>
|
||||
);
|
||||
)}
|
||||
<input
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (!label && !message) {
|
||||
return inputField;
|
||||
}
|
||||
if (!showVisibleLabel && !message) {
|
||||
return inputField;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-(--spacing-sm)">
|
||||
{label && <label className="body-bold-md text-base-ink-strong">{label}</label>}
|
||||
{inputField}
|
||||
{message && <span className="body-light-sm text-base-ink-strong">{message}</span>}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-col gap-(--spacing-sm)">
|
||||
{showVisibleLabel && (
|
||||
<label htmlFor={inputId} className="body-bold-md text-base-ink-strong">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
{inputField}
|
||||
{message && (
|
||||
<span className="body-light-sm text-base-ink-strong">{message}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,65 +2,101 @@
|
||||
|
||||
/* TheSans Font Family */
|
||||
@font-face {
|
||||
font-family: "TheSans";
|
||||
src: url("./assets/TheSansB-W5Plain.woff2") format("woff2");
|
||||
font-family: "TheSansB W2 ExtraLight";
|
||||
src: url("./assets/TheSansB-W2ExtraLight.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans";
|
||||
src: url("./assets/TheSansB-W5PlainItalic.woff2") format("woff2");
|
||||
font-family: "TheSansB W2 ExtraLight";
|
||||
src: url("./assets/TheSansB-W2ExtraLightItalic.woff2") format("woff2");
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans Light";
|
||||
font-family: "TheSansB W3 Light";
|
||||
src: url("./assets/TheSansB-W3Light.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans Light";
|
||||
font-family: "TheSansB W3 Light";
|
||||
src: url("./assets/TheSansB-W3LightItalic.woff2") format("woff2");
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans SemiLight";
|
||||
font-family: "TheSansB W4 SemiLight";
|
||||
src: url("./assets/TheSansB-W4SemiLight.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans SemiLight";
|
||||
font-family: "TheSansB W4 SemiLight";
|
||||
src: url("./assets/TheSansB-W4SemiLightItalic.woff2") format("woff2");
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans Plain";
|
||||
font-family: "TheSansB W5 Plain";
|
||||
src: url("./assets/TheSansB-W5Plain.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans Plain";
|
||||
font-family: "TheSansB W5 Plain";
|
||||
src: url("./assets/TheSansB-W5PlainItalic.woff2") format("woff2");
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans SemiBold";
|
||||
font-family: "TheSansB W6 SemiBold";
|
||||
src: url("./assets/TheSansB-W6SemiBold.woff2") format("woff2");
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "TheSans SemiBold";
|
||||
font-family: "TheSansB W6 SemiBold";
|
||||
src: url("./assets/TheSansB-W6SemiBoldItalic.woff2") format("woff2");
|
||||
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 {
|
||||
/* Colors */
|
||||
--color-primary: #05305d;
|
||||
@ -161,50 +197,50 @@
|
||||
--color-su-primary: #002f5f;
|
||||
--color-su-primary-80: #33587f;
|
||||
--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;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Text styles - Body */
|
||||
.body-light-sm {
|
||||
font-family: "TheSans Light", "TheSans", system-ui, sans-serif;
|
||||
font-family: "TheSansB W3 Light", system-ui, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.body-normal-md {
|
||||
font-family: "TheSans SemiLight", "TheSans", system-ui, sans-serif;
|
||||
font-family: "TheSansB W4 SemiLight", system-ui, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.body-normal-lg {
|
||||
font-family: 'TheSans SemiLight', 'TheSans', system-ui, sans-serif;
|
||||
font-family: "TheSansB W4 SemiLight", system-ui, sans-serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.body-semibold-md {
|
||||
font-family: 'TheSans Plain', 'TheSans', system-ui, sans-serif;
|
||||
font-family: "TheSansB W5 Plain", system-ui, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.body-semibold-lg {
|
||||
font-family: 'TheSans Plain', 'TheSans', system-ui, sans-serif;
|
||||
font-family: "TheSansB W5 Plain", system-ui, sans-serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.body-bold-md {
|
||||
font-family: 'TheSans SemiBold', 'TheSans', system-ui, sans-serif;
|
||||
font-family: "TheSansB W6 SemiBold", system-ui, sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.body-bold-lg {
|
||||
font-family: 'TheSans SemiBold', 'TheSans', system-ui, sans-serif;
|
||||
font-family: "TheSansB W6 SemiBold", system-ui, sans-serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* Text styles - Heading */
|
||||
.heading-semibold-lg {
|
||||
font-family: 'TheSans SemiBold', 'TheSans', system-ui, sans-serif;
|
||||
font-family: "TheSansB W6 SemiBold", system-ui, sans-serif;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
|
||||
@ -1,359 +1,375 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import Button from '../components/Button/Button';
|
||||
import TextInput from '../components/TextInput/TextInput';
|
||||
import ListItem from '../components/ListItem/ListItem';
|
||||
import SearchResultList from '../components/SearchResultList/SearchResultList';
|
||||
import Combobox from '../components/Combobox/Combobox';
|
||||
import ListCard from '../components/ListCard/ListCard';
|
||||
import ParticipantPicker from '../components/ParticipantPicker/ParticipantPicker';
|
||||
import { useState, useEffect } from "react";
|
||||
import Button from "../components/Button/Button";
|
||||
import TextInput from "../components/TextInput/TextInput";
|
||||
import { SearchIcon } from "../components/Icon/Icon";
|
||||
import ListItem from "../components/ListItem/ListItem";
|
||||
import SearchResultList from "../components/SearchResultList/SearchResultList";
|
||||
import Combobox from "../components/Combobox/Combobox";
|
||||
import ListCard from "../components/ListCard/ListCard";
|
||||
import ParticipantPicker from "../components/ParticipantPicker/ParticipantPicker";
|
||||
|
||||
const peopleOptions = [
|
||||
{ value: '1', label: 'Lennart Johansson', subtitle: 'lejo1891' },
|
||||
{ value: '2', label: 'Mats Rubarth', subtitle: 'matsrub1891' },
|
||||
{ value: '3', label: 'Daniel Tjernström', subtitle: 'datj1891' },
|
||||
{ value: '4', label: 'Johan Mjällby', subtitle: 'jomj1891' },
|
||||
{ value: '5', label: 'Krister Nordin', subtitle: 'krno1891' },
|
||||
{ value: '6', label: 'Kurre Hamrin', subtitle: 'kuha1891' },
|
||||
{ value: "1", label: "Lennart Johansson", subtitle: "lejo1891" },
|
||||
{ value: "2", label: "Mats Rubarth", subtitle: "matsrub1891" },
|
||||
{ value: "3", label: "Daniel Tjernström", subtitle: "datj1891" },
|
||||
{ value: "4", label: "Johan Mjällby", subtitle: "jomj1891" },
|
||||
{ value: "5", label: "Krister Nordin", subtitle: "krno1891" },
|
||||
{ value: "6", label: "Kurre Hamrin", subtitle: "kuha1891" },
|
||||
];
|
||||
|
||||
export default function ComponentLibrary() {
|
||||
const [darkMode, setDarkMode] = useState(() => {
|
||||
return document.documentElement.classList.contains('dark');
|
||||
});
|
||||
const [selectedPerson, setSelectedPerson] = useState<string>('');
|
||||
const [selectedPeople, setSelectedPeople] = useState<string[]>([]);
|
||||
const [participants, setParticipants] = useState<string[]>([]);
|
||||
const [darkMode, setDarkMode] = useState(() => {
|
||||
return document.documentElement.classList.contains("dark");
|
||||
});
|
||||
const [selectedPerson, setSelectedPerson] = useState<string>("");
|
||||
const [selectedPeople, setSelectedPeople] = useState<string[]>([]);
|
||||
const [participants, setParticipants] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (darkMode) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}, [darkMode]);
|
||||
useEffect(() => {
|
||||
if (darkMode) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
}, [darkMode]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Component Library</h1>
|
||||
return (
|
||||
<>
|
||||
<h1>Component Library</h1>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Dark Mode</h2>
|
||||
<Button variant="primary" onClick={() => setDarkMode(!darkMode)}>
|
||||
{darkMode ? 'Light Mode' : 'Dark Mode'}
|
||||
</Button>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Dark Mode</h2>
|
||||
<Button variant="primary" onClick={() => setDarkMode(!darkMode)}>
|
||||
{darkMode ? "Light Mode" : "Dark Mode"}
|
||||
</Button>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Button Variants</h2>
|
||||
<div className="flex flex-wrap gap-md">
|
||||
<Button variant="primary">Primary</Button>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="red">Red</Button>
|
||||
<Button variant="green">Green</Button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Button Variants</h2>
|
||||
<div className="flex flex-wrap gap-md">
|
||||
<Button variant="primary">Primary</Button>
|
||||
<Button variant="secondary">Secondary</Button>
|
||||
<Button variant="red">Red</Button>
|
||||
<Button variant="green">Green</Button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Button Sizes</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<Button size="sm">Small</Button>
|
||||
<Button size="md">Medium</Button>
|
||||
<Button size="lg">Large</Button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Button Sizes</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<Button size="sm">Small</Button>
|
||||
<Button size="md">Medium</Button>
|
||||
<Button size="lg">Large</Button>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input Sizes</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput
|
||||
size="sm"
|
||||
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>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input Sizes</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput size="sm" placeholder="Small" />
|
||||
<TextInput size="md" placeholder="Medium" />
|
||||
<TextInput size="lg" placeholder="Large" />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input with Icon</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput
|
||||
size="sm"
|
||||
placeholder="Small with icon"
|
||||
Icon={SearchIcon}
|
||||
label="Small search"
|
||||
hideLabel
|
||||
/>
|
||||
<TextInput
|
||||
size="md"
|
||||
placeholder="Medium with icon"
|
||||
Icon={SearchIcon}
|
||||
label="Medium search"
|
||||
hideLabel
|
||||
/>
|
||||
<TextInput
|
||||
size="lg"
|
||||
placeholder="Large with icon"
|
||||
Icon={SearchIcon}
|
||||
label="Large search"
|
||||
hideLabel
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input with Icon</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput
|
||||
size="sm"
|
||||
placeholder="Small with icon"
|
||||
icon={
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
size="md"
|
||||
placeholder="Medium with icon"
|
||||
icon={
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
size="lg"
|
||||
placeholder="Large with icon"
|
||||
icon={
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input States</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput placeholder="Default" label="Default state" hideLabel />
|
||||
<TextInput
|
||||
placeholder="Error state"
|
||||
error
|
||||
label="Error state"
|
||||
hideLabel
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input States</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput placeholder="Default" />
|
||||
<TextInput placeholder="Error state" error />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input With/Without Placeholder</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput
|
||||
placeholder="Placeholder"
|
||||
label="With placeholder"
|
||||
hideLabel
|
||||
/>
|
||||
<TextInput label="Without placeholder" hideLabel />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input With/Without Placeholder</h2>
|
||||
<div className="flex flex-wrap items-center gap-md">
|
||||
<TextInput placeholder="Placeholder" />
|
||||
<TextInput />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input Width Options</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<TextInput
|
||||
placeholder="Full width"
|
||||
fullWidth
|
||||
label="Full width input"
|
||||
hideLabel
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Custom width"
|
||||
customWidth="300px"
|
||||
label="Custom width input"
|
||||
hideLabel
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input Width Options</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<TextInput placeholder="Full width" fullWidth />
|
||||
<TextInput placeholder="Custom width" customWidth="300px" />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input with Label</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<TextInput label="Email" placeholder="Enter your email" />
|
||||
<TextInput label="Password" placeholder="Enter password" error />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input with Label</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<TextInput label="Email" placeholder="Enter your email" />
|
||||
<TextInput label="Password" placeholder="Enter password" error />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input with Label and Message</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<TextInput
|
||||
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>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Text Input with Label and Message</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<TextInput 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>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">List Item</h2>
|
||||
<div className="max-w-96 border border-base-ink-soft rounded-(--border-radius-md) overflow-hidden">
|
||||
<ListItem title="Lennart Johansson" subtitle="lejo1891" />
|
||||
<ListItem title="Mats Rubarth" subtitle="matsrub1891" />
|
||||
<ListItem title="Daniel Tjernström" subtitle="datj1891" selected />
|
||||
<ListItem title="Johan Mjällby" subtitle="jomj1891" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">List Item</h2>
|
||||
<div className="max-w-96 border border-base-ink-soft rounded-(--border-radius-md) overflow-hidden">
|
||||
<ListItem title="Lennart Johansson" subtitle="lejo1891" />
|
||||
<ListItem title="Mats Rubarth" subtitle="matsrub1891" />
|
||||
<ListItem title="Daniel Tjernström" subtitle="datj1891" selected />
|
||||
<ListItem title="Johan Mjällby" subtitle="jomj1891" />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">List Item - Title Only</h2>
|
||||
<div className="max-w-96 border border-base-ink-soft rounded-(--border-radius-md) overflow-hidden">
|
||||
<ListItem title="Krister Nordin" />
|
||||
<ListItem title="Kurre Hamrin" selected />
|
||||
<ListItem title="Per Karlsson" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">List Item - Title Only</h2>
|
||||
<div className="max-w-96 border border-base-ink-soft rounded-(--border-radius-md) overflow-hidden">
|
||||
<ListItem title="Krister Nordin" />
|
||||
<ListItem title="Kurre Hamrin" selected />
|
||||
<ListItem title="Per Karlsson" />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">SearchResultList</h2>
|
||||
<div className="max-w-96">
|
||||
<SearchResultList
|
||||
options={peopleOptions}
|
||||
selectedValues={["3"]}
|
||||
focusedIndex={1}
|
||||
noResultsText="Inga resultat"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">SearchResultList</h2>
|
||||
<div className="max-w-96">
|
||||
<SearchResultList
|
||||
options={peopleOptions}
|
||||
selectedValues={['3']}
|
||||
focusedIndex={1}
|
||||
noResultsText="Inga resultat"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">SearchResultList - Empty</h2>
|
||||
<div className="max-w-96">
|
||||
<SearchResultList options={[]} noResultsText="Inga resultat" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">SearchResultList - Empty</h2>
|
||||
<div className="max-w-96">
|
||||
<SearchResultList
|
||||
options={[]}
|
||||
noResultsText="Inga resultat"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Single Select</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
value={selectedPerson}
|
||||
onChange={(v) => setSelectedPerson(v as string)}
|
||||
placeholder="Sök..."
|
||||
label="Välj person"
|
||||
/>
|
||||
<p className="body-light-sm text-base-ink-placeholder">
|
||||
Selected:{" "}
|
||||
{selectedPerson
|
||||
? peopleOptions.find((p) => p.value === selectedPerson)?.label
|
||||
: "None"}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Single Select</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
value={selectedPerson}
|
||||
onChange={(v) => setSelectedPerson(v as string)}
|
||||
placeholder="Sök..."
|
||||
label="Välj person"
|
||||
/>
|
||||
<p className="body-light-sm text-base-ink-placeholder">
|
||||
Selected: {selectedPerson ? peopleOptions.find((p) => p.value === selectedPerson)?.label : 'None'}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Multi Select</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
value={selectedPeople}
|
||||
onChange={(v) => setSelectedPeople(v as string[])}
|
||||
placeholder="Sök..."
|
||||
label="Välj personer"
|
||||
multiple
|
||||
/>
|
||||
<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"}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Multi Select</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
value={selectedPeople}
|
||||
onChange={(v) => setSelectedPeople(v as string[])}
|
||||
placeholder="Sök..."
|
||||
label="Välj personer"
|
||||
multiple
|
||||
/>
|
||||
<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'}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Sizes</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<Combobox options={peopleOptions} placeholder="Small" size="sm" />
|
||||
<Combobox options={peopleOptions} placeholder="Medium" size="md" />
|
||||
<Combobox options={peopleOptions} placeholder="Large" size="lg" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Sizes</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
placeholder="Small"
|
||||
size="sm"
|
||||
/>
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
placeholder="Medium"
|
||||
size="md"
|
||||
/>
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
placeholder="Large"
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Custom Width</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
placeholder="Sök..."
|
||||
customWidth="350px"
|
||||
/>
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
placeholder="Sök..."
|
||||
label="Full width"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">Combobox - Custom Width</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
placeholder="Sök..."
|
||||
customWidth="350px"
|
||||
/>
|
||||
<Combobox
|
||||
options={peopleOptions}
|
||||
placeholder="Sök..."
|
||||
label="Full width"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ListCard</h2>
|
||||
<div className="flex flex-col gap-md max-w-96">
|
||||
<ListCard title="Lennart Johansson" onRemove={() => {}} />
|
||||
<ListCard title="Mats Rubarth" onRemove={() => {}} />
|
||||
<ListCard title="Daniel Tjernström" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ListCard</h2>
|
||||
<div className="flex flex-col gap-md max-w-96">
|
||||
<ListCard title="Lennart Johansson" onRemove={() => {}} />
|
||||
<ListCard title="Mats Rubarth" onRemove={() => {}} />
|
||||
<ListCard title="Daniel Tjernström" />
|
||||
</div>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ParticipantPicker</h2>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Sök deltagare..."
|
||||
label="Välj deltagare"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ParticipantPicker</h2>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Sök deltagare..."
|
||||
label="Välj deltagare"
|
||||
/>
|
||||
</section>
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ParticipantPicker - Sizes</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Small"
|
||||
size="sm"
|
||||
/>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Medium"
|
||||
size="md"
|
||||
/>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Large"
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ParticipantPicker - Sizes</h2>
|
||||
<div className="flex flex-wrap items-start gap-md">
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Small"
|
||||
size="sm"
|
||||
/>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Medium"
|
||||
size="md"
|
||||
/>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Large"
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ParticipantPicker - Custom Width</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Sök..."
|
||||
customWidth="350px"
|
||||
/>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Sök..."
|
||||
label="Full width"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
<section className="mt-lg">
|
||||
<h2 className="mb-md">ParticipantPicker - Custom Width</h2>
|
||||
<div className="flex flex-col gap-md">
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Sök..."
|
||||
customWidth="350px"
|
||||
/>
|
||||
<ParticipantPicker
|
||||
options={peopleOptions}
|
||||
value={participants}
|
||||
onChange={setParticipants}
|
||||
placeholder="Sök..."
|
||||
label="Full width"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react-swc';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
plugins: [react(), tailwindcss()],
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user