feat: add ToggleGroup support for labeled boolean widgets

This commit is contained in:
Csongor Czezar
2025-12-30 14:52:38 -08:00
parent 795962f3c3
commit 035a0f250c
10 changed files with 411 additions and 118 deletions

View File

@@ -163,6 +163,8 @@
"algoliasearch": "catalog:",
"axios": "catalog:",
"chart.js": "^4.5.0",
"class-variance-authority": "catalog:",
"clsx": "catalog:",
"cva": "catalog:",
"dompurify": "^3.2.5",
"dotenv": "catalog:",
@@ -180,6 +182,7 @@
"primevue": "catalog:",
"reka-ui": "catalog:",
"semver": "^7.7.2",
"tailwind-merge": "catalog:",
"three": "^0.170.0",
"tiptap-markdown": "^0.8.10",
"typegpu": "catalog:",

228
pnpm-lock.yaml generated
View File

@@ -138,6 +138,12 @@ catalogs:
axios:
specifier: ^1.8.2
version: 1.13.2
class-variance-authority:
specifier: 0.7.1
version: 0.7.1
clsx:
specifier: ^2.1.1
version: 2.1.1
cross-env:
specifier: ^10.1.0
version: 10.1.0
@@ -215,7 +221,7 @@ catalogs:
version: 1.1.1
pinia:
specifier: ^3.0.4
version: 2.2.2
version: 3.0.4
postcss-html:
specifier: ^1.8.0
version: 1.8.0
@@ -243,6 +249,9 @@ catalogs:
stylelint:
specifier: ^16.26.1
version: 16.26.1
tailwind-merge:
specifier: ^2.6.0
version: 2.6.0
tailwindcss:
specifier: ^4.1.12
version: 4.1.12
@@ -379,7 +388,7 @@ importers:
version: 4.2.5
'@sentry/vue':
specifier: 'catalog:'
version: 10.32.1(pinia@2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))(vue@3.5.13(typescript@5.9.3))
version: 10.32.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))(vue@3.5.13(typescript@5.9.3))
'@sparkjsdev/spark':
specifier: 'catalog:'
version: 0.1.10
@@ -428,6 +437,12 @@ importers:
chart.js:
specifier: ^4.5.0
version: 4.5.0
class-variance-authority:
specifier: 'catalog:'
version: 0.7.1
clsx:
specifier: 'catalog:'
version: 2.1.1
cva:
specifier: 'catalog:'
version: 1.0.0-beta.4(typescript@5.9.3)
@@ -466,7 +481,7 @@ importers:
version: 15.0.11
pinia:
specifier: 'catalog:'
version: 2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
version: 3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
primeicons:
specifier: 'catalog:'
version: 7.0.0
@@ -479,6 +494,9 @@ importers:
semver:
specifier: ^7.7.2
version: 7.7.3
tailwind-merge:
specifier: 'catalog:'
version: 2.6.0
three:
specifier: ^0.170.0
version: 0.170.0
@@ -536,7 +554,7 @@ importers:
version: 22.2.6(@babel/traverse@7.28.5)(nx@22.2.6)(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.2))(vitest@4.0.16)
'@pinia/testing':
specifier: 'catalog:'
version: 1.0.3(pinia@2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))
version: 1.0.3(pinia@3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))
'@playwright/test':
specifier: 'catalog:'
version: 1.57.0
@@ -758,7 +776,7 @@ importers:
version: 11.0.0(vue@3.5.13(typescript@5.9.3))
pinia:
specifier: 'catalog:'
version: 2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
version: 3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
primeicons:
specifier: 'catalog:'
version: 7.0.0
@@ -1510,6 +1528,10 @@ packages:
peerDependencies:
'@csstools/css-tokenizer': ^3.0.4
'@csstools/css-syntax-patches-for-csstree@1.0.20':
resolution: {integrity: sha512-8BHsjXfSciZxjmHQOuVdW2b8WLUPts9a+mfL13/PzEviufUEW2xnvQuOlKs9dRBHgRqJ53SF/DUoK9+MZk72oQ==}
engines: {node: '>=18'}
'@csstools/css-syntax-patches-for-csstree@1.0.22':
resolution: {integrity: sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==}
engines: {node: '>=18'}
@@ -1890,8 +1912,8 @@ packages:
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@exodus/bytes@1.7.0':
resolution: {integrity: sha512-5i+BtvujK/vM07YCGDyz4C4AyDzLmhxHMtM5HpUyPRtJPBdFPsj290ffXW+UXY21/G7GtXeHD2nRmq0T1ShyQQ==}
'@exodus/bytes@1.8.0':
resolution: {integrity: sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
peerDependencies:
'@exodus/crypto': ^1.0.0-rc.4
@@ -3862,18 +3884,27 @@ packages:
'@volar/language-core@2.4.15':
resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
'@volar/language-core@2.4.26':
resolution: {integrity: sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A==}
'@volar/language-core@2.4.27':
resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==}
'@volar/source-map@2.4.15':
resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
'@volar/source-map@2.4.26':
resolution: {integrity: sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw==}
'@volar/source-map@2.4.27':
resolution: {integrity: sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==}
'@volar/typescript@2.4.15':
resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
'@volar/typescript@2.4.26':
resolution: {integrity: sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA==}
'@volar/typescript@2.4.27':
resolution: {integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==}
@@ -3899,18 +3930,12 @@ packages:
'@vue/compiler-core@3.5.25':
resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==}
'@vue/compiler-core@3.5.26':
resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==}
'@vue/compiler-dom@3.5.13':
resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
'@vue/compiler-dom@3.5.25':
resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==}
'@vue/compiler-dom@3.5.26':
resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==}
'@vue/compiler-sfc@3.5.13':
resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
@@ -3929,17 +3954,23 @@ packages:
'@vue/devtools-api@6.6.3':
resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==}
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
'@vue/devtools-api@7.7.9':
resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==}
'@vue/devtools-core@8.0.5':
resolution: {integrity: sha512-dpCw8nl0GDBuiL9SaY0mtDxoGIEmU38w+TQiYEPOLhW03VDC0lfNMYXS/qhl4I0YlysGp04NLY4UNn6xgD0VIQ==}
peerDependencies:
vue: ^3.0.0
'@vue/devtools-kit@7.7.9':
resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==}
'@vue/devtools-kit@8.0.5':
resolution: {integrity: sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==}
'@vue/devtools-shared@7.7.9':
resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==}
'@vue/devtools-shared@8.0.5':
resolution: {integrity: sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==}
@@ -3982,9 +4013,6 @@ packages:
'@vue/shared@3.5.25':
resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==}
'@vue/shared@3.5.26':
resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==}
'@vue/test-utils@2.4.6':
resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
@@ -4171,8 +4199,8 @@ packages:
alien-signals@1.0.13:
resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
alien-signals@3.1.2:
resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
alien-signals@3.1.1:
resolution: {integrity: sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==}
ansi-align@3.0.1:
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
@@ -4493,6 +4521,9 @@ packages:
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
engines: {node: '>=18'}
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
clean-css@5.3.3:
resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
engines: {node: '>= 10.0'}
@@ -4695,8 +4726,8 @@ packages:
engines: {node: '>=4'}
hasBin: true
cssstyle@5.3.5:
resolution: {integrity: sha512-GlsEptulso7Jg0VaOZ8BXQi3AkYM5BOJKEO/rjMidSCq70FkIC5y0eawrCXeYzxgt3OCf4Ls+eoxN+/05vN0Ag==}
cssstyle@5.3.6:
resolution: {integrity: sha512-legscpSpgSAeGEe0TNcai97DKt9Vd9AsAdOL7Uoetb52Ar/8eJm3LIa39qpv8wWzLFlNG4vVvppQM+teaMPj3A==}
engines: {node: '>=20'}
csstype@3.2.3:
@@ -4945,10 +4976,6 @@ packages:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
entities@7.0.0:
resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==}
engines: {node: '>=0.12'}
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
@@ -5215,8 +5242,8 @@ packages:
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
expect-type@1.3.0:
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
expect-type@1.2.2:
resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==}
engines: {node: '>=12.0.0'}
exsolve@1.0.8:
@@ -6220,6 +6247,10 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@11.1.0:
resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==}
engines: {node: 20 || >=22}
lru-cache@11.2.4:
resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==}
engines: {node: 20 || >=22}
@@ -6803,6 +6834,9 @@ packages:
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
engines: {node: '>= 14.16'}
perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
perfect-debounce@2.0.0:
resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==}
@@ -6826,15 +6860,12 @@ packages:
engines: {node: '>=0.10'}
hasBin: true
pinia@2.2.2:
resolution: {integrity: sha512-ja2XqFWZC36mupU4z1ZzxeTApV7DOw44cV4dhQ9sGwun+N89v/XP7+j7q6TanS1u1tdbK4r+1BUx7heMaIdagA==}
pinia@3.0.4:
resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==}
peerDependencies:
'@vue/composition-api': ^1.4.0
typescript: '>=4.4.4'
vue: ^2.6.14 || ^3.3.0
typescript: '>=4.5.0'
vue: ^3.5.11
peerDependenciesMeta:
'@vue/composition-api':
optional: true
typescript:
optional: true
@@ -8111,8 +8142,8 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
webidl-conversions@8.0.0:
resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==}
webidl-conversions@8.0.1:
resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
engines: {node: '>=20'}
webpack-sources@3.3.3:
@@ -9218,6 +9249,8 @@ snapshots:
dependencies:
'@csstools/css-tokenizer': 3.0.4
'@csstools/css-syntax-patches-for-csstree@1.0.20': {}
'@csstools/css-syntax-patches-for-csstree@1.0.22': {}
'@csstools/css-tokenizer@3.0.4': {}
@@ -9447,7 +9480,7 @@ snapshots:
'@eslint/core': 0.17.0
levn: 0.4.1
'@exodus/bytes@1.7.0': {}
'@exodus/bytes@1.8.0': {}
'@firebase/analytics-compat@0.2.18(@firebase/app-compat@0.2.53)(@firebase/app@0.11.4)':
dependencies:
@@ -10611,9 +10644,9 @@ snapshots:
esquery: 1.6.0
typescript: 5.9.3
'@pinia/testing@1.0.3(pinia@2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))':
'@pinia/testing@1.0.3(pinia@3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))':
dependencies:
pinia: 2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
pinia: 3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -10916,13 +10949,13 @@ snapshots:
- encoding
- supports-color
'@sentry/vue@10.32.1(pinia@2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))(vue@3.5.13(typescript@5.9.3))':
'@sentry/vue@10.32.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)))(vue@3.5.13(typescript@5.9.3))':
dependencies:
'@sentry/browser': 10.32.1
'@sentry/core': 10.32.1
vue: 3.5.13(typescript@5.9.3)
optionalDependencies:
pinia: 2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
pinia: 3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3))
'@sinclair/typebox@0.34.40': {}
@@ -11736,12 +11769,18 @@ snapshots:
dependencies:
'@volar/source-map': 2.4.15
'@volar/language-core@2.4.26':
dependencies:
'@volar/source-map': 2.4.26
'@volar/language-core@2.4.27':
dependencies:
'@volar/source-map': 2.4.27
'@volar/source-map@2.4.15': {}
'@volar/source-map@2.4.26': {}
'@volar/source-map@2.4.27': {}
'@volar/typescript@2.4.15':
@@ -11750,6 +11789,12 @@ snapshots:
path-browserify: 1.0.1
vscode-uri: 3.1.0
'@volar/typescript@2.4.26':
dependencies:
'@volar/language-core': 2.4.26
path-browserify: 1.0.1
vscode-uri: 3.1.0
'@volar/typescript@2.4.27':
dependencies:
'@volar/language-core': 2.4.27
@@ -11768,7 +11813,7 @@ snapshots:
'@babel/types': 7.28.5
'@vue/babel-helper-vue-transform-on': 1.4.0
'@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.28.5)
'@vue/shared': 3.5.26
'@vue/shared': 3.5.25
optionalDependencies:
'@babel/core': 7.28.5
transitivePeerDependencies:
@@ -11801,14 +11846,6 @@ snapshots:
estree-walker: 2.0.2
source-map-js: 1.2.1
'@vue/compiler-core@3.5.26':
dependencies:
'@babel/parser': 7.28.5
'@vue/shared': 3.5.26
entities: 7.0.0
estree-walker: 2.0.2
source-map-js: 1.2.1
'@vue/compiler-dom@3.5.13':
dependencies:
'@vue/compiler-core': 3.5.13
@@ -11819,11 +11856,6 @@ snapshots:
'@vue/compiler-core': 3.5.25
'@vue/shared': 3.5.25
'@vue/compiler-dom@3.5.26':
dependencies:
'@vue/compiler-core': 3.5.26
'@vue/shared': 3.5.26
'@vue/compiler-sfc@3.5.13':
dependencies:
'@babel/parser': 7.28.5
@@ -11865,7 +11897,9 @@ snapshots:
'@vue/devtools-api@6.6.3': {}
'@vue/devtools-api@6.6.4': {}
'@vue/devtools-api@7.7.9':
dependencies:
'@vue/devtools-kit': 7.7.9
'@vue/devtools-core@8.0.5(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.2))(vue@3.5.13(typescript@5.9.3))':
dependencies:
@@ -11891,6 +11925,16 @@ snapshots:
transitivePeerDependencies:
- vite
'@vue/devtools-kit@7.7.9':
dependencies:
'@vue/devtools-shared': 7.7.9
birpc: 2.9.0
hookable: 5.5.3
mitt: 3.0.1
perfect-debounce: 1.0.0
speakingurl: 14.0.1
superjson: 2.2.2
'@vue/devtools-kit@8.0.5':
dependencies:
'@vue/devtools-shared': 8.0.5
@@ -11901,16 +11945,20 @@ snapshots:
speakingurl: 14.0.1
superjson: 2.2.2
'@vue/devtools-shared@7.7.9':
dependencies:
rfdc: 1.4.1
'@vue/devtools-shared@8.0.5':
dependencies:
rfdc: 1.4.1
'@vue/language-core@2.2.0(typescript@5.9.3)':
dependencies:
'@volar/language-core': 2.4.27
'@vue/compiler-dom': 3.5.26
'@volar/language-core': 2.4.26
'@vue/compiler-dom': 3.5.25
'@vue/compiler-vue2': 2.7.16
'@vue/shared': 3.5.26
'@vue/shared': 3.5.25
alien-signals: 0.4.14
minimatch: 9.0.5
muggle-string: 0.4.1
@@ -11921,9 +11969,9 @@ snapshots:
'@vue/language-core@2.2.12(typescript@5.9.3)':
dependencies:
'@volar/language-core': 2.4.15
'@vue/compiler-dom': 3.5.26
'@vue/compiler-dom': 3.5.25
'@vue/compiler-vue2': 2.7.16
'@vue/shared': 3.5.26
'@vue/shared': 3.5.25
alien-signals: 1.0.13
minimatch: 9.0.5
muggle-string: 0.4.1
@@ -11934,9 +11982,9 @@ snapshots:
'@vue/language-core@3.2.1':
dependencies:
'@volar/language-core': 2.4.27
'@vue/compiler-dom': 3.5.26
'@vue/shared': 3.5.26
alien-signals: 3.1.2
'@vue/compiler-dom': 3.5.25
'@vue/shared': 3.5.25
alien-signals: 3.1.1
muggle-string: 0.4.1
path-browserify: 1.0.1
picomatch: 4.0.3
@@ -11967,8 +12015,6 @@ snapshots:
'@vue/shared@3.5.25': {}
'@vue/shared@3.5.26': {}
'@vue/test-utils@2.4.6':
dependencies:
js-beautify: 1.15.1
@@ -12147,7 +12193,7 @@ snapshots:
alien-signals@1.0.13: {}
alien-signals@3.1.2: {}
alien-signals@3.1.1: {}
ansi-align@3.0.1:
dependencies:
@@ -12519,6 +12565,10 @@ snapshots:
chownr@3.0.0: {}
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
clean-css@5.3.3:
dependencies:
source-map: 0.6.1
@@ -12706,11 +12756,12 @@ snapshots:
cssesc@3.0.0: {}
cssstyle@5.3.5:
cssstyle@5.3.6:
dependencies:
'@asamuzakjp/css-color': 4.1.1
'@csstools/css-syntax-patches-for-csstree': 1.0.22
css-tree: 3.1.0
lru-cache: 11.2.4
csstype@3.2.3: {}
@@ -12948,8 +12999,6 @@ snapshots:
entities@6.0.1: {}
entities@7.0.0: {}
env-paths@2.2.1: {}
env-paths@3.0.0: {}
@@ -13341,7 +13390,7 @@ snapshots:
eventemitter3@5.0.1: {}
expect-type@1.3.0: {}
expect-type@1.2.2: {}
exsolve@1.0.8: {}
@@ -13744,7 +13793,7 @@ snapshots:
html-encoding-sniffer@6.0.0:
dependencies:
'@exodus/bytes': 1.7.0
'@exodus/bytes': 1.8.0
transitivePeerDependencies:
- '@exodus/crypto'
@@ -14157,8 +14206,8 @@ snapshots:
dependencies:
'@acemir/cssom': 0.9.30
'@asamuzakjp/dom-selector': 6.7.6
'@exodus/bytes': 1.7.0
cssstyle: 5.3.5
'@exodus/bytes': 1.8.0
cssstyle: 5.3.6
data-urls: 6.0.0
decimal.js: 10.6.0
html-encoding-sniffer: 6.0.0
@@ -14170,7 +14219,7 @@ snapshots:
symbol-tree: 3.2.4
tough-cookie: 6.0.0
w3c-xmlserializer: 5.0.0
webidl-conversions: 8.0.0
webidl-conversions: 8.0.1
whatwg-mimetype: 4.0.0
whatwg-url: 15.1.0
ws: 8.18.3
@@ -14426,6 +14475,8 @@ snapshots:
lru-cache@10.4.3: {}
lru-cache@11.1.0: {}
lru-cache@11.2.4: {}
lru-cache@5.1.1:
@@ -15299,7 +15350,7 @@ snapshots:
path-scurry@2.0.0:
dependencies:
lru-cache: 11.2.4
lru-cache: 11.1.0
minipass: 7.1.2
path-type@4.0.0: {}
@@ -15312,6 +15363,8 @@ snapshots:
pathval@2.0.1: {}
perfect-debounce@1.0.0: {}
perfect-debounce@2.0.0: {}
picocolors@1.1.1: {}
@@ -15324,11 +15377,10 @@ snapshots:
pidtree@0.6.0: {}
pinia@2.2.2(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)):
pinia@3.0.4(typescript@5.9.3)(vue@3.5.13(typescript@5.9.3)):
dependencies:
'@vue/devtools-api': 6.6.4
'@vue/devtools-api': 7.7.9
vue: 3.5.13(typescript@5.9.3)
vue-demi: 0.14.10(vue@3.5.13(typescript@5.9.3))
optionalDependencies:
typescript: 5.9.3
@@ -16184,7 +16236,7 @@ snapshots:
stylelint@16.26.1(typescript@5.9.3):
dependencies:
'@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
'@csstools/css-syntax-patches-for-csstree': 1.0.22
'@csstools/css-syntax-patches-for-csstree': 1.0.20
'@csstools/css-tokenizer': 3.0.4
'@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
'@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.1)
@@ -16688,7 +16740,7 @@ snapshots:
dependencies:
'@microsoft/api-extractor': 7.52.13(@types/node@24.10.4)
'@rollup/pluginutils': 5.3.0(rollup@4.53.5)
'@volar/typescript': 2.4.27
'@volar/typescript': 2.4.26
'@vue/language-core': 2.2.0(typescript@5.9.3)
compare-versions: 6.1.1
debug: 4.4.3
@@ -16801,7 +16853,7 @@ snapshots:
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5)
'@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5)
'@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.5)
'@vue/compiler-dom': 3.5.26
'@vue/compiler-dom': 3.5.25
kolorist: 1.8.0
magic-string: 0.30.21
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.2)
@@ -16816,7 +16868,7 @@ snapshots:
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5)
'@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5)
'@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.28.5)
'@vue/compiler-dom': 3.5.26
'@vue/compiler-dom': 3.5.25
kolorist: 1.8.0
magic-string: 0.30.21
vite: 7.3.0(@types/node@25.0.3)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.39.2)(tsx@4.19.4)(yaml@2.8.2)
@@ -16867,7 +16919,7 @@ snapshots:
'@vitest/spy': 4.0.16
'@vitest/utils': 4.0.16
es-module-lexer: 1.7.0
expect-type: 1.3.0
expect-type: 1.2.2
magic-string: 0.30.21
obug: 2.1.1
pathe: 2.0.3
@@ -16922,7 +16974,7 @@ snapshots:
dependencies:
'@babel/parser': 7.28.5
'@babel/types': 7.28.5
'@vue/compiler-dom': 3.5.26
'@vue/compiler-dom': 3.5.25
'@vue/compiler-sfc': 3.5.25
ast-types: 0.16.1
esm-resolve: 1.0.11
@@ -17004,7 +17056,7 @@ snapshots:
webidl-conversions@3.0.1: {}
webidl-conversions@8.0.0: {}
webidl-conversions@8.0.1: {}
webpack-sources@3.3.3: {}
@@ -17027,7 +17079,7 @@ snapshots:
whatwg-url@15.1.0:
dependencies:
tr46: 6.0.0
webidl-conversions: 8.0.0
webidl-conversions: 8.0.1
whatwg-url@5.0.0:
dependencies:

View File

@@ -47,6 +47,8 @@ catalog:
'@webgpu/types': ^0.1.66
algoliasearch: ^5.21.0
axios: ^1.8.2
class-variance-authority: 0.7.1
clsx: ^2.1.1
cross-env: ^10.1.0
cva: 1.0.0-beta.4
dotenv: ^16.4.5
@@ -82,6 +84,7 @@ catalog:
rollup-plugin-visualizer: ^6.0.4
storybook: ^10.1.9
stylelint: ^16.26.1
tailwind-merge: ^2.6.0
tailwindcss: ^4.1.12
tailwindcss-primeui: ^0.6.1
tsx: ^4.15.6

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { VariantProps } from 'class-variance-authority'
import type { ToggleGroupRootEmits, ToggleGroupRootProps } from 'reka-ui'
import { ToggleGroupRoot, useForwardPropsEmits } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { provide } from 'vue'
import type { toggleVariants } from '@/components/ui/toggle'
import { cn } from '@/utils/tailwindUtil'
type ToggleGroupVariants = VariantProps<typeof toggleVariants>
const props = defineProps<
ToggleGroupRootProps & {
class?: HTMLAttributes['class']
variant?: ToggleGroupVariants['variant']
size?: ToggleGroupVariants['size']
}
>()
const emits = defineEmits<ToggleGroupRootEmits>()
provide('toggleGroup', {
variant: props.variant,
size: props.size
})
const delegatedProps = reactiveOmit(props, 'class')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<ToggleGroupRoot
v-slot="slotProps"
v-bind="forwarded"
:class="cn('flex items-center justify-center gap-1', props.class)"
>
<slot v-bind="slotProps" />
</ToggleGroupRoot>
</template>

View File

@@ -0,0 +1,45 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { VariantProps } from 'class-variance-authority'
import type { ToggleGroupItemProps } from 'reka-ui'
import { ToggleGroupItem, useForwardProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { inject } from 'vue'
import { toggleVariants } from '@/components/ui/toggle'
import { cn } from '@/utils/tailwindUtil'
type ToggleGroupVariants = VariantProps<typeof toggleVariants>
const props = defineProps<
ToggleGroupItemProps & {
class?: HTMLAttributes['class']
variant?: ToggleGroupVariants['variant']
size?: ToggleGroupVariants['size']
}
>()
const context = inject<ToggleGroupVariants>('toggleGroup')
const delegatedProps = reactiveOmit(props, 'class', 'size', 'variant')
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<ToggleGroupItem
v-slot="slotProps"
v-bind="forwardedProps"
:class="
cn(
toggleVariants({
variant: context?.variant || variant,
size: context?.size || size
}),
props.class
)
"
>
<slot v-bind="slotProps" />
</ToggleGroupItem>
</template>

View File

@@ -0,0 +1,2 @@
export { default as ToggleGroup } from "./ToggleGroup.vue"
export { default as ToggleGroupItem } from "./ToggleGroupItem.vue"

View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { ToggleEmits, ToggleProps } from 'reka-ui'
import { Toggle, useForwardPropsEmits } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cn } from '@/utils/tailwindUtil'
import type { ToggleVariants } from '.'
import { toggleVariants } from '.'
const props = withDefaults(
defineProps<
ToggleProps & {
class?: HTMLAttributes['class']
variant?: ToggleVariants['variant']
size?: ToggleVariants['size']
}
>(),
{
variant: 'default',
size: 'default',
disabled: false
}
)
const emits = defineEmits<ToggleEmits>()
const delegatedProps = reactiveOmit(props, 'class', 'size', 'variant')
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<Toggle
v-slot="slotProps"
data-slot="toggle"
v-bind="forwarded"
:class="cn(toggleVariants({ variant, size }), props.class)"
>
<slot v-bind="slotProps" />
</Toggle>
</template>

View File

@@ -0,0 +1,28 @@
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
export { default as Toggle } from "./Toggle.vue"
export const toggleVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
{
variants: {
variant: {
default: "bg-transparent",
outline:
"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
},
size: {
default: "h-9 px-2 min-w-9",
sm: "h-8 px-1.5 min-w-8",
lg: "h-10 px-2.5 min-w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export type ToggleVariants = VariantProps<typeof toggleVariants>

View File

@@ -3,6 +3,7 @@ import PrimeVue from 'primevue/config'
import ToggleSwitch from 'primevue/toggleswitch'
import { describe, expect, it } from 'vitest'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import WidgetToggleSwitch from './WidgetToggleSwitch.vue'
@@ -10,7 +11,7 @@ import WidgetToggleSwitch from './WidgetToggleSwitch.vue'
describe('WidgetToggleSwitch Value Binding', () => {
const createMockWidget = (
value: boolean = false,
options: Record<string, any> = {},
options: Record<string, unknown> = {},
callback?: (value: boolean) => void
): SimplifiedWidget<boolean> => ({
name: 'test_toggle',
@@ -33,7 +34,7 @@ describe('WidgetToggleSwitch Value Binding', () => {
},
global: {
plugins: [PrimeVue],
components: { ToggleSwitch }
components: { ToggleSwitch, ToggleGroup, ToggleGroupItem }
}
})
}
@@ -150,43 +151,80 @@ describe('WidgetToggleSwitch Value Binding', () => {
})
describe('Label Display', () => {
it('displays label_off when toggle is false', () => {
it('uses ToggleGroup when labels are provided', () => {
const widget = createMockWidget(false, { on: 'Enabled', off: 'Disabled' })
const wrapper = mountComponent(widget, false)
expect(wrapper.text()).toContain('Disabled')
expect(wrapper.text()).not.toContain('Enabled')
expect(wrapper.findComponent({ name: 'ToggleGroup' }).exists()).toBe(true)
expect(wrapper.findComponent({ name: 'ToggleSwitch' }).exists()).toBe(
false
)
})
it('displays label_on when toggle is true', () => {
it('uses ToggleSwitch when no labels are provided', () => {
const widget = createMockWidget(false, {})
const wrapper = mountComponent(widget, false)
expect(wrapper.findComponent({ name: 'ToggleSwitch' }).exists()).toBe(
true
)
expect(wrapper.findComponent({ name: 'ToggleGroup' }).exists()).toBe(
false
)
})
it('displays both label_on and label_off in ToggleGroup', () => {
const widget = createMockWidget(false, { on: 'Enabled', off: 'Disabled' })
const wrapper = mountComponent(widget, false)
expect(wrapper.text()).toContain('Enabled')
expect(wrapper.text()).toContain('Disabled')
})
it('displays correct active state for false', () => {
const widget = createMockWidget(false, { on: 'Enabled', off: 'Disabled' })
const wrapper = mountComponent(widget, false)
const toggleGroup = wrapper.findComponent({ name: 'ToggleGroup' })
expect(toggleGroup.props('modelValue')).toBe('off')
})
it('displays correct active state for true', () => {
const widget = createMockWidget(true, { on: 'Enabled', off: 'Disabled' })
const wrapper = mountComponent(widget, true)
expect(wrapper.text()).toContain('Enabled')
expect(wrapper.text()).not.toContain('Disabled')
const toggleGroup = wrapper.findComponent({ name: 'ToggleGroup' })
expect(toggleGroup.props('modelValue')).toBe('on')
})
it('updates label when toggled', async () => {
it('updates active state when toggled', async () => {
const widget = createMockWidget(false, {
on: 'Markdown',
off: 'Plaintext'
})
const wrapper = mountComponent(widget, false)
expect(wrapper.text()).toContain('Plaintext')
const toggleGroup = wrapper.findComponent({ name: 'ToggleGroup' })
expect(toggleGroup.props('modelValue')).toBe('off')
await wrapper.setProps({ modelValue: true })
expect(wrapper.text()).toContain('Markdown')
expect(wrapper.text()).not.toContain('Plaintext')
expect(toggleGroup.props('modelValue')).toBe('on')
})
it('does not display label when options are not provided', () => {
const widget = createMockWidget(false, {})
it('emits update:modelValue when ToggleGroup item is clicked', async () => {
const widget = createMockWidget(false, {
on: 'Markdown',
off: 'Plaintext'
})
const wrapper = mountComponent(widget, false)
const labelSpan = wrapper.find('span')
expect(labelSpan.exists()).toBe(false)
const toggleGroup = wrapper.findComponent({ name: 'ToggleGroup' })
await toggleGroup.vm.$emit('update:modelValue', 'on')
const emitted = wrapper.emitted('update:modelValue')
expect(emitted).toBeDefined()
expect(emitted![0]).toContain(true)
})
})
})

View File

@@ -1,13 +1,30 @@
<template>
<WidgetLayoutField :widget>
<div v-if="currentLabel" class="ml-auto flex items-center gap-2">
<ToggleSwitch
v-model="modelValue"
v-bind="filteredProps"
:aria-label="widget.name"
/>
<span class="text-sm">{{ currentLabel }}</span>
</div>
<WidgetLayoutField :widget="widgetWithStyle">
<!-- Use ToggleGroup when explicit labels are provided -->
<ToggleGroup
v-if="hasLabels"
type="single"
:model-value="toggleGroupValue"
class="ml-auto gap-0 bg-node-component-surface"
@update:model-value="handleToggleGroupChange"
>
<ToggleGroupItem
value="off"
:aria-label="`${widget.name}: ${labelOff}`"
class="rounded-l-md rounded-r-none border-0 bg-transparent text-node-component-text data-[state=on]:!bg-white data-[state=on]:!text-black hover:bg-node-component-border/20 cursor-pointer"
>
{{ labelOff }}
</ToggleGroupItem>
<ToggleGroupItem
value="on"
:aria-label="`${widget.name}: ${labelOn}`"
class="rounded-l-none rounded-r-md border-0 bg-transparent text-node-component-text data-[state=on]:!bg-white data-[state=on]:!text-black hover:bg-node-component-border/20 cursor-pointer"
>
{{ labelOn }}
</ToggleGroupItem>
</ToggleGroup>
<!-- Use ToggleSwitch for implicit boolean states -->
<ToggleSwitch
v-else
v-model="modelValue"
@@ -22,6 +39,7 @@
import ToggleSwitch from 'primevue/toggleswitch'
import { computed } from 'vue'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import {
STANDARD_EXCLUDED_PROPS,
@@ -33,7 +51,7 @@ import WidgetLayoutField from './layout/WidgetLayoutField.vue'
interface BooleanWidgetOptions {
on?: string
off?: string
[key: string]: any
[key: string]: unknown
}
const { widget } = defineProps<{
@@ -46,9 +64,30 @@ const filteredProps = computed(() =>
filterWidgetProps(widget.options, STANDARD_EXCLUDED_PROPS)
)
const currentLabel = computed(() => {
return modelValue.value
? (widget.options?.on ?? (widget.options?.off ? 'true' : undefined))
: (widget.options?.off ?? (widget.options?.on ? 'false' : undefined))
const hasLabels = computed(() => {
return !!(widget.options?.on || widget.options?.off)
})
const labelOn = computed(() => widget.options?.on ?? 'true')
const labelOff = computed(() => widget.options?.off ?? 'false')
const toggleGroupValue = computed(() => {
return modelValue.value ? 'on' : 'off'
})
const handleToggleGroupChange = (value: unknown) => {
if (value === 'on') {
modelValue.value = true
} else if (value === 'off') {
modelValue.value = false
}
}
// Override WidgetLayoutField styling when using ToggleGroup
const widgetWithStyle = computed(() => ({
...widget,
borderStyle: hasLabels.value
? 'focus-within:!ring-0 !bg-transparent !rounded-none focus-within:!outline-none'
: undefined
}))
</script>