From 4fa21b5f0f4ef34f942a299d42ef5d6d007b6b1c Mon Sep 17 00:00:00 2001 From: Csongor Czezar Date: Fri, 26 Dec 2025 16:07:39 -0800 Subject: [PATCH 01/22] feat: add label support to boolean toggle widgets --- .../components/WidgetToggleSwitch.test.ts | 44 ++++++++++++++++++- .../widgets/components/WidgetToggleSwitch.vue | 19 +++++--- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts index 7e7a876484..2346ef3b4b 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts @@ -1,7 +1,6 @@ import { mount } from '@vue/test-utils' import PrimeVue from 'primevue/config' import ToggleSwitch from 'primevue/toggleswitch' -import type { ToggleSwitchProps } from 'primevue/toggleswitch' import { describe, expect, it } from 'vitest' import type { SimplifiedWidget } from '@/types/simplifiedWidget' @@ -11,7 +10,7 @@ import WidgetToggleSwitch from './WidgetToggleSwitch.vue' describe('WidgetToggleSwitch Value Binding', () => { const createMockWidget = ( value: boolean = false, - options: Partial = {}, + options: Record = {}, callback?: (value: boolean) => void ): SimplifiedWidget => ({ name: 'test_toggle', @@ -149,4 +148,45 @@ describe('WidgetToggleSwitch Value Binding', () => { expect(emitted![3]).toContain(false) }) }) + + describe('Label Display', () => { + it('displays label_off when toggle is false', () => { + const widget = createMockWidget(false, { on: 'Enabled', off: 'Disabled' }) + const wrapper = mountComponent(widget, false) + + expect(wrapper.text()).toContain('Disabled') + expect(wrapper.text()).not.toContain('Enabled') + }) + + it('displays label_on when toggle is 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') + }) + + it('updates label when toggled', async () => { + const widget = createMockWidget(false, { + on: 'Markdown', + off: 'Plaintext' + }) + const wrapper = mountComponent(widget, false) + + expect(wrapper.text()).toContain('Plaintext') + + await wrapper.setProps({ modelValue: true }) + + expect(wrapper.text()).toContain('Markdown') + expect(wrapper.text()).not.toContain('Plaintext') + }) + + it('does not display label when options are not provided', () => { + const widget = createMockWidget(false, {}) + const wrapper = mountComponent(widget, false) + + const labelSpan = wrapper.find('span') + expect(labelSpan.exists()).toBe(false) + }) + }) }) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue index b902b6655e..eb49d2aba1 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue @@ -1,11 +1,13 @@ @@ -30,4 +32,9 @@ const modelValue = defineModel() const filteredProps = computed(() => filterWidgetProps(widget.options, STANDARD_EXCLUDED_PROPS) ) + +const currentLabel = computed(() => { + const options = widget.options as { on?: string; off?: string } + return modelValue.value ? options.on : options.off +}) From b16202e3ea362e0f449b354812ec0bda9c5f95d0 Mon Sep 17 00:00:00 2001 From: Csongor Czezar Date: Fri, 26 Dec 2025 16:49:22 -0800 Subject: [PATCH 02/22] fix: render wrapper dynamically --- .../widgets/components/WidgetToggleSwitch.vue | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue index eb49d2aba1..d709492187 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue @@ -1,13 +1,20 @@ From 5fb3fc564696a9a9a458a81183af09001ee90b5a Mon Sep 17 00:00:00 2001 From: Csongor Czezar Date: Fri, 26 Dec 2025 17:03:32 -0800 Subject: [PATCH 03/22] fix: used generic typing --- .../widgets/components/WidgetToggleSwitch.vue | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue index d709492187..4a28acd4dd 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue @@ -30,8 +30,14 @@ import { import WidgetLayoutField from './layout/WidgetLayoutField.vue' +interface BooleanWidgetOptions { + on?: string + off?: string + [key: string]: any +} + const { widget } = defineProps<{ - widget: SimplifiedWidget + widget: SimplifiedWidget }>() const modelValue = defineModel() @@ -41,7 +47,6 @@ const filteredProps = computed(() => ) const currentLabel = computed(() => { - const options = widget.options as { on?: string; off?: string } - return modelValue.value ? options.on : options.off + return modelValue.value ? widget.options?.on : widget.options?.off }) From 795962f3c3cd6358f81ed2a6180fd592c9ec3b4d Mon Sep 17 00:00:00 2001 From: Csongor Czezar Date: Fri, 26 Dec 2025 17:18:14 -0800 Subject: [PATCH 04/22] fix: added fallback labels --- .../vueNodes/widgets/components/WidgetToggleSwitch.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue index 4a28acd4dd..ea2682de23 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue @@ -47,6 +47,8 @@ const filteredProps = computed(() => ) const currentLabel = computed(() => { - return modelValue.value ? widget.options?.on : widget.options?.off + return modelValue.value + ? (widget.options?.on ?? (widget.options?.off ? 'true' : undefined)) + : (widget.options?.off ?? (widget.options?.on ? 'false' : undefined)) }) From 035a0f250ce240cfa9a2bd47f8e475187d43319c Mon Sep 17 00:00:00 2001 From: Csongor Czezar Date: Tue, 30 Dec 2025 14:52:38 -0800 Subject: [PATCH 05/22] feat: add ToggleGroup support for labeled boolean widgets --- package.json | 3 + pnpm-lock.yaml | 228 +++++++++++------- pnpm-workspace.yaml | 3 + .../ui/toggle-group/ToggleGroup.vue | 41 ++++ .../ui/toggle-group/ToggleGroupItem.vue | 45 ++++ src/components/ui/toggle-group/index.ts | 2 + src/components/ui/toggle/Toggle.vue | 42 ++++ src/components/ui/toggle/index.ts | 28 +++ .../components/WidgetToggleSwitch.test.ts | 68 ++++-- .../widgets/components/WidgetToggleSwitch.vue | 67 +++-- 10 files changed, 410 insertions(+), 117 deletions(-) create mode 100644 src/components/ui/toggle-group/ToggleGroup.vue create mode 100644 src/components/ui/toggle-group/ToggleGroupItem.vue create mode 100644 src/components/ui/toggle-group/index.ts create mode 100644 src/components/ui/toggle/Toggle.vue create mode 100644 src/components/ui/toggle/index.ts diff --git a/package.json b/package.json index 6dcf03f5b4..e667ff8d3b 100644 --- a/package.json +++ b/package.json @@ -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:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6eea185643..0a97884665 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index bd4638b0d2..b85d2624a2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -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 diff --git a/src/components/ui/toggle-group/ToggleGroup.vue b/src/components/ui/toggle-group/ToggleGroup.vue new file mode 100644 index 0000000000..426e5fff32 --- /dev/null +++ b/src/components/ui/toggle-group/ToggleGroup.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/components/ui/toggle-group/ToggleGroupItem.vue b/src/components/ui/toggle-group/ToggleGroupItem.vue new file mode 100644 index 0000000000..2b4284c8d0 --- /dev/null +++ b/src/components/ui/toggle-group/ToggleGroupItem.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/components/ui/toggle-group/index.ts b/src/components/ui/toggle-group/index.ts new file mode 100644 index 0000000000..c6ce2fc976 --- /dev/null +++ b/src/components/ui/toggle-group/index.ts @@ -0,0 +1,2 @@ +export { default as ToggleGroup } from "./ToggleGroup.vue" +export { default as ToggleGroupItem } from "./ToggleGroupItem.vue" diff --git a/src/components/ui/toggle/Toggle.vue b/src/components/ui/toggle/Toggle.vue new file mode 100644 index 0000000000..920de4b4ed --- /dev/null +++ b/src/components/ui/toggle/Toggle.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/ui/toggle/index.ts b/src/components/ui/toggle/index.ts new file mode 100644 index 0000000000..d873390e7c --- /dev/null +++ b/src/components/ui/toggle/index.ts @@ -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 diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts index 2346ef3b4b..bd5affb844 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.test.ts @@ -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 = {}, + options: Record = {}, callback?: (value: boolean) => void ): SimplifiedWidget => ({ 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.findComponent({ name: 'ToggleGroup' }).exists()).toBe(true) + expect(wrapper.findComponent({ name: 'ToggleSwitch' }).exists()).toBe( + false + ) + }) + + 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') - expect(wrapper.text()).not.toContain('Enabled') }) - it('displays label_on when toggle is true', () => { + 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) }) }) }) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue index ea2682de23..7ce9a2f1bd 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue @@ -1,13 +1,30 @@ diff --git a/src/components/ui/toggle-group/toggleGroup.variants.ts b/src/components/ui/toggle-group/toggleGroup.variants.ts index 4147caee89..fbd7f47779 100644 --- a/src/components/ui/toggle-group/toggleGroup.variants.ts +++ b/src/components/ui/toggle-group/toggleGroup.variants.ts @@ -2,21 +2,11 @@ import type { VariantProps } from 'cva' import { cva } from 'cva' export const toggleGroupVariants = cva({ - base: 'flex gap-[var(--primitive-padding-padding-1,4px)] p-[var(--primitive-padding-padding-1,4px)] rounded-[var(--primitive-border-radius-rounded-sm,4px)]', - variants: { - variant: { - primary: 'bg-component-node-widget-background', - secondary: 'bg-component-node-widget-background', - inverted: 'bg-component-node-widget-background' - } - }, - defaultVariants: { - variant: 'primary' - } + base: 'flex gap-[var(--primitive-padding-padding-1,4px)] p-[var(--primitive-padding-padding-1,4px)] rounded-[var(--primitive-border-radius-rounded-sm,4px)] bg-component-node-widget-background' }) export const toggleGroupItemVariants = cva({ - base: 'flex-1 inline-flex items-center justify-center border-0 rounded-[var(--primitive-border-radius-rounded-sm,4px)] px-[var(--primitive-padding-padding-2,8px)] py-[var(--primitive-padding-padding-1,4px)] text-xs font-inter font-normal transition-colors cursor-pointer', + base: 'flex-1 inline-flex items-center justify-center border-0 rounded-[var(--primitive-border-radius-rounded-sm,4px)] px-[var(--primitive-padding-padding-2,8px)] py-[var(--primitive-padding-padding-1,4px)] text-xs font-inter font-normal transition-colors cursor-pointer overflow-hidden', variants: { variant: { primary: [ @@ -37,8 +27,10 @@ export const toggleGroupItemVariants = cva({ } }, defaultVariants: { - variant: 'primary' + variant: 'secondary' } }) -export type ToggleGroupVariants = VariantProps +export type ToggleGroupItemVariants = VariantProps< + typeof toggleGroupItemVariants +> diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue index 020f917f8b..3158ccb851 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetToggleSwitch.vue @@ -1,10 +1,12 @@