From 398bb9df8da4ef0745d70f874c7dddfde0eb5b99 Mon Sep 17 00:00:00 2001 From: EmNudge <24513691+EmNudge@users.noreply.github.com> Date: Wed, 4 Feb 2026 00:55:21 -0500 Subject: [PATCH] Migrate from Solid.js to Vue 3 with Composition API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace Solid dependencies with Vue 3 and @vitejs/plugin-vue - Switch bundler from vite to rolldown-vite for faster builds - Convert all stores from createSignal/createMemo to ref/computed - Migrate all .tsx components to Vue SFCs with - + +
diff --git a/index.html b/index.html index 0ee1229..46d5de7 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,6 @@
- + diff --git a/package.json b/package.json index a80a459..82ee390 100644 --- a/package.json +++ b/package.json @@ -7,23 +7,21 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", - "typecheck": "tsc --noEmit", + "typecheck": "vue-tsc --noEmit", "format": "oxfmt --write src", "lint": "oxfmt --check src && oxlint src" }, "dependencies": { "@emnudge/unicode-query": "^0.0.1", - "solid-js": "^1.9.5", - "solid-styled": "^0.12.0", - "solid-icons": "^1.1.0" + "vue": "^3.5.0" }, "devDependencies": { + "@vitejs/plugin-vue": "^5.0.0", "oxfmt": "^0.28.0", "oxlint": "^0.16.0", "typescript": "^5.0.0", - "unplugin-solid-styled": "^0.12.0", - "vite": "^7.3.0", - "vite-plugin-solid": "^2.11.2" + "rolldown-vite": "^7.3.0", + "vue-tsc": "^2.0.0" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da33030..534b495 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,79 +11,31 @@ importers: '@emnudge/unicode-query': specifier: ^0.0.1 version: 0.0.1 - solid-icons: - specifier: ^1.1.0 - version: 1.2.0(solid-js@1.9.11) - solid-js: - specifier: ^1.9.5 - version: 1.9.11 - solid-styled: - specifier: ^0.12.0 - version: 0.12.0(solid-js@1.9.11) + vue: + specifier: ^3.5.0 + version: 3.5.27(typescript@5.9.3) devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.0.0 + version: 5.2.4(vite@7.3.0(lightningcss@1.31.1))(vue@3.5.27(typescript@5.9.3)) oxfmt: specifier: ^0.28.0 version: 0.28.0 oxlint: specifier: ^0.16.0 version: 0.16.12 + rolldown-vite: + specifier: ^7.3.0 + version: 7.3.1(esbuild@0.27.2) typescript: specifier: ^5.0.0 version: 5.9.3 - unplugin-solid-styled: - specifier: ^0.12.0 - version: 0.12.0(rollup@4.54.0)(solid-styled@0.12.0(solid-js@1.9.11))(vite@7.3.0(lightningcss@1.31.1)) - vite: - specifier: ^7.3.0 - version: 7.3.0(lightningcss@1.31.1) - vite-plugin-solid: - specifier: ^2.11.2 - version: 2.11.10(solid-js@1.9.11)(vite@7.3.0(lightningcss@1.31.1)) + vue-tsc: + specifier: ^2.0.0 + version: 2.2.12(typescript@5.9.3) packages: - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.29.0': - resolution: {integrity: sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.18.6': - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -92,36 +44,23 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} - engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} - engines: {node: '>=6.9.0'} + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} - engines: {node: '>=6.9.0'} + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} '@emnudge/unicode-query@0.0.1': resolution: {integrity: sha512-CHsoZCtQE+FsGJkpSoaMU554Q/EZXctQe/sf05AC4afMCr52ppFzUXLcjj1ceOJwGyAiUsHJicuqNZFKPWHOZA==} @@ -282,21 +221,18 @@ packages: cpu: [x64] os: [win32] - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + + '@oxc-project/runtime@0.101.0': + resolution: {integrity: sha512-t3qpfVZIqSiLQ5Kqt/MC4Ge/WCOGrrcagAdzTcDaggupjiGxUx4nJF2v6wUCXWSzWHn5Ns7XLv13fCJEwCOERQ==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@oxc-project/types@0.101.0': + resolution: {integrity: sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ==} '@oxfmt/darwin-arm64@0.28.0': resolution: {integrity: sha512-jmUfF7cNJPw57bEK7sMIqrYRgn4LH428tSgtgLTCtjuGuu1ShREyrkeB7y8HtkXRfhBs4lVY+HMLhqElJvZ6ww==} @@ -378,14 +314,85 @@ packages: cpu: [x64] os: [win32] - '@rollup/pluginutils@5.3.0': - resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + '@rolldown/binding-android-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-yIsKqMz0CtRnVa6x3Pa+mzTihr4Ty+Z6HfPbZ7RVbk1Uxnco4+CUn7Qbm/5SBol1JD/7nvY8rphAgyAi7Lj6Vg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.53': + resolution: {integrity: sha512-GTXe+mxsCGUnJOFMhfGWmefP7Q9TpYUseHvhAhr21nCTgdS8jPsvirb0tJwM3lN0/u/cg7bpFNa16fQrjKrCjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': + resolution: {integrity: sha512-9Tmp7bBvKqyDkMcL4e089pH3RsjD3SUungjmqWtyhNOxoQMh0fSmINTyYV8KXtE+JkxYMPWvnEt+/mfpVCkk8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': + resolution: {integrity: sha512-a1y5fiB0iovuzdbjUxa7+Zcvgv+mTmlGGC4XydVIsyl48eoxgaYkA3l9079hyTyhECsPq+mbr0gVQsFU11OJAQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-bpIGX+ov9PhJYV+wHNXl9rzq4F0QvILiURn0y0oepbQx+7stmQsKA0DhPGwmhfvF856wq+gbM8L92SAa/CBcLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-bGe5EBB8FVjHBR1mOLOPEFg1Lp3//7geqWkU5NIhxe+yH0W8FVrQ6WRYOap4SUTKdklD/dC4qPLREkMMQ855FA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-qL+63WKVQs1CMvFedlPt0U9PiEKJOAL/bsHMKUDS6Vp2Q+YAv/QLPu8rcvkfIMvQ0FPU2WL0aX4eWwF6e/GAnA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-VGl9JIGjoJh3H8Mb+7xnVqODajBmrdOOb9lxWXdcmxyI+zjB2sux69br0hZJDTyLJfvBoYm439zPACYbCjGRmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-B4iIserJXuSnNzA5xBLFUIjTfhNy7d9sq4FUMQY3GhQWGVhS2RWWzzDnkSU6MUt7/aHUrep0CdQfXUJI9D3W7A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': + resolution: {integrity: sha512-BUjAEgpABEJXilGq/BPh7jeU3WAJ5o15c1ZEgHaDWSz3LB881LQZnbNJHmUiM4d1JQWMYYyR1Y490IBHi2FPJg==} engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-s27uU7tpCWSjHBnxyVXHt3rMrQdJq5MHNv3BzsewCIroIw3DJFjMH1dzCPPMUFxnh1r52Nf9IJ/eWp6LDoyGcw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-cjWL/USPJ1g0en2htb4ssMjIycc36RvdQAx1WlXnS6DpULswiUTVXPDesTifSKYSyvx24E0YqQkEm0K/M2Z/AA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} '@rollup/rollup-android-arm-eabi@4.54.0': resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} @@ -497,83 +504,89 @@ packages: cpu: [x64] os: [win32] - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@volar/language-core@2.4.15': + resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==} - '@types/css-tree@2.3.11': - resolution: {integrity: sha512-aEokibJOI77uIlqoBOkVbaQGC9zII0A+JH1kcTNKW2CwyYWD8KM6qdo+4c77wD3wZOQfJuNWAr9M4hdk+YhDIg==} + '@volar/source-map@2.4.15': + resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@volar/typescript@2.4.15': + resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==} - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true + '@vue/compiler-core@3.5.27': + resolution: {integrity: sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==} - babel-plugin-jsx-dom-expressions@0.40.3: - resolution: {integrity: sha512-5HOwwt0BYiv/zxl7j8Pf2bGL6rDXfV6nUhLs8ygBX+EFJXzBPHM/euj9j/6deMZ6wa52Wb2PBaAV5U/jKwIY1w==} - peerDependencies: - '@babel/core': ^7.20.12 + '@vue/compiler-dom@3.5.27': + resolution: {integrity: sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==} + + '@vue/compiler-sfc@3.5.27': + resolution: {integrity: sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==} - babel-preset-solid@1.9.10: - resolution: {integrity: sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ==} + '@vue/compiler-ssr@3.5.27': + resolution: {integrity: sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/language-core@2.2.12': + resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==} peerDependencies: - '@babel/core': ^7.0.0 - solid-js: ^1.9.10 + typescript: '*' peerDependenciesMeta: - solid-js: + typescript: optional: true - baseline-browser-mapping@2.9.19: - resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} - hasBin: true + '@vue/reactivity@3.5.27': + resolution: {integrity: sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + '@vue/runtime-core@3.5.27': + resolution: {integrity: sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A==} + + '@vue/runtime-dom@3.5.27': + resolution: {integrity: sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg==} + + '@vue/server-renderer@3.5.27': + resolution: {integrity: sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA==} + peerDependencies: + vue: 3.5.27 + + '@vue/shared@3.5.27': + resolution: {integrity: sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==} - caniuse-lite@1.0.30001767: - resolution: {integrity: sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==} + alien-signals@1.0.13: + resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - css-tree@3.1.0: - resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - electron-to-chromium@1.5.283: - resolution: {integrity: sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==} - - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} esbuild@0.27.2: @@ -581,10 +594,6 @@ packages: engines: {node: '>=18'} hasBin: true - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -602,28 +611,8 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - html-entities@2.3.3: - resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} - - is-what@4.1.16: - resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} - engines: {node: '>=12.13'} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true lightningcss-android-arm64@1.31.1: @@ -696,27 +685,21 @@ packages: resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} engines: {node: '>= 12.0.0'} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - mdn-data@2.12.2: - resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} - merge-anything@5.1.7: - resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} - engines: {node: '>=12.13'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - oxfmt@0.28.0: resolution: {integrity: sha512-3+hhBqPE6Kp22KfJmnstrZbl+KdOVSEu1V0ABaFIg1rYLtrMgrupx9znnHgHLqKxAVHebjTdiCJDk30CXOt6cw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -727,8 +710,8 @@ packages: engines: {node: '>=8.*'} hasBin: true - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -741,43 +724,55 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - rollup@4.54.0: - resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + rolldown-vite@7.3.1: + resolution: {integrity: sha512-LYzdNAjRHhF2yA4JUQm/QyARyi216N2rpJ0lJZb8E9FU2y5v6Vk+xq/U4XBOxMefpWixT5H3TslmAHm1rqIq2w==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - - seroval-plugins@1.5.0: - resolution: {integrity: sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA==} - engines: {node: '>=10'} - peerDependencies: - seroval: ^1.0 - - seroval@1.5.0: - resolution: {integrity: sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==} - engines: {node: '>=10'} - - solid-icons@1.2.0: - resolution: {integrity: sha512-yjQxWQMi9l19P5Af9vjsvalTMDFPDL4XD6etrUdNxNVhFp1bMYF0SFwUa5VYDfJ7u0SF3Qkz/F7BZxqcLUwklw==} peerDependencies: - solid-js: '*' - - solid-js@1.9.11: - resolution: {integrity: sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==} + '@types/node': ^20.19.0 || >=22.12.0 + esbuild: ^0.27.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true - solid-refresh@0.6.3: - resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} - peerDependencies: - solid-js: ^1.3 + rolldown@1.0.0-beta.53: + resolution: {integrity: sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true - solid-styled@0.12.0: - resolution: {integrity: sha512-eoQlDScKmd1xCSf/3Lhz44kScI2lnumwa0YLCkrMbJmm3gZbHALK/0B4ZBD148VjUyjzs3r0cDfZ3QLcyeWT1A==} - engines: {node: '>=12'} - peerDependencies: - solid-js: ^1 + rollup@4.54.0: + resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} @@ -791,41 +786,14 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - unplugin-solid-styled@0.12.0: - resolution: {integrity: sha512-CRUMVL+Jjh5/aBCQU7IvA9e6MYWJJzbcmca2VRgY5XY9XTjk1shVSgRwAeEHXr7mTekQz6zuwd4fjE8YxB9o+Q==} - engines: {node: '>=10'} - peerDependencies: - solid-styled: '>=0.9' - vite: '>=3' - peerDependenciesMeta: - vite: - optional: true - - unplugin@2.3.11: - resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} - engines: {node: '>=18.12.0'} - - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - vite-plugin-solid@2.11.10: - resolution: {integrity: sha512-Yr1dQybmtDtDAHkii6hXuc1oVH9CPcS/Zb2jN/P36qqcrkNnVPsMTzQ06jyzFPFjj3U1IYKMVt/9ZqcwGCEbjw==} - peerDependencies: - '@testing-library/jest-dom': ^5.16.6 || ^5.17.0 || ^6.* - solid-js: ^1.7.2 - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - peerDependenciesMeta: - '@testing-library/jest-dom': - optional: true - vite@7.3.0: resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -866,132 +834,53 @@ packages: yaml: optional: true - vitefu@1.1.1: - resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-tsc@2.2.12: + resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.27: + resolution: {integrity: sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==} peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + typescript: '*' peerDependenciesMeta: - vite: + typescript: optional: true - webpack-virtual-modules@0.6.2: - resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - snapshots: - '@babel/code-frame@7.29.0': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.29.0': {} - - '@babel/core@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.29.0': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.28.6': - dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.18.6': - dependencies: - '@babel/types': 7.29.0 - - '@babel/helper-module-imports@7.28.6': - dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-plugin-utils@7.28.6': {} - '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.6': - dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - '@babel/parser@7.29.0': dependencies: '@babel/types': 7.29.0 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/types@7.29.0': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 - '@babel/template@7.28.6': + '@emnapi/core@1.8.1': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true - '@babel/traverse@7.29.0': + '@emnapi/runtime@1.8.1': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.0 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color + tslib: 2.8.1 + optional: true - '@babel/types@7.29.0': + '@emnapi/wasi-threads@1.1.0': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + tslib: 2.8.1 + optional: true '@emnudge/unicode-query@0.0.1': {} @@ -1073,24 +962,18 @@ snapshots: '@esbuild/win32-x64@0.27.2': optional: true - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/remapping@2.3.5': + '@napi-rs/wasm-runtime@1.1.1': dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true - '@jridgewell/resolve-uri@3.1.2': {} + '@oxc-project/runtime@0.101.0': {} - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@oxc-project/types@0.101.0': {} '@oxfmt/darwin-arm64@0.28.0': optional: true @@ -1140,13 +1023,48 @@ snapshots: '@oxlint/win32-x64@0.16.12': optional: true - '@rollup/pluginutils@5.3.0(rollup@4.54.0)': + '@rolldown/binding-android-arm64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.54.0 + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.53': {} '@rollup/rollup-android-arm-eabi@4.54.0': optional: true @@ -1214,79 +1132,117 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.54.0': optional: true - '@types/babel__core@7.20.5': + '@tybys/wasm-util@0.10.1': dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 + tslib: 2.8.1 + optional: true + + '@types/estree@1.0.8': {} - '@types/babel__generator@7.27.0': + '@vitejs/plugin-vue@5.2.4(vite@7.3.0(lightningcss@1.31.1))(vue@3.5.27(typescript@5.9.3))': dependencies: - '@babel/types': 7.29.0 + vite: 7.3.0(lightningcss@1.31.1) + vue: 3.5.27(typescript@5.9.3) - '@types/babel__template@7.4.4': + '@volar/language-core@2.4.15': dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 + '@volar/source-map': 2.4.15 + + '@volar/source-map@2.4.15': {} - '@types/babel__traverse@7.28.0': + '@volar/typescript@2.4.15': dependencies: - '@babel/types': 7.29.0 + '@volar/language-core': 2.4.15 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/compiler-core@3.5.27': + dependencies: + '@babel/parser': 7.29.0 + '@vue/shared': 3.5.27 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 - '@types/css-tree@2.3.11': {} + '@vue/compiler-dom@3.5.27': + dependencies: + '@vue/compiler-core': 3.5.27 + '@vue/shared': 3.5.27 - '@types/estree@1.0.8': {} + '@vue/compiler-sfc@3.5.27': + dependencies: + '@babel/parser': 7.29.0 + '@vue/compiler-core': 3.5.27 + '@vue/compiler-dom': 3.5.27 + '@vue/compiler-ssr': 3.5.27 + '@vue/shared': 3.5.27 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 - acorn@8.15.0: {} + '@vue/compiler-ssr@3.5.27': + dependencies: + '@vue/compiler-dom': 3.5.27 + '@vue/shared': 3.5.27 - babel-plugin-jsx-dom-expressions@0.40.3(@babel/core@7.29.0): + '@vue/compiler-vue2@2.7.16': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.18.6 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 - html-entities: 2.3.3 - parse5: 7.3.0 + de-indent: 1.0.2 + he: 1.2.0 - babel-preset-solid@1.9.10(@babel/core@7.29.0)(solid-js@1.9.11): + '@vue/language-core@2.2.12(typescript@5.9.3)': dependencies: - '@babel/core': 7.29.0 - babel-plugin-jsx-dom-expressions: 0.40.3(@babel/core@7.29.0) + '@volar/language-core': 2.4.15 + '@vue/compiler-dom': 3.5.27 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.27 + alien-signals: 1.0.13 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 optionalDependencies: - solid-js: 1.9.11 + typescript: 5.9.3 - baseline-browser-mapping@2.9.19: {} + '@vue/reactivity@3.5.27': + dependencies: + '@vue/shared': 3.5.27 + + '@vue/runtime-core@3.5.27': + dependencies: + '@vue/reactivity': 3.5.27 + '@vue/shared': 3.5.27 + + '@vue/runtime-dom@3.5.27': + dependencies: + '@vue/reactivity': 3.5.27 + '@vue/runtime-core': 3.5.27 + '@vue/shared': 3.5.27 + csstype: 3.2.3 - browserslist@4.28.1: + '@vue/server-renderer@3.5.27(vue@3.5.27(typescript@5.9.3))': dependencies: - baseline-browser-mapping: 2.9.19 - caniuse-lite: 1.0.30001767 - electron-to-chromium: 1.5.283 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + '@vue/compiler-ssr': 3.5.27 + '@vue/shared': 3.5.27 + vue: 3.5.27(typescript@5.9.3) - caniuse-lite@1.0.30001767: {} + '@vue/shared@3.5.27': {} - convert-source-map@2.0.0: {} + alien-signals@1.0.13: {} - css-tree@3.1.0: + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: dependencies: - mdn-data: 2.12.2 - source-map-js: 1.2.1 + balanced-match: 1.0.2 csstype@3.2.3: {} - debug@4.4.3: - dependencies: - ms: 2.1.3 + de-indent@1.0.2: {} detect-libc@2.1.2: {} - electron-to-chromium@1.5.283: {} - - entities@6.0.1: {} + entities@7.0.1: {} esbuild@0.27.2: optionalDependencies: @@ -1317,8 +1273,6 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 - escalade@3.2.0: {} - estree-walker@2.0.2: {} fdir@6.5.0(picomatch@4.0.3): @@ -1328,17 +1282,7 @@ snapshots: fsevents@2.3.3: optional: true - gensync@1.0.0-beta.2: {} - - html-entities@2.3.3: {} - - is-what@4.1.16: {} - - js-tokens@4.0.0: {} - - jsesc@3.1.0: {} - - json5@2.2.3: {} + he@1.2.0: {} lightningcss-android-arm64@1.31.1: optional: true @@ -1389,22 +1333,18 @@ snapshots: lightningcss-win32-arm64-msvc: 1.31.1 lightningcss-win32-x64-msvc: 1.31.1 - lru-cache@5.1.1: + magic-string@0.30.21: dependencies: - yallist: 3.1.1 - - mdn-data@2.12.2: {} + '@jridgewell/sourcemap-codec': 1.5.5 - merge-anything@5.1.7: + minimatch@9.0.5: dependencies: - is-what: 4.1.16 + brace-expansion: 2.0.2 - ms@2.1.3: {} + muggle-string@0.4.1: {} nanoid@3.3.11: {} - node-releases@2.0.27: {} - oxfmt@0.28.0: dependencies: tinypool: 2.1.0 @@ -1429,9 +1369,7 @@ snapshots: '@oxlint/win32-arm64': 0.16.12 '@oxlint/win32-x64': 0.16.12 - parse5@7.3.0: - dependencies: - entities: 6.0.1 + path-browserify@1.0.1: {} picocolors@1.1.1: {} @@ -1443,6 +1381,38 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + rolldown-vite@7.3.1(esbuild@0.27.2): + dependencies: + '@oxc-project/runtime': 0.101.0 + fdir: 6.5.0(picomatch@4.0.3) + lightningcss: 1.31.1 + picomatch: 4.0.3 + postcss: 8.5.6 + rolldown: 1.0.0-beta.53 + tinyglobby: 0.2.15 + optionalDependencies: + esbuild: 0.27.2 + fsevents: 2.3.3 + + rolldown@1.0.0-beta.53: + dependencies: + '@oxc-project/types': 0.101.0 + '@rolldown/pluginutils': 1.0.0-beta.53 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-x64': 1.0.0-beta.53 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.53 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.53 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.53 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.53 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.53 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.53 + rollup@4.54.0: dependencies: '@types/estree': 1.0.8 @@ -1471,46 +1441,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.54.0 fsevents: 2.3.3 - semver@6.3.1: {} - - seroval-plugins@1.5.0(seroval@1.5.0): - dependencies: - seroval: 1.5.0 - - seroval@1.5.0: {} - - solid-icons@1.2.0(solid-js@1.9.11): - dependencies: - solid-js: 1.9.11 - - solid-js@1.9.11: - dependencies: - csstype: 3.2.3 - seroval: 1.5.0 - seroval-plugins: 1.5.0(seroval@1.5.0) - - solid-refresh@0.6.3(solid-js@1.9.11): - dependencies: - '@babel/generator': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/types': 7.29.0 - solid-js: 1.9.11 - transitivePeerDependencies: - - supports-color - - solid-styled@0.12.0(solid-js@1.9.11): - dependencies: - '@babel/core': 7.29.0 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@types/css-tree': 2.3.11 - browserslist: 4.28.1 - css-tree: 3.1.0 - lightningcss: 1.31.1 - solid-js: 1.9.11 - transitivePeerDependencies: - - supports-color - source-map-js@1.2.1: {} tinyglobby@0.2.15: @@ -1520,43 +1450,10 @@ snapshots: tinypool@2.1.0: {} - typescript@5.9.3: {} - - unplugin-solid-styled@0.12.0(rollup@4.54.0)(solid-styled@0.12.0(solid-js@1.9.11))(vite@7.3.0(lightningcss@1.31.1)): - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.54.0) - solid-styled: 0.12.0(solid-js@1.9.11) - unplugin: 2.3.11 - optionalDependencies: - vite: 7.3.0(lightningcss@1.31.1) - transitivePeerDependencies: - - rollup - - unplugin@2.3.11: - dependencies: - '@jridgewell/remapping': 2.3.5 - acorn: 8.15.0 - picomatch: 4.0.3 - webpack-virtual-modules: 0.6.2 - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 + tslib@2.8.1: + optional: true - vite-plugin-solid@2.11.10(solid-js@1.9.11)(vite@7.3.0(lightningcss@1.31.1)): - dependencies: - '@babel/core': 7.29.0 - '@types/babel__core': 7.20.5 - babel-preset-solid: 1.9.10(@babel/core@7.29.0)(solid-js@1.9.11) - merge-anything: 5.1.7 - solid-js: 1.9.11 - solid-refresh: 0.6.3(solid-js@1.9.11) - vite: 7.3.0(lightningcss@1.31.1) - vitefu: 1.1.1(vite@7.3.0(lightningcss@1.31.1)) - transitivePeerDependencies: - - supports-color + typescript@5.9.3: {} vite@7.3.0(lightningcss@1.31.1): dependencies: @@ -1570,10 +1467,20 @@ snapshots: fsevents: 2.3.3 lightningcss: 1.31.1 - vitefu@1.1.1(vite@7.3.0(lightningcss@1.31.1)): - optionalDependencies: - vite: 7.3.0(lightningcss@1.31.1) + vscode-uri@3.1.0: {} - webpack-virtual-modules@0.6.2: {} + vue-tsc@2.2.12(typescript@5.9.3): + dependencies: + '@volar/typescript': 2.4.15 + '@vue/language-core': 2.2.12(typescript@5.9.3) + typescript: 5.9.3 - yallist@3.1.1: {} + vue@3.5.27(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.27 + '@vue/compiler-sfc': 3.5.27 + '@vue/runtime-dom': 3.5.27 + '@vue/server-renderer': 3.5.27(vue@3.5.27(typescript@5.9.3)) + '@vue/shared': 3.5.27 + optionalDependencies: + typescript: 5.9.3 diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 8ff90b5..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { onMount, Show } from "solid-js"; -import { css } from "solid-styled"; -import { hasFirstLoadedSig, selectedCodepointSig, searchModeSig, resultsSig } from "$stores"; - -import { GitHubIcon } from "$icons/github"; -import { ResultsContainer } from "$components/table-container/ResultsContainer"; -import { EasySearch } from "$components/EasySearch"; -import { Header } from "$components/Header"; -import { AdvancedSearch } from "$components/advanced-search/AdvancedSearch"; -import { Loader } from "$components/Loader"; -import { InfoContainer } from "$components/table-container/InfoContainer"; - -export default function App() { - onMount(() => { - import("./queryProxy"); - }); - - css` - :global(body) { - overflow-y: scroll; - } - main { - text-align: center; - margin: 0 auto; - width: 600px; - max-width: 100%; - } - @media (min-width: 640px) { - main { - max-width: none; - } - } - .content.middle { - display: grid; - grid-template-columns: minmax(500px, auto) 600px 500px; - justify-content: center; - } - @media (max-width: 1600px) { - .content.middle { - display: grid; - grid-template-columns: minmax(500px, auto) 600px; - } - } - aside div { - position: fixed; - width: 400px; - overflow-y: auto; - height: 100vh; - } - `; - - return ( - <> -
- - - -
-
- - -
- - -
- - }> - -
-

No results fit that query :/

- - } - > - -
-
-
-
- -
- - ); -} diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..1c973e5 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,90 @@ + + + + + + + diff --git a/src/components/EasySearch.tsx b/src/components/EasySearch.tsx deleted file mode 100644 index 2e2dcb8..0000000 --- a/src/components/EasySearch.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { createSignal, onMount, onCleanup, Show } from "solid-js"; -import { css } from "solid-styled"; -import { easySearchSig, setEasySearchSig } from "$stores"; -import { debounce } from "$utils/debounce"; - -interface EasySearchProps { - onSearch?: () => void; -} - -export function EasySearch(props: EasySearchProps) { - const [error, setError] = createSignal(""); - let inputEl: HTMLInputElement | undefined; - - const search = debounce(() => props.onSearch?.(), 100); - - function hasRegexError(text: string) { - const regexRes = text.match(/\/(.+?)\/([a-z]+)?/); - if (!regexRes) return false; - - const [, regStr, flags] = regexRes; - - try { - new RegExp(regStr!, flags); - return false; - } catch { - return true; - } - } - - function trySearch() { - if (hasRegexError(easySearchSig())) { - setError("Regex Error. Please verify search text."); - return; - } - setError(""); - search(); - } - - function handleKeyDown(e: KeyboardEvent) { - const inputIsFocused = document.activeElement === inputEl; - if (inputIsFocused) return; - - if (e.key.length > 1 || /\p{C}/u.test(e.key)) return; - if (e.ctrlKey || e.altKey || e.metaKey) return; - - if (e.key === " ") { - e.preventDefault(); - return; - } - - inputEl?.focus(); - } - - onMount(() => { - window.addEventListener("keydown", handleKeyDown); - onCleanup(() => window.removeEventListener("keydown", handleKeyDown)); - }); - - css` - input { - width: 90%; - margin: 0 auto; - border: 1px solid transparent; - box-shadow: -3px 4px 2px #0000000f; - } - :global(:root[data-theme="dark"]) input { - background-color: #151515; - color: white; - border: 1px solid #6b6b6b; - box-shadow: 1px 1px 1px 2px #0000006e; - } - input::-webkit-input-placeholder { - opacity: 0.5; - } - input:focus { - outline: 1px solid #a5b5ff; - box-shadow: -3px 4px 2px #894aff0f; - } - `; - - return ( -
{ - e.preventDefault(); - trySearch(); - }} - > - { - setEasySearchSig(e.currentTarget.value); - trySearch(); - }} - /> - - -
- {error()} -
-
- ); -} diff --git a/src/components/EasySearch.vue b/src/components/EasySearch.vue new file mode 100644 index 0000000..be95f4c --- /dev/null +++ b/src/components/EasySearch.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/src/components/Header.tsx b/src/components/Header.tsx deleted file mode 100644 index f5c600d..0000000 --- a/src/components/Header.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { Show } from "solid-js"; -import { css } from "solid-styled"; -import { searchModeSig, setSearchModeSig } from "$stores"; -import { FilterIcon } from "$icons/filter"; -import { ThemeSwitcher } from "./ThemeSwitcher"; - -export function Header() { - function toggleSearchMode() { - setSearchModeSig((mode: "simple" | "advanced") => - mode === "advanced" ? "simple" : "advanced", - ); - } - - css` - header { - display: flex; - justify-content: space-between; - max-width: 600px; - margin: 20px auto; - display: flex; - align-items: center; - } - h1 { - font-size: 1.7em; - } - .buttons { - display: flex; - } - button { - cursor: pointer; - margin: 0; - z-index: 2; - padding: 10px 20px; - } - .active { - color: var(--hsl-bg); - background: var(--hsl); - transition: 0.25s; - } - button:focus { - border: 1px solid var(--hsl); - } - `; - - return ( -
-
-

Unicode Lookup

- Advanced Search}> -

Simple Search

-
-
- -
- - -
-
- ); -} diff --git a/src/components/Header.vue b/src/components/Header.vue new file mode 100644 index 0000000..7d343c5 --- /dev/null +++ b/src/components/Header.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx deleted file mode 100644 index 1ff9a6a..0000000 --- a/src/components/Loader.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { createSignal, onMount, onCleanup, For } from "solid-js"; -import { css } from "solid-styled"; - -export function Loader() { - const [str, setStr] = createSignal("xxxxxx"); - - onMount(() => { - const id = setInterval(() => { - const codepoint = Math.floor(Math.random() * 0xfff); - const char = String.fromCodePoint(codepoint); - - const index = Math.floor(Math.random() * str().length); - setStr((s) => s.slice(0, index) + char + s.slice(index + 1)); - }, 50); - - onCleanup(() => clearInterval(id)); - }); - - css` - section { - text-align: center; - padding-top: 50px; - } - .chars span { - display: inline-block; - width: 25px; - text-align: center; - line-height: 1em; - } - `; - - return ( -
-

Fetching Data

-
-
- {(char) => {char}} -
-
- ); -} diff --git a/src/components/Loader.vue b/src/components/Loader.vue new file mode 100644 index 0000000..4765865 --- /dev/null +++ b/src/components/Loader.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/src/components/ThemeSwitcher.tsx b/src/components/ThemeSwitcher.tsx deleted file mode 100644 index e0eba52..0000000 --- a/src/components/ThemeSwitcher.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { createSignal, onMount, onCleanup } from "solid-js"; -import { css } from "solid-styled"; -import { SunIcon } from "$icons/sun"; - -export function ThemeSwitcher() { - const [isDarkTheme, setIsDarkTheme] = createSignal(false); - - const updateTheme = (darkTheme: boolean) => { - const newTheme = darkTheme ? "dark" : "light"; - document.documentElement.dataset.theme = newTheme; - localStorage.setItem("color-scheme", newTheme); - }; - - const getColorSchemeLS = () => localStorage.getItem("color-scheme") as "dark" | "light" | null; - - onMount(() => { - const colorSchemeMedia = window.matchMedia("(prefers-color-scheme: dark)"); - const onColorSchemeChange = (e: MediaQueryListEvent) => { - if (getColorSchemeLS()) return; - setIsDarkTheme(e.matches); - }; - colorSchemeMedia.addEventListener("change", onColorSchemeChange); - - const colorSchemeLS = getColorSchemeLS(); - if (colorSchemeLS) { - setIsDarkTheme(colorSchemeLS === "dark"); - } else { - const isSystemDarkTheme = window.matchMedia("(prefers-color-scheme: dark)").matches; - setIsDarkTheme(isSystemDarkTheme); - } - - updateTheme(isDarkTheme()); - - onCleanup(() => colorSchemeMedia.removeEventListener("change", onColorSchemeChange)); - }); - - const toggleTheme = () => { - const newValue = !isDarkTheme(); - setIsDarkTheme(newValue); - updateTheme(newValue); - }; - - css` - button { - cursor: pointer; - margin: 0; - background: none; - border: none; - padding: 0 10px; - opacity: 0.6; - } - button:active { - transform: scale(0.9); - } - `; - - return ( - - ); -} diff --git a/src/components/ThemeSwitcher.vue b/src/components/ThemeSwitcher.vue new file mode 100644 index 0000000..eeb34e7 --- /dev/null +++ b/src/components/ThemeSwitcher.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/src/components/advanced-search/AdvancedSearch.tsx b/src/components/advanced-search/AdvancedSearch.tsx deleted file mode 100644 index b4361b1..0000000 --- a/src/components/advanced-search/AdvancedSearch.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { onMount, For } from "solid-js"; -import { css } from "solid-styled"; -import { BoxSet } from "./BoxSet"; -import { Button } from "./Button"; -import { boxesSig, setBoxesSig, getNewBox, type Box } from "$stores"; - -interface AdvancedSearchProps { - onSearch?: () => void; -} - -export function AdvancedSearch(_props: AdvancedSearchProps) { - const onClose = (index: number) => () => { - setBoxesSig((boxes: Box[]) => [...boxes.slice(0, index), ...boxes.slice(index + 1)]); - }; - - const updateBox = (index: number) => (box: Box) => { - setBoxesSig((boxes: Box[]) => [...boxes.slice(0, index), box, ...boxes.slice(index + 1)]); - }; - - onMount(() => { - if (boxesSig().length === 0) { - setBoxesSig([getNewBox()]); - } - }); - - css` - .buttons { - display: flex; - justify-content: flex-end; - } - .buttons :global(*) { - margin: 5px; - } - `; - - return ( - <> -
e.preventDefault()}> - - {(box, i) => } - -
- -
- -
- - ); -} diff --git a/src/components/advanced-search/AdvancedSearch.vue b/src/components/advanced-search/AdvancedSearch.vue new file mode 100644 index 0000000..0658245 --- /dev/null +++ b/src/components/advanced-search/AdvancedSearch.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/src/components/advanced-search/BoxContent.tsx b/src/components/advanced-search/BoxContent.tsx deleted file mode 100644 index fb9a083..0000000 --- a/src/components/advanced-search/BoxContent.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { createSignal, createEffect, createMemo } from "solid-js"; -import { Dynamic } from "solid-js/web"; -import { css } from "solid-styled"; -import { CloseIcon } from "$icons/close"; -import { Select } from "$lib/components"; -import { boxTypeMap } from "./box-types"; - -interface BoxContentProps { - name: string; - data: any; - hue?: number; - onClose?: () => void; - onNameChange?: (name: string) => void; - onDataChange?: (data: any) => void; -} - -export function BoxContent(props: BoxContentProps) { - const [localName, setLocalName] = createSignal(props.name); - - const componentNames = createMemo(() => [...boxTypeMap.keys()]); - const component = createMemo(() => boxTypeMap.get(localName())); - - createEffect(() => { - props.onNameChange?.(localName()); - props.onDataChange?.(undefined); - }); - - css` - .content { - padding: 40px; - display: grid; - justify-content: center; - margin-top: -1px; - position: relative; - } - button { - position: absolute; - top: 5px; - right: 10px; - background: none; - border: none; - font-size: 2em; - padding: 3px; - line-height: 0.8em; - margin: 0; - z-index: 2; - cursor: pointer; - } - hr { - margin: 10px -40px; - opacity: 0.4; - } - `; - - return ( -
- - - props.onChange?.(e.currentTarget.value)} - > - {(option) => } - - ); -} diff --git a/src/components/advanced-search/Dropdown.vue b/src/components/advanced-search/Dropdown.vue new file mode 100644 index 0000000..138992f --- /dev/null +++ b/src/components/advanced-search/Dropdown.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/src/components/advanced-search/Input.tsx b/src/components/advanced-search/Input.tsx deleted file mode 100644 index bdf9856..0000000 --- a/src/components/advanced-search/Input.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { createSignal, createEffect } from "solid-js"; -import { css } from "solid-styled"; - -type Mutator = (text: string) => any; -type Validator = (text: string, mutatedValue: any) => string | true; - -interface InputProps { - value?: any; - mutator?: Mutator; - validator?: Validator; - hue?: number; - placeholder?: string; - onChange?: (value: any) => void; -} - -export function Input(props: InputProps) { - const [text, setText] = createSignal(props.value ?? ""); - const [errText, setErrText] = createSignal(""); - - createEffect(() => { - const mutator = props.mutator ?? ((t: any) => t); - const validator = props.validator ?? (() => true); - - const newValue = mutator(text()); - const isValid = validator(text(), newValue); - - if (isValid === true) { - props.onChange?.(newValue); - setErrText(""); - } else { - setErrText(isValid); - } - }); - - css` - p { - color: red; - } - `; - - return ( - <> - setText(e.currentTarget.value)} - /> -

{errText()}

- - ); -} diff --git a/src/components/advanced-search/Input.vue b/src/components/advanced-search/Input.vue new file mode 100644 index 0000000..6709804 --- /dev/null +++ b/src/components/advanced-search/Input.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/src/components/advanced-search/NumberInput.tsx b/src/components/advanced-search/NumberInput.tsx deleted file mode 100644 index fde55c6..0000000 --- a/src/components/advanced-search/NumberInput.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { createSignal, createEffect } from "solid-js"; -import { css } from "solid-styled"; - -interface NumberInputProps { - value?: number; - type?: "dec" | "hex" | "bin"; - onChange?: (value: number) => void; -} - -function parseHex(hex: string) { - if (!/^[A-Fa-f0-9]+$/.test(hex)) return NaN; - return parseInt(hex, 16); -} - -function parseBin(bin: string) { - if (!/^[01]+$/.test(bin)) return NaN; - return parseInt(bin, 2); -} - -function getValue(type: string | undefined, text: string | number) { - if (type === "hex") return parseHex(String(text)); - if (type === "bin") return parseBin(String(text)); - return Number(text); -} - -function getText(type: string | undefined, value: number) { - if (type === "hex") return value.toString(16); - if (type === "bin") return value.toString(2); - return String(value); -} - -function* cycle(arr: T[]) { - while (true) { - yield* arr; - } -} - -export function NumberInput(props: NumberInputProps) { - const [currText, setCurrText] = createSignal(props.value ?? 0); - const [errText, setErrText] = createSignal(""); - const [type, setType] = createSignal<"dec" | "hex" | "bin">(props.type ?? "dec"); - - const typeIter = cycle(["hex", "bin", "dec"] as const); - - createEffect(() => { - const newVal = getValue(type(), currText()); - if (Number.isNaN(newVal)) { - setErrText("Invalid input"); - } else { - setErrText(""); - props.onChange?.(newVal); - } - }); - - function changeType() { - const nextType = typeIter.next().value; - if (nextType) { - setType(nextType); - const currentVal = getValue(type(), currText()); - if (!Number.isNaN(currentVal)) { - setCurrText(getText(nextType, currentVal)); - } - } - } - - css` - p { - margin: 0; - color: red; - } - `; - - return ( - <> -
- setCurrText(e.currentTarget.value)} - /> - -
-

{errText()}

- - ); -} diff --git a/src/components/advanced-search/NumberInput.vue b/src/components/advanced-search/NumberInput.vue new file mode 100644 index 0000000..71be22d --- /dev/null +++ b/src/components/advanced-search/NumberInput.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/components/advanced-search/box-types/BidiClass.tsx b/src/components/advanced-search/box-types/BidiClass.tsx deleted file mode 100644 index d1dc535..0000000 --- a/src/components/advanced-search/box-types/BidiClass.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { createSignal, createEffect } from "solid-js"; -import { BidiClassMap } from "$utils/unicode"; -import { Dropdown } from "../Dropdown"; - -interface BidiClassProps { - data?: string; - onDataChange?: (data: string) => void; -} - -const displayToBidiMap = new Map( - [...BidiClassMap.entries()].map(([className, displayName]) => { - return [`${className} (${displayName})`, className]; - }), -); -const names = [...displayToBidiMap.keys()]; - -export function BidiClass(props: BidiClassProps) { - const [displayName, setDisplayName] = createSignal(names[0]!); - - createEffect(() => { - const bidiClass = displayToBidiMap.get(displayName()); - if (bidiClass) { - props.onDataChange?.(bidiClass); - } - }); - - return ; -} diff --git a/src/components/advanced-search/box-types/BidiClass.vue b/src/components/advanced-search/box-types/BidiClass.vue new file mode 100644 index 0000000..1469d2f --- /dev/null +++ b/src/components/advanced-search/box-types/BidiClass.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/advanced-search/box-types/CodepointRange.tsx b/src/components/advanced-search/box-types/CodepointRange.tsx deleted file mode 100644 index 50b5185..0000000 --- a/src/components/advanced-search/box-types/CodepointRange.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { createSignal, createEffect } from "solid-js"; -import { NumberInput } from "../NumberInput"; - -interface CodepointRangeProps { - data?: { from: number; to: number }; - onDataChange?: (data: { from: number; to: number }) => void; -} - -export function CodepointRange(props: CodepointRangeProps) { - const [from, setFrom] = createSignal(props.data?.from ?? 0); - const [to, setTo] = createSignal(props.data?.to ?? 65535); - - createEffect(() => { - props.onDataChange?.({ from: from(), to: to() }); - }); - - return ( -
- From - - To - -
- ); -} diff --git a/src/components/advanced-search/box-types/CodepointRange.vue b/src/components/advanced-search/box-types/CodepointRange.vue new file mode 100644 index 0000000..e37939e --- /dev/null +++ b/src/components/advanced-search/box-types/CodepointRange.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/components/advanced-search/box-types/NameIncludes.tsx b/src/components/advanced-search/box-types/NameIncludes.tsx deleted file mode 100644 index 4187961..0000000 --- a/src/components/advanced-search/box-types/NameIncludes.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { createSignal, createEffect } from "solid-js"; -import { Input } from "../Input"; - -interface NameIncludesProps { - data?: string; - onDataChange?: (data: string) => void; -} - -export function NameIncludes(props: NameIncludesProps) { - const [text, setText] = createSignal(props.data ?? "symbol"); - - createEffect(() => { - props.onDataChange?.(text()); - }); - - function validator(text: string) { - if (/^[A-Za-z0-9 ()<>,-]+$/.test(text)) return true; - return "Impossible name"; - } - - return ; -} diff --git a/src/components/advanced-search/box-types/NameIncludes.vue b/src/components/advanced-search/box-types/NameIncludes.vue new file mode 100644 index 0000000..2f38c9b --- /dev/null +++ b/src/components/advanced-search/box-types/NameIncludes.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/components/advanced-search/box-types/RegexMatch.tsx b/src/components/advanced-search/box-types/RegexMatch.tsx deleted file mode 100644 index c674351..0000000 --- a/src/components/advanced-search/box-types/RegexMatch.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { createSignal, createEffect } from "solid-js"; -import { Input } from "../Input"; -import { Dropdown } from "../Dropdown"; - -type Data = { regex: RegExp; matchOn: "Character" | "Name" }; - -interface RegexMatchProps { - data?: Data; - onDataChange?: (data: Data) => void; -} - -export function getRegexFromString(text: string) { - const res = text.match(/^\/(.+)\/([A-Za-z]*)$/); - if (!res) return null; - - const [, regex, flags] = res; - try { - return new RegExp(regex!, flags); - } catch { - return null; - } -} - -export function RegexMatch(props: RegexMatchProps) { - const [regex, setRegex] = createSignal("/[A-Za-z]/u"); - const [matchOn, setMatchOn] = createSignal<"Character" | "Name">("Character"); - - createEffect(() => { - const regexObj = getRegexFromString(regex()); - if (regexObj) { - props.onDataChange?.({ regex: regexObj, matchOn: matchOn() }); - } - }); - - function validateRegex(text: string) { - if (getRegexFromString(text)) return true; - return "invalid regex"; - } - - return ( - <> - Regex - - - Match On - setMatchOn(v as "Character" | "Name")} - /> - - ); -} diff --git a/src/components/advanced-search/box-types/RegexMatch.vue b/src/components/advanced-search/box-types/RegexMatch.vue new file mode 100644 index 0000000..281c241 --- /dev/null +++ b/src/components/advanced-search/box-types/RegexMatch.vue @@ -0,0 +1,67 @@ + + + diff --git a/src/components/advanced-search/box-types/UnicodeBlock.tsx b/src/components/advanced-search/box-types/UnicodeBlock.tsx deleted file mode 100644 index 112687d..0000000 --- a/src/components/advanced-search/box-types/UnicodeBlock.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { createSignal, createEffect, createMemo } from "solid-js"; -import { Dropdown } from "../Dropdown"; -import { blockLookupSig } from "$stores"; - -interface UnicodeBlockProps { - data?: string; - onDataChange?: (data: string) => void; -} - -export function UnicodeBlock(props: UnicodeBlockProps) { - const blockNames = createMemo(() => blockLookupSig().map((block) => block.name)); - const [shouldSort, setShouldSort] = createSignal(true); - const [blockName, setBlockName] = createSignal(""); - - const blocks = createMemo(() => (shouldSort() ? [...blockNames()].sort() : blockNames())); - - // Initialize with first block name - createEffect(() => { - const names = blockNames(); - if (names.length > 0 && !blockName()) { - setBlockName(names[0]!); - } - }); - - createEffect(() => { - if (blockName()) { - props.onDataChange?.(blockName()); - } - }); - - return ( - <> - - - - - ); -} diff --git a/src/components/advanced-search/box-types/UnicodeBlock.vue b/src/components/advanced-search/box-types/UnicodeBlock.vue new file mode 100644 index 0000000..f0ac6af --- /dev/null +++ b/src/components/advanced-search/box-types/UnicodeBlock.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/components/advanced-search/box-types/UnicodePlane.tsx b/src/components/advanced-search/box-types/UnicodePlane.tsx deleted file mode 100644 index 688b8ef..0000000 --- a/src/components/advanced-search/box-types/UnicodePlane.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { createSignal, createEffect } from "solid-js"; -import { Dropdown } from "../Dropdown"; -import { planeMap } from "$utils/unicode"; - -interface UnicodePlaneProps { - data?: number; - onDataChange?: (data: number) => void; -} - -const nameToNumMap = new Map( - Array.from({ length: 17 }).map((_, i) => { - const name = planeMap.get(i) || "Unassigned"; - return [`(${i}) ${name}`, i]; - }), -); -const names = [...nameToNumMap.keys()]; - -export function UnicodePlane(props: UnicodePlaneProps) { - const [activeName, setActiveName] = createSignal(names[0]!); - - createEffect(() => { - const planeNum = nameToNumMap.get(activeName()); - if (planeNum !== undefined) { - props.onDataChange?.(planeNum); - } - }); - - return ; -} diff --git a/src/components/advanced-search/box-types/UnicodePlane.vue b/src/components/advanced-search/box-types/UnicodePlane.vue new file mode 100644 index 0000000..c537864 --- /dev/null +++ b/src/components/advanced-search/box-types/UnicodePlane.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/advanced-search/box-types/UnicodeProperty.tsx b/src/components/advanced-search/box-types/UnicodeProperty.tsx deleted file mode 100644 index fdcc706..0000000 --- a/src/components/advanced-search/box-types/UnicodeProperty.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { createSignal, createEffect, Show } from "solid-js"; -import { Dropdown } from "../Dropdown"; -import { properties, scripts } from "$utils/unicode"; - -interface UnicodePropertyProps { - data?: string; - onDataChange?: (data: string) => void; -} - -export function UnicodeProperty(props: UnicodePropertyProps) { - const [activeProperty, setActiveProperty] = createSignal(properties[0]!); - const [activeScript, setActiveScript] = createSignal(scripts[0]!); - - createEffect(() => { - if (activeProperty() === "Script") { - props.onDataChange?.(`Script=${activeScript()}`); - } else { - props.onDataChange?.(activeProperty()); - } - }); - - return ( - <> - - - - - - - ); -} diff --git a/src/components/advanced-search/box-types/UnicodeProperty.vue b/src/components/advanced-search/box-types/UnicodeProperty.vue new file mode 100644 index 0000000..958359c --- /dev/null +++ b/src/components/advanced-search/box-types/UnicodeProperty.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/advanced-search/box-types/index.ts b/src/components/advanced-search/box-types/index.ts index 719997a..3c903e8 100644 --- a/src/components/advanced-search/box-types/index.ts +++ b/src/components/advanced-search/box-types/index.ts @@ -1,13 +1,13 @@ -import { BidiClass } from "./BidiClass"; -import { CodepointRange } from "./CodepointRange"; -import { NameIncludes } from "./NameIncludes"; -import { RegexMatch } from "./RegexMatch"; -import { UnicodeBlock } from "./UnicodeBlock"; -import { UnicodePlane } from "./UnicodePlane"; -import { UnicodeProperty } from "./UnicodeProperty"; -import type { Component } from "solid-js"; +import BidiClass from "./BidiClass.vue"; +import CodepointRange from "./CodepointRange.vue"; +import NameIncludes from "./NameIncludes.vue"; +import RegexMatch from "./RegexMatch.vue"; +import UnicodeBlock from "./UnicodeBlock.vue"; +import UnicodePlane from "./UnicodePlane.vue"; +import UnicodeProperty from "./UnicodeProperty.vue"; +import type { Component } from "vue"; -const entries: [string, Component<{ data?: any; onDataChange?: (data: any) => void }>][] = [ +const entries: [string, Component][] = [ ["Codepoint Range", CodepointRange], ["Name Includes", NameIncludes], ["Regex Match", RegexMatch], diff --git a/src/components/table-container/InfoContainer.tsx b/src/components/table-container/InfoContainer.tsx deleted file mode 100644 index 5b2cf6d..0000000 --- a/src/components/table-container/InfoContainer.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { createMemo, For } from "solid-js"; -import { css } from "solid-styled"; -import { - getPropertiesForChar, - getPlaneForCodepoint, - getCodepointBlock, - Category, - getDecompFromStr, - BidiClassMap, -} from "$utils/unicode"; -import { blockLookupSig, symbolHtmlNamesMapSig } from "$stores"; - -import { CaseMapping } from "./info-tables/CaseMapping"; -import { Encoding } from "./info-tables/Encoding"; -import { NumberEquiv } from "./info-tables/NumberEquiv"; -import { HtmlEntities } from "./info-tables/HtmlEntities"; -import { Properties } from "./info-tables/Properties"; -import type { UnicodeMapData } from "@emnudge/unicode-query"; - -interface InfoContainerProps { - codepoint: number; - name: string; - info: UnicodeMapData; -} - -export function InfoContainer(props: InfoContainerProps) { - const category = createMemo( - () => `${props.info.category} (${Category[props.info.category as keyof typeof Category]})`, - ); - const charBlock = createMemo(() => getCodepointBlock(blockLookupSig(), props.codepoint)); - const planeData = createMemo(() => getPlaneForCodepoint(props.codepoint)); - - const decomposition = createMemo(() => - props.info.decompositionStr ? getDecompFromStr(props.info.decompositionStr) : null, - ); - const decompText = createMemo(() => - decomposition() != null - ? `<${decomposition()!.type}> ${decomposition()!.codepoints.join(" ")}` - : "", - ); - - const baseInfo = createMemo( - () => - [ - ["Bidi Class", `${props.info.bidiClass} (${BidiClassMap.get(props.info.bidiClass)})`], - !!props.info.oldName && - props.info.oldName !== props.name && ["Old Name", props.info.oldName], - ["Codepoint", props.codepoint], - ["Category", category()], - !!decompText() && ["Decomposition", decompText()], - [ - "Plane", - planeData().name - ? `${planeData().name} (Plane ${planeData().number}) ` - : `Plane ${planeData().number}`, - ], - ["Block", `${charBlock().name} (Range ${charBlock().range[0]}-${charBlock().range[1]}) `], - ].filter(Boolean) as [string, string][], - ); - - const properties = createMemo(() => getPropertiesForChar(String.fromCodePoint(props.codepoint))); - - const htmlEntityNames = createMemo(() => symbolHtmlNamesMapSig().get(props.codepoint) ?? []); - - css` - div.container { - border-radius: 8px; - padding: 20px; - } - .basic-info { - display: grid; - } - td { - padding: 5px; - } - td:nth-child(1) { - font-weight: bold; - } - tr:nth-child(odd) { - background-color: var(--bg-offset); - } - h1 { - text-align: center; - } - .display-box { - background-color: var(--bg-offset); - height: 64px; - width: 64px; - font-size: 3em; - border: 1px solid grey; - display: flex; - align-items: center; - justify-content: center; - margin: 12px 0; - } - header { - display: flex; - flex-direction: column; - align-items: center; - } - `; - - return ( -
-
-

{props.name}

-
- {String.fromCodePoint(props.codepoint)} -
-
- - - - {([name, value]) => ( - - - - - )} - - -
{name}{value}
- - - - - - -
- -
- ); -} diff --git a/src/components/table-container/InfoContainer.vue b/src/components/table-container/InfoContainer.vue new file mode 100644 index 0000000..40f7a0c --- /dev/null +++ b/src/components/table-container/InfoContainer.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/src/components/table-container/ResultsContainer.tsx b/src/components/table-container/ResultsContainer.tsx deleted file mode 100644 index 5752c4c..0000000 --- a/src/components/table-container/ResultsContainer.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { createSignal, createMemo, createEffect, onCleanup, For } from "solid-js"; -import { css } from "solid-styled"; -import { ResultsRow } from "./ResultsRow"; -import { activeIndexSig, setActiveIndexSig, resultsSig } from "$stores"; - -export function ResultsContainer() { - const [resultsNum, setResultsNum] = createSignal(50); - let intersectionObserver: HTMLDivElement | undefined; - - // Reset resultsNum when results change - createEffect(() => { - resultsSig(); - setResultsNum(50); - }); - - // Intersection observer for infinite scroll - createEffect(() => { - if (!intersectionObserver) return; - - const intObserver = new IntersectionObserver( - (entries) => { - const entry = entries[0]; - if (!entry?.isIntersecting) return; - - if (resultsSig().length > resultsNum()) { - setResultsNum((n) => n + 50); - } - }, - { - rootMargin: "0px", - threshold: 0.8, - }, - ); - - intObserver.observe(intersectionObserver); - onCleanup(() => intObserver.disconnect()); - }); - - const shownResults = createMemo(() => resultsSig().slice(0, resultsNum())); - - function handleFocus(e: FocusEvent) { - const index = (e.target as Element).querySelector("img")?.dataset.index; - if (!index) return; - setActiveIndexSig(Number(index)); - } - - function handleKeydown(e: KeyboardEvent) { - if (e.key === "ArrowDown" && activeIndexSig() < resultsSig().length - 1) { - setActiveIndexSig((i: number) => i + 1); - } else if (e.key === "ArrowUp" && activeIndexSig() >= 0) { - setActiveIndexSig((i: number) => i - 1); - } - } - - css` - .table { - text-align: left; - } - p { - text-align: left; - margin: 0; - opacity: 0.7; - font-size: 0.8rem; - } - .intersection-observer { - height: 5rem; - } - `; - - return ( - <> -

- {resultsSig().length} result{resultsSig().length > 1 ? "s" : ""} -

- -
- - {([codepoint, info], i) => } - - -
-
- - ); -} diff --git a/src/components/table-container/ResultsContainer.vue b/src/components/table-container/ResultsContainer.vue new file mode 100644 index 0000000..29f899e --- /dev/null +++ b/src/components/table-container/ResultsContainer.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/src/components/table-container/ResultsRow.tsx b/src/components/table-container/ResultsRow.tsx deleted file mode 100644 index 1824ff7..0000000 --- a/src/components/table-container/ResultsRow.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { createEffect } from "solid-js"; -import { css } from "solid-styled"; -import { activeIndexSig, setActiveIndexSig } from "$stores"; -import { getCodepoint } from "$utils/char"; -import type { UnicodeMapData } from "@emnudge/unicode-query"; - -interface ResultsRowProps { - codepoint: number; - info: UnicodeMapData; - index: number; -} - -export function ResultsRow(props: ResultsRowProps) { - let rowEl: HTMLButtonElement | undefined; - - createEffect(() => { - if (activeIndexSig() === props.index) { - rowEl?.focus(); - } - }); - - css` - .row { - border: 1px solid transparent; - width: 100%; - background-color: var(--bg-offset); - position: relative; - } - .row:after { - content: attr(data-codepoint); - display: block; - position: absolute; - bottom: 0; - right: 0; - padding: 0.5rem; - opacity: 0.5; - font-size: 0.8rem; - } - .row.active { - border: 1px solid blue; - } - .content { - width: 100%; - padding: 0.5rem; - display: grid; - grid-template-columns: auto 1fr; - gap: 1rem; - } - .symbol { - width: 2rem; - text-align: center; - } - .symbol::before, - .symbol::after { - content: '"'; - } - .name { - text-align: left; - opacity: 0.75; - } - `; - - return ( - - ); -} diff --git a/src/components/table-container/ResultsRow.vue b/src/components/table-container/ResultsRow.vue new file mode 100644 index 0000000..78fd283 --- /dev/null +++ b/src/components/table-container/ResultsRow.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/src/components/table-container/info-tables/CaseMapping.tsx b/src/components/table-container/info-tables/CaseMapping.tsx deleted file mode 100644 index 101b0a3..0000000 --- a/src/components/table-container/info-tables/CaseMapping.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { createMemo, Show, For } from "solid-js"; -import type { UnicodeMapData } from "@emnudge/unicode-query"; - -interface CaseMappingProps { - titlecaseMapping: UnicodeMapData["titlecaseMapping"]; - uppercaseMapping: UnicodeMapData["uppercaseMapping"]; - lowercaseMapping: UnicodeMapData["lowercaseMapping"]; -} - -export function CaseMapping(props: CaseMappingProps) { - const caseMappings = createMemo( - () => - Object.entries({ - titlecaseMapping: props.titlecaseMapping, - uppercaseMapping: props.uppercaseMapping, - lowercaseMapping: props.lowercaseMapping, - }).filter(([, v]) => v) as [string, number][], - ); - - return ( - -
- - - - - - - - - {([key, value]) => ( - - - - - )} - - -
- Case Mapping -
{key}{String.fromCodePoint(value)}
-
- ); -} diff --git a/src/components/table-container/info-tables/CaseMapping.vue b/src/components/table-container/info-tables/CaseMapping.vue new file mode 100644 index 0000000..0bc56fd --- /dev/null +++ b/src/components/table-container/info-tables/CaseMapping.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/table-container/info-tables/Encoding.tsx b/src/components/table-container/info-tables/Encoding.tsx deleted file mode 100644 index 87264b0..0000000 --- a/src/components/table-container/info-tables/Encoding.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { createMemo, For } from "solid-js"; -import { css } from "solid-styled"; -import { encodingModeSig, setEncodingModeSig } from "$stores"; -import { getEncodings } from "$utils/char"; - -interface EncodingProps { - codepoint: number; -} - -type EncodingType = "hex" | "bin" | "dec"; -const encodingTypes: EncodingType[] = ["hex", "bin", "dec"]; - -function getNumber(num: number, encoding: EncodingType, length: number) { - if (encoding === "hex") return `0x${num.toString(16).padStart(length / 4, "0")}`; - if (encoding === "bin") return `0b${num.toString(2).padStart(length, "0")}`; - return `${String(num)}`; -} - -export function Encoding(props: EncodingProps) { - const encodings = createMemo(() => getEncodings(props.codepoint)); - - const encodingsTable = createMemo(() => - encodingModeSig() === "dec" - ? ([ - ["UTF-8", encodings().utf8, 8], - ["UTF-16", encodings().utf16BE, 16], - ] as [string, Uint8Array | Uint16Array, number][]) - : ([ - ["UTF-8", encodings().utf8, 8], - ["UTF-16 BE", encodings().utf16BE, 16], - ["UTF-16 LE", encodings().utf16LE, 16], - ] as [string, Uint8Array | Uint16Array, number][]), - ); - - css` - .bit-sets span { - padding: 0 3px; - } - .title { - color: var(--label-col); - font-weight: bold; - } - td { - padding: 5px; - } - th:first-child { - color: var(--primary-text); - font-weight: 400; - padding-right: 1rem; - } - tbody tr:nth-child(odd) { - background-color: var(--bg-offset); - } - .encoding-type { - display: flex; - } - .encoding-type button { - opacity: 0.6; - cursor: pointer; - padding: 6px; - border: none; - background: 0; - margin: 0; - font-weight: 600; - } - .encoding-type button:focus { - outline: 1px solid rgba(0, 0, 255, 0.443); - } - .encoding-type .active { - opacity: 0.95; - background-color: rgba(128, 128, 128, 0.097); - } - `; - - return ( - <> -
- - - - - - - - - - - {([name, bitSets, length]) => ( - - - - - )} - - -
Encoding -
- - {(encoding) => ( - - )} - -
-
{name} - - {(bitSet) => ( - - )} - -
- - ); -} diff --git a/src/components/table-container/info-tables/Encoding.vue b/src/components/table-container/info-tables/Encoding.vue new file mode 100644 index 0000000..468f5f4 --- /dev/null +++ b/src/components/table-container/info-tables/Encoding.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/src/components/table-container/info-tables/HtmlEntities.tsx b/src/components/table-container/info-tables/HtmlEntities.tsx deleted file mode 100644 index 14deb4c..0000000 --- a/src/components/table-container/info-tables/HtmlEntities.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { For } from "solid-js"; -import { css } from "solid-styled"; - -interface HtmlEntitiesProps { - names: string[]; - codepoint: number; -} - -export function HtmlEntities(props: HtmlEntitiesProps) { - css` - th { - text-align: left; - color: var(--primary-text); - font-weight: 400; - padding-bottom: 6px; - } - tbody tr:nth-child(odd) { - background-color: var(--bg-offset); - } - td { - padding: 6px; - } - td:first-child { - padding-right: 30px; - } - `; - - return ( - <> -
- - - - - - - - - {(name) => ( - - - - - )} - - - - - - - - - - -
- Html Entities -
Named&{name};
Hex&#x{props.codepoint.toString(16).padStart(5, "0")};
Dec&#{props.codepoint};
- - ); -} diff --git a/src/components/table-container/info-tables/HtmlEntities.vue b/src/components/table-container/info-tables/HtmlEntities.vue new file mode 100644 index 0000000..5dc22b1 --- /dev/null +++ b/src/components/table-container/info-tables/HtmlEntities.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/src/components/table-container/info-tables/NumberEquiv.tsx b/src/components/table-container/info-tables/NumberEquiv.tsx deleted file mode 100644 index 9031b82..0000000 --- a/src/components/table-container/info-tables/NumberEquiv.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { createMemo, Show, For } from "solid-js"; -import type { UnicodeMapData } from "@emnudge/unicode-query"; - -interface NumberEquivProps { - digitEquiv: UnicodeMapData["digitEquiv"]; - numericEquiv: UnicodeMapData["numericEquiv"]; - decimalEquiv: UnicodeMapData["decimalEquiv"]; -} - -export function NumberEquiv(props: NumberEquivProps) { - const numberMappings = createMemo(() => - Object.entries({ - digitEquiv: props.digitEquiv, - numericEquiv: props.numericEquiv, - decimalEquiv: props.decimalEquiv, - }).filter(([, v]) => v), - ); - - return ( - -
- - - - - - - - - {([key, value]) => ( - - - - - )} - - -
- Number Equivalent -
{key}{value}
-
- ); -} diff --git a/src/components/table-container/info-tables/NumberEquiv.vue b/src/components/table-container/info-tables/NumberEquiv.vue new file mode 100644 index 0000000..24a7b04 --- /dev/null +++ b/src/components/table-container/info-tables/NumberEquiv.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/table-container/info-tables/Properties.tsx b/src/components/table-container/info-tables/Properties.tsx deleted file mode 100644 index c62ec9b..0000000 --- a/src/components/table-container/info-tables/Properties.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { For } from "solid-js"; -import { css } from "solid-styled"; - -interface PropertiesProps { - properties: string[]; -} - -export function Properties(props: PropertiesProps) { - css` - h3 { - padding-bottom: 6px; - color: var(--primary-text); - font-size: 1rem; - font-weight: 400; - } - .properties { - width: 100%; - display: flex; - flex-wrap: wrap; - gap: 5px; - text-align: left; - } - .properties span { - background: var(--bg-offset); - padding: 2px 8px; - border-radius: 4px; - } - `; - - return ( - <> -

Properties

-
- {(property) => {property}} -
- - ); -} diff --git a/src/components/table-container/info-tables/Properties.vue b/src/components/table-container/info-tables/Properties.vue new file mode 100644 index 0000000..16684f6 --- /dev/null +++ b/src/components/table-container/info-tables/Properties.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/src/icons/CloseIcon.vue b/src/icons/CloseIcon.vue new file mode 100644 index 0000000..286a350 --- /dev/null +++ b/src/icons/CloseIcon.vue @@ -0,0 +1,8 @@ + diff --git a/src/icons/FilterIcon.vue b/src/icons/FilterIcon.vue new file mode 100644 index 0000000..fc4c08c --- /dev/null +++ b/src/icons/FilterIcon.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/icons/GitHubIcon.vue b/src/icons/GitHubIcon.vue new file mode 100644 index 0000000..3120a43 --- /dev/null +++ b/src/icons/GitHubIcon.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/icons/SettingsIcon.vue b/src/icons/SettingsIcon.vue new file mode 100644 index 0000000..bd8ac9c --- /dev/null +++ b/src/icons/SettingsIcon.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/src/icons/SunIcon.vue b/src/icons/SunIcon.vue new file mode 100644 index 0000000..48c1650 --- /dev/null +++ b/src/icons/SunIcon.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/icons/close.tsx b/src/icons/close.tsx deleted file mode 100644 index 79d1724..0000000 --- a/src/icons/close.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export function CloseIcon() { - return ( - - - - ); -} diff --git a/src/icons/filter.tsx b/src/icons/filter.tsx deleted file mode 100644 index 5993865..0000000 --- a/src/icons/filter.tsx +++ /dev/null @@ -1,16 +0,0 @@ -interface FilterIconProps { - active?: boolean; -} - -export function FilterIcon(props: FilterIconProps) { - const fill = () => (props.active ? "var(--hsl-bg)" : "var(--hsl)"); - return ( - - - - ); -} diff --git a/src/icons/github.tsx b/src/icons/github.tsx deleted file mode 100644 index 909c4db..0000000 --- a/src/icons/github.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { css } from "solid-styled"; - -interface GitHubIconProps { - size?: number; - href: string; -} - -export function GitHubIcon(props: GitHubIconProps) { - css` - svg { - position: absolute; - top: 0; - right: 0; - } - `; - - return ( - - - - - - - - ); -} diff --git a/src/icons/settings.tsx b/src/icons/settings.tsx deleted file mode 100644 index a6dc7c9..0000000 --- a/src/icons/settings.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { css } from "solid-styled"; - -interface SettingsIconProps { - active?: boolean; - ariaLabel?: string; - onClick?: () => void; -} - -export function SettingsIcon(props: SettingsIconProps) { - css` - button { - border: none; - background: none; - padding: 0; - margin: 0; - cursor: pointer; - } - button:active { - background: none; - } - button svg { - transform: rotate(0deg); - transform-origin: 50% 50%; - transition: 0.15s; - } - button:active svg, - button.active svg { - transform: rotate(180deg); - } - `; - - return ( - - ); -} diff --git a/src/icons/sun.tsx b/src/icons/sun.tsx deleted file mode 100644 index 4280466..0000000 --- a/src/icons/sun.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { css } from "solid-styled"; - -export function SunIcon() { - css` - :global(:root[data-theme="dark"]) svg.theme-sun { - fill: white; - } - - :global(:root[data-theme="light"]) svg.theme-sun { - fill: black; - } - `; - - return ( - - - - - ); -} diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index 9ec51f0..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { render } from "solid-js/web"; -import "./global.css"; -import App from "./App"; - -render(() => , document.getElementById("app")!); diff --git a/src/lib/components/feedback/Alert.tsx b/src/lib/components/feedback/Alert.tsx deleted file mode 100644 index 3ba17b9..0000000 --- a/src/lib/components/feedback/Alert.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Show, type JSX } from "solid-js"; -import { css } from "solid-styled"; - -type VariantValue = "info" | "success" | "warning" | "error"; - -interface AlertProps { - variant?: VariantValue; - title?: string; - dismissible?: boolean; - icon?: boolean; - children?: JSX.Element; - onClose?: () => void; -} - -const icons: Record = { - info: "\u2139", - success: "\u2713", - warning: "\u26A0", - error: "\u2715", -}; - -export function Alert(props: AlertProps) { - const variant = () => props.variant ?? "info"; - - css` - .alert { - display: flex; - gap: var(--space-3); - padding: var(--space-4); - border-radius: var(--radius-md); - border: var(--border-width-1) solid; - } - .alert--info { - background: var(--color-info-bg); - color: var(--color-info); - border-color: var(--color-info-border); - } - .alert--success { - background: var(--color-success-bg); - color: var(--color-success); - border-color: var(--color-success-border); - } - .alert--warning { - background: var(--color-warning-bg); - color: var(--color-warning); - border-color: var(--color-warning-border); - } - .alert--error { - background: var(--color-error-bg); - color: var(--color-error); - border-color: var(--color-error-border); - } - .alert__icon { - flex-shrink: 0; - font-size: var(--font-size-lg); - font-weight: var(--font-weight-bold); - } - .alert__content { - flex: 1; - min-width: 0; - } - .alert__title { - font-weight: var(--font-weight-semibold); - margin-bottom: var(--space-1); - } - .alert__message { - font-size: var(--font-size-base); - } - .alert__close { - flex-shrink: 0; - background: none; - border: none; - padding: 0; - margin: 0; - cursor: pointer; - opacity: 0.7; - transition: opacity var(--duration-base); - color: inherit; - font-size: var(--font-size-lg); - line-height: 1; - } - .alert__close:hover { - opacity: 1; - } - `; - - return ( - - ); -} diff --git a/src/lib/components/feedback/Alert.vue b/src/lib/components/feedback/Alert.vue new file mode 100644 index 0000000..aa871f5 --- /dev/null +++ b/src/lib/components/feedback/Alert.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/lib/components/feedback/Badge.tsx b/src/lib/components/feedback/Badge.tsx deleted file mode 100644 index fc19f66..0000000 --- a/src/lib/components/feedback/Badge.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { createMemo, Show, type JSX } from "solid-js"; -import { css } from "solid-styled"; - -type VariantValue = "default" | "primary" | "success" | "warning" | "error" | "info"; -type SizeValue = "sm" | "base" | "lg"; - -interface BadgeProps { - variant?: VariantValue; - size?: SizeValue; - dot?: boolean; - pill?: boolean; - children?: JSX.Element; -} - -export function Badge(props: BadgeProps) { - const classes = createMemo(() => - [ - "badge", - `badge--${props.variant ?? "default"}`, - `badge--${props.size ?? "base"}`, - props.pill && "badge--pill", - props.dot && "badge--dot", - ] - .filter(Boolean) - .join(" "), - ); - - css` - .badge { - display: inline-flex; - align-items: center; - gap: var(--space-1); - font-weight: var(--font-weight-medium); - border-radius: var(--radius-md); - white-space: nowrap; - } - .badge--default { - background: var(--color-bg-offset); - color: var(--color-text); - border: var(--border-width-1) solid var(--color-border); - } - .badge--primary { - background: var(--color-primary-bg); - color: var(--color-primary); - border: var(--border-width-1) solid var(--color-primary); - } - .badge--success { - background: var(--color-success-bg); - color: var(--color-success); - border: var(--border-width-1) solid var(--color-success-border); - } - .badge--warning { - background: var(--color-warning-bg); - color: var(--color-warning); - border: var(--border-width-1) solid var(--color-warning-border); - } - .badge--error { - background: var(--color-error-bg); - color: var(--color-error); - border: var(--border-width-1) solid var(--color-error-border); - } - .badge--info { - background: var(--color-info-bg); - color: var(--color-info); - border: var(--border-width-1) solid var(--color-info-border); - } - .badge--sm { - padding: var(--space-1) var(--space-2); - font-size: var(--font-size-xs); - } - .badge--base { - padding: var(--space-1) var(--space-3); - font-size: var(--font-size-sm); - } - .badge--lg { - padding: var(--space-2) var(--space-4); - font-size: var(--font-size-base); - } - .badge--pill { - border-radius: var(--radius-full); - } - .badge--dot { - padding-left: var(--space-2); - } - .badge__dot { - width: 0.5rem; - height: 0.5rem; - border-radius: var(--radius-full); - background: currentColor; - } - `; - - return ( - - - - - {props.children} - - ); -} diff --git a/src/lib/components/feedback/Badge.vue b/src/lib/components/feedback/Badge.vue new file mode 100644 index 0000000..059c689 --- /dev/null +++ b/src/lib/components/feedback/Badge.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/lib/components/feedback/Spinner.tsx b/src/lib/components/feedback/Spinner.tsx deleted file mode 100644 index 2dde000..0000000 --- a/src/lib/components/feedback/Spinner.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { createMemo } from "solid-js"; -import { css } from "solid-styled"; - -type SizeValue = "sm" | "base" | "lg" | "xl"; -type ColorValue = "primary" | "white" | "current"; - -interface SpinnerProps { - size?: SizeValue; - color?: ColorValue; -} - -export function Spinner(props: SpinnerProps) { - const classes = createMemo(() => - ["spinner", `spinner--${props.size ?? "base"}`, `spinner--${props.color ?? "primary"}`].join( - " ", - ), - ); - - css` - .spinner { - display: inline-flex; - align-items: center; - justify-content: center; - } - .spinner__circle { - border-radius: 50%; - border-style: solid; - border-right-color: transparent; - animation: spin 0.6s linear infinite; - } - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .spinner--sm .spinner__circle { - width: 1rem; - height: 1rem; - border-width: 2px; - } - .spinner--base .spinner__circle { - width: 1.5rem; - height: 1.5rem; - border-width: 2px; - } - .spinner--lg .spinner__circle { - width: 2rem; - height: 2rem; - border-width: 3px; - } - .spinner--xl .spinner__circle { - width: 3rem; - height: 3rem; - border-width: 4px; - } - .spinner--primary .spinner__circle { - border-color: var(--color-primary); - border-right-color: transparent; - } - .spinner--white .spinner__circle { - border-color: white; - border-right-color: transparent; - } - .spinner--current .spinner__circle { - border-color: currentColor; - border-right-color: transparent; - } - `; - - return ( -
-
-
- ); -} diff --git a/src/lib/components/feedback/Spinner.vue b/src/lib/components/feedback/Spinner.vue new file mode 100644 index 0000000..8c46607 --- /dev/null +++ b/src/lib/components/feedback/Spinner.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/lib/components/feedback/index.ts b/src/lib/components/feedback/index.ts index 16886b3..766098a 100644 --- a/src/lib/components/feedback/index.ts +++ b/src/lib/components/feedback/index.ts @@ -1,3 +1,3 @@ -export { Alert } from "./Alert"; -export { Badge } from "./Badge"; -export { Spinner } from "./Spinner"; +export { default as Alert } from "./Alert.vue"; +export { default as Badge } from "./Badge.vue"; +export { default as Spinner } from "./Spinner.vue"; diff --git a/src/lib/components/forms/Button.tsx b/src/lib/components/forms/Button.tsx deleted file mode 100644 index fc98fa7..0000000 --- a/src/lib/components/forms/Button.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { createMemo, Show, type JSX } from "solid-js"; -import { css } from "solid-styled"; - -type VariantValue = "primary" | "secondary" | "ghost" | "outline" | "danger"; -type SizeValue = "sm" | "base" | "lg"; -type TypeValue = "button" | "submit" | "reset"; - -interface ButtonProps { - variant?: VariantValue; - size?: SizeValue; - disabled?: boolean; - loading?: boolean; - fullWidth?: boolean; - hue?: number; - type?: TypeValue; - onClick?: (e: MouseEvent) => void; - children?: JSX.Element; -} - -export function Button(props: ButtonProps) { - const classes = createMemo(() => - [ - "btn", - props.hue !== undefined ? "styled" : `btn--${props.variant ?? "primary"}`, - `btn--${props.size ?? "base"}`, - props.fullWidth && "btn--full-width", - (props.disabled || props.loading) && "btn--disabled", - ] - .filter(Boolean) - .join(" "), - ); - - const style = createMemo(() => (props.hue !== undefined ? `--hue: ${props.hue}` : undefined)); - - css` - .btn { - margin: 0; - padding: var(--space-2) var(--space-5); - border-radius: var(--radius-md); - box-shadow: var(--shadow-sm); - cursor: pointer; - font-family: var(--font-family-base); - font-weight: var(--font-weight-medium); - transition: var(--transition-colors), var(--transition-transform); - display: inline-flex; - align-items: center; - justify-content: center; - gap: var(--space-2); - position: relative; - border: var(--border-width-1) solid transparent; - } - .btn:focus-visible { - outline: 2px solid var(--color-border-focus); - outline-offset: 2px; - } - .btn--primary { - color: var(--color-primary); - background: var(--color-primary-bg); - border-color: var(--color-primary); - } - .btn--primary:hover:not(:disabled) { - background: var(--color-primary-hover); - filter: brightness(0.95); - } - .btn--primary:active:not(:disabled) { - transform: scale(0.98); - } - .btn--secondary { - color: var(--color-text); - background: var(--color-bg-offset); - border-color: var(--color-border); - } - .btn--secondary:hover:not(:disabled) { - background: var(--color-bg-elevated); - border-color: var(--color-text-secondary); - } - .btn--secondary:active:not(:disabled) { - transform: scale(0.98); - } - .btn--ghost { - color: var(--color-text-primary); - background: transparent; - border-color: transparent; - box-shadow: none; - } - .btn--ghost:hover:not(:disabled) { - background: var(--color-bg-offset); - } - .btn--ghost:active:not(:disabled) { - transform: scale(0.98); - } - .btn--outline { - color: var(--color-primary); - background: transparent; - border-color: var(--color-primary); - box-shadow: none; - } - .btn--outline:hover:not(:disabled) { - background: var(--color-primary-bg); - } - .btn--outline:active:not(:disabled) { - transform: scale(0.98); - } - .btn--danger { - color: white; - background: var(--color-error); - border-color: var(--color-error); - } - .btn--danger:hover:not(:disabled) { - filter: brightness(1.1); - } - .btn--danger:active:not(:disabled) { - transform: scale(0.98); - } - .btn--sm { - padding: var(--space-1) var(--space-3); - font-size: var(--font-size-sm); - } - .btn--lg { - padding: var(--space-3) var(--space-6); - font-size: var(--font-size-lg); - } - .btn--full-width { - width: 100%; - } - .btn:disabled, - .btn--disabled { - opacity: 0.5; - cursor: not-allowed; - } - .btn__spinner { - width: 1em; - height: 1em; - border: 2px solid currentColor; - border-right-color: transparent; - border-radius: 50%; - animation: spin 0.6s linear infinite; - } - @keyframes spin { - to { - transform: rotate(360deg); - } - } - .btn__content--loading { - opacity: 0.7; - } - .btn.styled { - color: var(--hsl); - background: var(--hsl-bg); - border-color: var(--hsl); - } - .btn.styled:hover:not(:disabled) { - filter: brightness(0.95); - } - .btn.styled:active:not(:disabled) { - transform: scale(0.98); - } - `; - - return ( - - ); -} diff --git a/src/lib/components/forms/Button.vue b/src/lib/components/forms/Button.vue new file mode 100644 index 0000000..5a9c3cb --- /dev/null +++ b/src/lib/components/forms/Button.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/src/lib/components/forms/Checkbox.tsx b/src/lib/components/forms/Checkbox.tsx deleted file mode 100644 index 1ae7fbb..0000000 --- a/src/lib/components/forms/Checkbox.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { createMemo, createEffect, Show } from "solid-js"; -import { css } from "solid-styled"; - -interface CheckboxProps { - checked?: boolean; - disabled?: boolean; - label?: string; - error?: string | boolean; - indeterminate?: boolean; - onChange?: (checked: boolean) => void; -} - -export function Checkbox(props: CheckboxProps) { - let inputElement: HTMLInputElement | undefined; - - const hasError = createMemo(() => - typeof props.error === "string" ? !!props.error : props.error, - ); - - createEffect(() => { - if (inputElement) { - inputElement.indeterminate = props.indeterminate ?? false; - } - }); - - function handleChange(e: Event) { - const target = e.target as HTMLInputElement; - props.onChange?.(target.checked); - } - - css` - .checkbox-wrapper { - display: inline-flex; - flex-direction: column; - gap: var(--space-1); - } - .checkbox-label { - display: inline-flex; - align-items: center; - gap: var(--space-2); - cursor: pointer; - user-select: none; - } - .checkbox-label--disabled { - opacity: 0.6; - cursor: not-allowed; - } - .checkbox-input { - position: absolute; - opacity: 0; - width: 0; - height: 0; - } - .checkbox-box { - display: flex; - align-items: center; - justify-content: center; - width: 1.25rem; - height: 1.25rem; - border: var(--border-width-2) solid var(--color-border); - border-radius: var(--radius-sm); - background: var(--color-bg-input); - transition: var(--transition-colors); - flex-shrink: 0; - } - .checkbox-input:focus-visible + .checkbox-box { - outline: 2px solid var(--color-border-focus); - outline-offset: 2px; - } - .checkbox-input:checked + .checkbox-box { - background: var(--color-primary); - border-color: var(--color-primary); - } - .checkbox-input:indeterminate + .checkbox-box { - background: var(--color-primary); - border-color: var(--color-primary); - } - .checkbox-label:hover .checkbox-box:not(.checkbox-box--error) { - border-color: var(--color-primary); - } - .checkbox-box--error { - border-color: var(--color-error-border); - } - .checkbox-icon { - width: 1rem; - height: 1rem; - color: white; - } - .checkbox-text { - color: var(--color-text); - font-size: var(--font-size-base); - } - .checkbox-error { - font-size: var(--font-size-sm); - color: var(--color-error); - margin-left: calc(1.25rem + var(--space-2)); - } - `; - - return ( -
- - - {props.error as string} - -
- ); -} diff --git a/src/lib/components/forms/Checkbox.vue b/src/lib/components/forms/Checkbox.vue new file mode 100644 index 0000000..4182346 --- /dev/null +++ b/src/lib/components/forms/Checkbox.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/src/lib/components/forms/Input.tsx b/src/lib/components/forms/Input.tsx deleted file mode 100644 index 0a34040..0000000 --- a/src/lib/components/forms/Input.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { createMemo, Show } from "solid-js"; -import { css } from "solid-styled"; - -type TypeValue = "text" | "email" | "password" | "number" | "search" | "tel" | "url"; -type SizeValue = "sm" | "base" | "lg"; - -interface InputProps { - value?: string; - type?: TypeValue; - placeholder?: string; - disabled?: boolean; - readonly?: boolean; - error?: string | boolean; - size?: SizeValue; - fullWidth?: boolean; - hue?: number; - autocomplete?: string; - onInput?: (e: InputEvent & { currentTarget: HTMLInputElement }) => void; - onFocus?: (e: FocusEvent) => void; - onBlur?: (e: FocusEvent) => void; - onChange?: (value: string) => void; - ref?: (el: HTMLInputElement) => void; -} - -export function Input(props: InputProps) { - const hasError = createMemo(() => - typeof props.error === "string" ? !!props.error : props.error, - ); - - const classes = createMemo(() => - [ - "input", - props.hue !== undefined ? "styled" : "input--default", - `input--${props.size ?? "base"}`, - hasError() && "input--error", - props.fullWidth && "input--full-width", - props.disabled && "input--disabled", - ] - .filter(Boolean) - .join(" "), - ); - - const style = createMemo(() => (props.hue !== undefined ? `--hue: ${props.hue}` : undefined)); - - css` - .input-wrapper { - display: inline-flex; - flex-direction: column; - gap: var(--space-1); - width: auto; - } - .input { - margin: 0; - padding: var(--space-2) var(--space-4); - border-radius: var(--radius-md); - border: var(--border-width-1) solid var(--color-border); - background: var(--color-bg-input); - color: var(--color-text); - font-family: var(--font-family-base); - transition: var(--transition-colors); - box-sizing: border-box; - } - .input::placeholder { - color: var(--color-text-tertiary); - opacity: 0.6; - } - .input:focus { - outline: none; - border-color: var(--color-border-focus); - box-shadow: var(--shadow-input-focus); - } - .input--default:hover:not(:disabled):not(:focus) { - border-color: var(--color-text-secondary); - } - .input--sm { - padding: var(--space-1) var(--space-3); - font-size: var(--font-size-sm); - height: var(--size-input-height-sm); - } - .input--base { - height: var(--size-input-height-base); - } - .input--lg { - padding: var(--space-3) var(--space-5); - font-size: var(--font-size-lg); - height: var(--size-input-height-lg); - } - .input--error { - border-color: var(--color-error-border); - } - .input--error:focus { - border-color: var(--color-error); - } - .input-error { - font-size: var(--font-size-sm); - color: var(--color-error); - margin-top: var(--space-1); - } - .input--full-width { - width: 100%; - } - .input:disabled, - .input--disabled { - opacity: 0.6; - cursor: not-allowed; - background: var(--color-bg-offset); - } - .input.styled { - color: var(--hsl); - background: var(--hsl-bg); - border-color: var(--hsl); - } - .input.styled:focus { - border-color: var(--hsl); - box-shadow: var(--shadow-input-focus); - } - `; - - return ( -
- { - props.onInput?.(e); - props.onChange?.(e.currentTarget.value); - }} - onFocus={props.onFocus} - onBlur={props.onBlur} - aria-invalid={hasError()} - /> - - {props.error as string} - -
- ); -} diff --git a/src/lib/components/forms/Input.vue b/src/lib/components/forms/Input.vue new file mode 100644 index 0000000..7797315 --- /dev/null +++ b/src/lib/components/forms/Input.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/src/lib/components/forms/Label.tsx b/src/lib/components/forms/Label.tsx deleted file mode 100644 index 3ab4c6e..0000000 --- a/src/lib/components/forms/Label.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { createMemo, Show, type JSX } from "solid-js"; -import { css } from "solid-styled"; - -type SizeValue = "sm" | "base" | "lg"; - -interface LabelProps { - for?: string; - required?: boolean; - optional?: boolean; - size?: SizeValue; - children?: JSX.Element; -} - -export function Label(props: LabelProps) { - const classes = createMemo(() => ["label", `label--${props.size ?? "base"}`].join(" ")); - - css` - .label { - display: block; - color: var(--color-text-secondary); - font-weight: var(--font-weight-medium); - margin-bottom: var(--space-1); - } - .label--sm { - font-size: var(--font-size-sm); - } - .label--base { - font-size: var(--font-size-base); - } - .label--lg { - font-size: var(--font-size-lg); - } - .label-indicator { - margin-left: var(--space-1); - } - .label-indicator--required { - color: var(--color-error); - } - .label-indicator--optional { - color: var(--color-text-tertiary); - font-weight: var(--font-weight-normal); - font-size: var(--font-size-sm); - } - `; - - return ( - - ); -} diff --git a/src/lib/components/forms/Label.vue b/src/lib/components/forms/Label.vue new file mode 100644 index 0000000..b084832 --- /dev/null +++ b/src/lib/components/forms/Label.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/lib/components/forms/Radio.tsx b/src/lib/components/forms/Radio.tsx deleted file mode 100644 index 0bc2f3f..0000000 --- a/src/lib/components/forms/Radio.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { Show } from "solid-js"; -import { css } from "solid-styled"; - -interface RadioProps { - value: string | number; - name: string; - checked?: boolean; - disabled?: boolean; - label?: string; - onChange?: (value: string | number) => void; -} - -export function Radio(props: RadioProps) { - function handleChange() { - props.onChange?.(props.value); - } - - css` - .radio-label { - display: inline-flex; - align-items: center; - gap: var(--space-2); - cursor: pointer; - user-select: none; - } - .radio-label--disabled { - opacity: 0.6; - cursor: not-allowed; - } - .radio-input { - position: absolute; - opacity: 0; - width: 0; - height: 0; - } - .radio-box { - display: flex; - align-items: center; - justify-content: center; - width: 1.25rem; - height: 1.25rem; - border: var(--border-width-2) solid var(--color-border); - border-radius: var(--radius-full); - background: var(--color-bg-input); - transition: var(--transition-colors); - flex-shrink: 0; - } - .radio-input:focus-visible + .radio-box { - outline: 2px solid var(--color-border-focus); - outline-offset: 2px; - } - .radio-label:hover .radio-box { - border-color: var(--color-primary); - } - .radio-input:checked ~ .radio-box { - border-color: var(--color-primary); - } - .radio-dot { - width: 0.5rem; - height: 0.5rem; - border-radius: var(--radius-full); - background: var(--color-primary); - transform: scale(0); - transition: transform var(--duration-base) var(--ease-out); - } - .radio-input:checked ~ .radio-box .radio-dot { - transform: scale(1); - } - .radio-text { - color: var(--color-text); - font-size: var(--font-size-base); - } - `; - - return ( - - ); -} diff --git a/src/lib/components/forms/Radio.vue b/src/lib/components/forms/Radio.vue new file mode 100644 index 0000000..06630fc --- /dev/null +++ b/src/lib/components/forms/Radio.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/src/lib/components/forms/Select.tsx b/src/lib/components/forms/Select.tsx deleted file mode 100644 index f44ff17..0000000 --- a/src/lib/components/forms/Select.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { createMemo, Show, For } from "solid-js"; -import { css } from "solid-styled"; - -type OptionValue = string | number; -type Option = { value: OptionValue; label: string }; -type SizeValue = "sm" | "base" | "lg"; - -interface SelectProps { - value?: string | number; - options: Option[] | string[] | number[]; - placeholder?: string; - disabled?: boolean; - error?: string | boolean; - size?: SizeValue; - fullWidth?: boolean; - hue?: number; - onChange?: (value: OptionValue) => void; -} - -export function Select(props: SelectProps) { - const normalizedOptions = createMemo(() => - props.options.map((opt) => { - if (typeof opt === "object" && "value" in opt && "label" in opt) { - return opt; - } - return { value: opt, label: String(opt) }; - }), - ); - - const hasError = createMemo(() => - typeof props.error === "string" ? !!props.error : props.error, - ); - - const classes = createMemo(() => - [ - "select", - props.hue !== undefined ? "styled" : "select--default", - `select--${props.size ?? "base"}`, - hasError() && "select--error", - props.fullWidth && "select--full-width", - props.disabled && "select--disabled", - ] - .filter(Boolean) - .join(" "), - ); - - const style = createMemo(() => (props.hue !== undefined ? `--hue: ${props.hue}` : undefined)); - - function handleChange(e: Event) { - const target = e.target as HTMLSelectElement; - const selectedValue = target.value; - - if (props.options.length > 0 && typeof props.options[0] === "number") { - props.onChange?.(Number(selectedValue)); - } else { - props.onChange?.(selectedValue); - } - } - - css` - .select-wrapper { - display: inline-flex; - flex-direction: column; - gap: var(--space-1); - width: auto; - } - .select { - margin: 0; - padding: var(--space-2) var(--space-4); - border-radius: var(--radius-md); - border: var(--border-width-1) solid var(--color-border); - background: var(--color-bg-input); - color: var(--color-text); - font-family: var(--font-family-base); - transition: var(--transition-colors); - box-sizing: border-box; - cursor: pointer; - } - .select:focus { - outline: none; - border-color: var(--color-border-focus); - box-shadow: var(--shadow-input-focus); - } - .select--default:hover:not(:disabled):not(:focus) { - border-color: var(--color-text-secondary); - } - .select--sm { - padding: var(--space-1) var(--space-3); - font-size: var(--font-size-sm); - height: var(--size-input-height-sm); - } - .select--base { - height: var(--size-input-height-base); - } - .select--lg { - padding: var(--space-3) var(--space-5); - font-size: var(--font-size-lg); - height: var(--size-input-height-lg); - } - .select--error { - border-color: var(--color-error-border); - } - .select--error:focus { - border-color: var(--color-error); - } - .select-error { - font-size: var(--font-size-sm); - color: var(--color-error); - margin-top: var(--space-1); - } - .select--full-width { - width: 100%; - } - .select:disabled, - .select--disabled { - opacity: 0.6; - cursor: not-allowed; - background: var(--color-bg-offset); - } - .select.styled { - color: var(--hsl); - background: var(--hsl-bg); - border-color: var(--hsl); - } - .select.styled:focus { - border-color: var(--hsl); - box-shadow: var(--shadow-input-focus); - } - option { - background: var(--color-bg-input); - color: var(--color-text); - } - `; - - return ( -
- - - {props.error as string} - -
- ); -} diff --git a/src/lib/components/forms/Select.vue b/src/lib/components/forms/Select.vue new file mode 100644 index 0000000..f9b4810 --- /dev/null +++ b/src/lib/components/forms/Select.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/src/lib/components/forms/Switch.tsx b/src/lib/components/forms/Switch.tsx deleted file mode 100644 index eab4c91..0000000 --- a/src/lib/components/forms/Switch.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { Show } from "solid-js"; -import { css } from "solid-styled"; - -type SizeValue = "sm" | "base" | "lg"; - -interface SwitchProps { - checked?: boolean; - disabled?: boolean; - label?: string; - size?: SizeValue; - onChange?: (checked: boolean) => void; -} - -export function Switch(props: SwitchProps) { - const size = () => props.size ?? "base"; - - function handleChange(e: Event) { - const target = e.target as HTMLInputElement; - props.onChange?.(target.checked); - } - - css` - .switch-label { - display: inline-flex; - align-items: center; - gap: var(--space-3); - cursor: pointer; - user-select: none; - } - .switch-label--disabled { - opacity: 0.6; - cursor: not-allowed; - } - .switch-input { - position: absolute; - opacity: 0; - width: 0; - height: 0; - } - .switch-track { - position: relative; - display: inline-block; - background: var(--color-border); - border-radius: var(--radius-full); - transition: var(--transition-colors); - flex-shrink: 0; - } - .switch-track--sm { - width: 2rem; - height: 1rem; - } - .switch-track--base { - width: 2.5rem; - height: 1.25rem; - } - .switch-track--lg { - width: 3rem; - height: 1.5rem; - } - .switch-input:focus-visible + .switch-track { - outline: 2px solid var(--color-border-focus); - outline-offset: 2px; - } - .switch-track--checked { - background: var(--color-primary); - } - .switch-thumb { - position: absolute; - top: 0.125rem; - left: 0.125rem; - background: white; - border-radius: var(--radius-full); - transition: transform var(--duration-base) var(--ease-out); - box-shadow: var(--shadow-xs); - } - .switch-thumb--sm { - width: 0.75rem; - height: 0.75rem; - } - .switch-thumb--base { - width: 1rem; - height: 1rem; - } - .switch-thumb--lg { - width: 1.25rem; - height: 1.25rem; - } - .switch-input:checked + .switch-track--sm .switch-thumb { - transform: translateX(1rem); - } - .switch-input:checked + .switch-track--base .switch-thumb { - transform: translateX(1.25rem); - } - .switch-input:checked + .switch-track--lg .switch-thumb { - transform: translateX(1.5rem); - } - .switch-text { - color: var(--color-text); - font-size: var(--font-size-base); - } - `; - - return ( - - ); -} diff --git a/src/lib/components/forms/Switch.vue b/src/lib/components/forms/Switch.vue new file mode 100644 index 0000000..412b7a4 --- /dev/null +++ b/src/lib/components/forms/Switch.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/src/lib/components/forms/TextArea.tsx b/src/lib/components/forms/TextArea.tsx deleted file mode 100644 index e07f728..0000000 --- a/src/lib/components/forms/TextArea.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { createMemo, Show } from "solid-js"; -import { css } from "solid-styled"; - -type ResizeValue = "none" | "vertical" | "horizontal" | "both"; - -interface TextAreaProps { - value?: string; - placeholder?: string; - disabled?: boolean; - readonly?: boolean; - error?: string | boolean; - rows?: number; - resize?: ResizeValue; - fullWidth?: boolean; - onInput?: (e: InputEvent & { currentTarget: HTMLTextAreaElement }) => void; - onFocus?: (e: FocusEvent) => void; - onBlur?: (e: FocusEvent) => void; - onChange?: (value: string) => void; -} - -export function TextArea(props: TextAreaProps) { - const hasError = createMemo(() => - typeof props.error === "string" ? !!props.error : props.error, - ); - - const classes = createMemo(() => - [ - "textarea", - hasError() && "textarea--error", - props.fullWidth && "textarea--full-width", - props.disabled && "textarea--disabled", - `textarea--resize-${props.resize ?? "vertical"}`, - ] - .filter(Boolean) - .join(" "), - ); - - css` - .textarea-wrapper { - display: inline-flex; - flex-direction: column; - gap: var(--space-1); - width: auto; - } - .textarea { - margin: 0; - padding: var(--space-3) var(--space-4); - border-radius: var(--radius-md); - border: var(--border-width-1) solid var(--color-border); - background: var(--color-bg-input); - color: var(--color-text); - font-family: var(--font-family-base); - font-size: var(--font-size-base); - line-height: var(--line-height-normal); - transition: var(--transition-colors); - box-sizing: border-box; - min-height: 5rem; - } - .textarea::placeholder { - color: var(--color-text-tertiary); - opacity: 0.6; - } - .textarea:focus { - outline: none; - border-color: var(--color-border-focus); - box-shadow: var(--shadow-input-focus); - } - .textarea:hover:not(:disabled):not(:focus) { - border-color: var(--color-text-secondary); - } - .textarea--resize-none { - resize: none; - } - .textarea--resize-vertical { - resize: vertical; - } - .textarea--resize-horizontal { - resize: horizontal; - } - .textarea--resize-both { - resize: both; - } - .textarea--error { - border-color: var(--color-error-border); - } - .textarea--error:focus { - border-color: var(--color-error); - } - .textarea-error { - font-size: var(--font-size-sm); - color: var(--color-error); - margin-top: var(--space-1); - } - .textarea--full-width { - width: 100%; - } - .textarea:disabled, - .textarea--disabled { - opacity: 0.6; - cursor: not-allowed; - background: var(--color-bg-offset); - } - `; - - return ( -
-