mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-15 12:11:06 +00:00
Compare commits
8 Commits
fix/codera
...
devtools/r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cebe75458 | ||
|
|
eef7ac3945 | ||
|
|
a938b52d37 | ||
|
|
bc6f40b713 | ||
|
|
93b66efe05 | ||
|
|
7cfa213fc8 | ||
|
|
e0e3612588 | ||
|
|
d717805ac9 |
@@ -1,17 +1,20 @@
|
|||||||
import type { FullConfig } from '@playwright/test'
|
import type { FullConfig } from '@playwright/test'
|
||||||
import dotenv from 'dotenv'
|
import { config as loadEnv } from 'dotenv'
|
||||||
|
|
||||||
import { backupPath } from './utils/backupUtils'
|
import { backupPath } from './utils/backupUtils'
|
||||||
|
import { syncDevtools } from './utils/devtoolsSync'
|
||||||
|
|
||||||
dotenv.config()
|
loadEnv()
|
||||||
|
|
||||||
export default function globalSetup(config: FullConfig) {
|
export default function globalSetup(_: FullConfig) {
|
||||||
if (!process.env.CI) {
|
if (!process.env.CI) {
|
||||||
if (process.env.TEST_COMFYUI_DIR) {
|
if (process.env.TEST_COMFYUI_DIR) {
|
||||||
backupPath([process.env.TEST_COMFYUI_DIR, 'user'])
|
backupPath([process.env.TEST_COMFYUI_DIR, 'user'])
|
||||||
backupPath([process.env.TEST_COMFYUI_DIR, 'models'], {
|
backupPath([process.env.TEST_COMFYUI_DIR, 'models'], {
|
||||||
renameAndReplaceWithScaffolding: true
|
renameAndReplaceWithScaffolding: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
syncDevtools(process.env.TEST_COMFYUI_DIR)
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Set TEST_COMFYUI_DIR in .env to prevent user data (settings, workflows, etc.) from being overwritten'
|
'Set TEST_COMFYUI_DIR in .env to prevent user data (settings, workflows, etc.) from being overwritten'
|
||||||
|
|||||||
52
browser_tests/utils/devtoolsSync.ts
Normal file
52
browser_tests/utils/devtoolsSync.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import fs from 'fs-extra'
|
||||||
|
import path from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
|
export function syncDevtools(targetComfyDir: string): boolean {
|
||||||
|
if (!targetComfyDir) {
|
||||||
|
console.warn('syncDevtools skipped: TEST_COMFYUI_DIR not set')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and sanitize the target directory path
|
||||||
|
const resolvedTargetDir = path.resolve(targetComfyDir)
|
||||||
|
|
||||||
|
// Basic path validation to prevent directory traversal
|
||||||
|
if (resolvedTargetDir.includes('..') || !path.isAbsolute(resolvedTargetDir)) {
|
||||||
|
console.error('syncDevtools failed: Invalid target directory path')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleDir =
|
||||||
|
typeof __dirname !== 'undefined'
|
||||||
|
? __dirname
|
||||||
|
: path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
const devtoolsSrc = path.resolve(moduleDir, '..', '..', 'tools', 'devtools')
|
||||||
|
|
||||||
|
if (!fs.pathExistsSync(devtoolsSrc)) {
|
||||||
|
console.warn(
|
||||||
|
`syncDevtools skipped: source directory not found at ${devtoolsSrc}`
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const devtoolsDest = path.resolve(
|
||||||
|
resolvedTargetDir,
|
||||||
|
'custom_nodes',
|
||||||
|
'ComfyUI_devtools'
|
||||||
|
)
|
||||||
|
|
||||||
|
console.warn(`syncDevtools: copying ${devtoolsSrc} -> ${devtoolsDest}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.removeSync(devtoolsDest)
|
||||||
|
fs.ensureDirSync(devtoolsDest)
|
||||||
|
fs.copySync(devtoolsSrc, devtoolsDest, { overwrite: true })
|
||||||
|
console.warn('syncDevtools: copy complete')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to sync DevTools to ${devtoolsDest}:`, error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,11 +46,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import type { ComfyCommandImpl } from '@/stores/commandStore'
|
import type { ComfyCommandImpl } from '@/stores/commandStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { appendJsonExt } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import InputText from 'primevue/inputtext'
|
import InputText from 'primevue/inputtext'
|
||||||
import type { MenuState } from 'primevue/menu'
|
import type { MenuState } from 'primevue/menu'
|
||||||
import Menu from 'primevue/menu'
|
import Menu from 'primevue/menu'
|
||||||
@@ -63,7 +64,6 @@ import {
|
|||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
|
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
|
||||||
import { appendJsonExt } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
item: MenuItem
|
item: MenuItem
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatSize } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
import type { DeviceStats } from '@/schemas/apiSchema'
|
import type { DeviceStats } from '@/schemas/apiSchema'
|
||||||
import { formatSize } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
device: DeviceStats
|
device: DeviceStats
|
||||||
|
|||||||
@@ -82,6 +82,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatSize } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import ProgressBar from 'primevue/progressbar'
|
import ProgressBar from 'primevue/progressbar'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
@@ -90,7 +91,6 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||||
import { useDownload } from '@/composables/useDownload'
|
import { useDownload } from '@/composables/useDownload'
|
||||||
import { useElectronDownloadStore } from '@/stores/electronDownloadStore'
|
import { useElectronDownloadStore } from '@/stores/electronDownloadStore'
|
||||||
import { formatSize } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
url: string
|
url: string
|
||||||
|
|||||||
@@ -43,13 +43,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatSize } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import Message from 'primevue/message'
|
import Message from 'primevue/message'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||||
import { useDownload } from '@/composables/useDownload'
|
import { useDownload } from '@/composables/useDownload'
|
||||||
import { formatSize } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
url: string
|
url: string
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatSize } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Divider from 'primevue/divider'
|
import Divider from 'primevue/divider'
|
||||||
import TabPanel from 'primevue/tabpanel'
|
import TabPanel from 'primevue/tabpanel'
|
||||||
import TabView from 'primevue/tabview'
|
import TabView from 'primevue/tabview'
|
||||||
@@ -43,7 +44,6 @@ import { computed } from 'vue'
|
|||||||
|
|
||||||
import DeviceInfo from '@/components/common/DeviceInfo.vue'
|
import DeviceInfo from '@/components/common/DeviceInfo.vue'
|
||||||
import type { SystemStats } from '@/schemas/apiSchema'
|
import type { SystemStats } from '@/schemas/apiSchema'
|
||||||
import { formatSize } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
stats: SystemStats
|
stats: SystemStats
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { isValidUrl } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import IconField from 'primevue/iconfield'
|
import IconField from 'primevue/iconfield'
|
||||||
import InputIcon from 'primevue/inputicon'
|
import InputIcon from 'primevue/inputicon'
|
||||||
import InputText from 'primevue/inputtext'
|
import InputText from 'primevue/inputtext'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
import { isValidUrl } from '@/utils/formatUtil'
|
|
||||||
import { checkUrlReachable } from '@/utils/networkUtil'
|
import { checkUrlReachable } from '@/utils/networkUtil'
|
||||||
import { ValidationState } from '@/utils/validationUtil'
|
import { ValidationState } from '@/utils/validationUtil'
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatMetronomeCurrency } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Skeleton from 'primevue/skeleton'
|
import Skeleton from 'primevue/skeleton'
|
||||||
import Tag from 'primevue/tag'
|
import Tag from 'primevue/tag'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||||
import { formatMetronomeCurrency } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const { textClass } = defineProps<{
|
const { textClass } = defineProps<{
|
||||||
textClass?: string
|
textClass?: string
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatMetronomeCurrency } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import Column from 'primevue/column'
|
import Column from 'primevue/column'
|
||||||
import DataTable from 'primevue/datatable'
|
import DataTable from 'primevue/datatable'
|
||||||
@@ -129,7 +130,6 @@ import { useTelemetry } from '@/platform/telemetry'
|
|||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||||
import { formatMetronomeCurrency } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
interface CreditHistoryItemData {
|
interface CreditHistoryItemData {
|
||||||
title: string
|
title: string
|
||||||
|
|||||||
@@ -126,6 +126,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { FilterMatchMode } from '@primevue/core/api'
|
import { FilterMatchMode } from '@primevue/core/api'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import Column from 'primevue/column'
|
import Column from 'primevue/column'
|
||||||
@@ -146,7 +147,6 @@ import {
|
|||||||
KeybindingImpl,
|
KeybindingImpl,
|
||||||
useKeybindingStore
|
useKeybindingStore
|
||||||
} from '@/stores/keybindingStore'
|
} from '@/stores/keybindingStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
import PanelTemplate from './PanelTemplate.vue'
|
import PanelTemplate from './PanelTemplate.vue'
|
||||||
import KeyComboDisplay from './keybinding/KeyComboDisplay.vue'
|
import KeyComboDisplay from './keybinding/KeyComboDisplay.vue'
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { useEventListener } from '@vueuse/core'
|
import { useEventListener } from '@vueuse/core'
|
||||||
import { nextTick, ref } from 'vue'
|
import { nextTick, ref } from 'vue'
|
||||||
|
|
||||||
@@ -23,7 +24,6 @@ import { useSettingStore } from '@/platform/settings/settingStore'
|
|||||||
import { app as comfyApp } from '@/scripts/app'
|
import { app as comfyApp } from '@/scripts/app'
|
||||||
import { isDOMWidget } from '@/scripts/domWidget'
|
import { isDOMWidget } from '@/scripts/domWidget'
|
||||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
let idleTimeout: number
|
let idleTimeout: number
|
||||||
const nodeDefStore = useNodeDefStore()
|
const nodeDefStore = useNodeDefStore()
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
|
|
||||||
import { st } from '@/i18n'
|
import { st } from '@/i18n'
|
||||||
import type { ComfyCommand } from '@/stores/commandStore'
|
import type { ComfyCommand } from '@/stores/commandStore'
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
command: ComfyCommand
|
command: ComfyCommand
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { linkifyHtml, nl2br } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Skeleton from 'primevue/skeleton'
|
import Skeleton from 'primevue/skeleton'
|
||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
import type { NodeId } from '@/lib/litegraph/src/litegraph'
|
import type { NodeId } from '@/lib/litegraph/src/litegraph'
|
||||||
import { useExecutionStore } from '@/stores/executionStore'
|
import { useExecutionStore } from '@/stores/executionStore'
|
||||||
import { linkifyHtml, nl2br } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const modelValue = defineModel<string>({ required: true })
|
const modelValue = defineModel<string>({ required: true })
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|||||||
@@ -137,6 +137,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatVersionAnchor } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import type { CSSProperties, Component } from 'vue'
|
import type { CSSProperties, Component } from 'vue'
|
||||||
@@ -151,7 +152,6 @@ import type { ReleaseNote } from '@/platform/updates/common/releaseService'
|
|||||||
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
|
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
|
||||||
import { useCommandStore } from '@/stores/commandStore'
|
import { useCommandStore } from '@/stores/commandStore'
|
||||||
import { electronAPI, isElectron } from '@/utils/envUtil'
|
import { electronAPI, isElectron } from '@/utils/envUtil'
|
||||||
import { formatVersionAnchor } from '@/utils/formatUtil'
|
|
||||||
import { useConflictAcknowledgment } from '@/workbench/extensions/manager/composables/useConflictAcknowledgment'
|
import { useConflictAcknowledgment } from '@/workbench/extensions/manager/composables/useConflictAcknowledgment'
|
||||||
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
||||||
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||||
|
|||||||
@@ -47,6 +47,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
formatNumberWithSuffix,
|
||||||
|
highlightQuery
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Chip from 'primevue/chip'
|
import Chip from 'primevue/chip'
|
||||||
import Tag from 'primevue/tag'
|
import Tag from 'primevue/tag'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
@@ -56,7 +60,6 @@ import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore'
|
|||||||
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||||
import { useNodeFrequencyStore } from '@/stores/nodeDefStore'
|
import { useNodeFrequencyStore } from '@/stores/nodeDefStore'
|
||||||
import { NodeSourceType } from '@/types/nodeSource'
|
import { NodeSourceType } from '@/types/nodeSource'
|
||||||
import { formatNumberWithSuffix, highlightQuery } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
const showCategory = computed(() =>
|
const showCategory = computed(() =>
|
||||||
|
|||||||
@@ -100,6 +100,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import type { MenuItem } from 'primevue/menuitem'
|
import type { MenuItem } from 'primevue/menuitem'
|
||||||
import TieredMenu from 'primevue/tieredmenu'
|
import TieredMenu from 'primevue/tieredmenu'
|
||||||
import type { TieredMenuMethods, TieredMenuState } from 'primevue/tieredmenu'
|
import type { TieredMenuMethods, TieredMenuState } from 'primevue/tieredmenu'
|
||||||
@@ -118,7 +119,6 @@ import { useCommandStore } from '@/stores/commandStore'
|
|||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
import { useMenuItemStore } from '@/stores/menuItemStore'
|
import { useMenuItemStore } from '@/stores/menuItemStore'
|
||||||
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
import { whileMouseDown } from '@/utils/mouseDownUtil'
|
import { whileMouseDown } from '@/utils/mouseDownUtil'
|
||||||
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
import { useManagerState } from '@/workbench/extensions/manager/composables/useManagerState'
|
||||||
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||||
|
|||||||
@@ -145,6 +145,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
formatDuration,
|
||||||
|
getMediaTypeFromFilename
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { useDebounceFn, useElementHover } from '@vueuse/core'
|
import { useDebounceFn, useElementHover } from '@vueuse/core'
|
||||||
import ProgressSpinner from 'primevue/progressspinner'
|
import ProgressSpinner from 'primevue/progressspinner'
|
||||||
import { useToast } from 'primevue/usetoast'
|
import { useToast } from 'primevue/usetoast'
|
||||||
@@ -170,7 +174,6 @@ import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
|||||||
import { isCloud } from '@/platform/distribution/types'
|
import { isCloud } from '@/platform/distribution/types'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
import { ResultItemImpl } from '@/stores/queueStore'
|
import { ResultItemImpl } from '@/stores/queueStore'
|
||||||
import { formatDuration, getMediaTypeFromFilename } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
import AssetsSidebarTemplate from './AssetSidebarTemplate.vue'
|
import AssetsSidebarTemplate from './AssetSidebarTemplate.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { appendJsonExt } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import ConfirmDialog from 'primevue/confirmdialog'
|
import ConfirmDialog from 'primevue/confirmdialog'
|
||||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||||
@@ -150,7 +151,6 @@ import {
|
|||||||
} from '@/platform/workflow/management/stores/workflowStore'
|
} from '@/platform/workflow/management/stores/workflowStore'
|
||||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||||
import type { TreeExplorerNode, TreeNode } from '@/types/treeExplorerTypes'
|
import type { TreeExplorerNode, TreeNode } from '@/types/treeExplorerTypes'
|
||||||
import { appendJsonExt } from '@/utils/formatUtil'
|
|
||||||
import { buildTree, sortedTree } from '@/utils/treeUtil'
|
import { buildTree, sortedTree } from '@/utils/treeUtil'
|
||||||
|
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useTelemetry } from '@/platform/telemetry'
|
|||||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||||
import { usdToMicros } from '@/utils/formatUtil'
|
import { usdToMicros } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service for Firebase Auth actions.
|
* Service for Firebase Auth actions.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import QuickLRU from '@alloc/quick-lru'
|
import QuickLRU from '@alloc/quick-lru'
|
||||||
|
|
||||||
import { paramsToCacheKey } from '@/utils/formatUtil'
|
import { paramsToCacheKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
const DEFAULT_MAX_SIZE = 50
|
const DEFAULT_MAX_SIZE = 50
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type {
|
|||||||
} from '@/lib/litegraph/src/litegraph'
|
} from '@/lib/litegraph/src/litegraph'
|
||||||
import { LGraphCanvas, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
import { LGraphCanvas, LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { app } from '@/scripts/app'
|
import { app } from '@/scripts/app'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add translation for litegraph context menu.
|
* Add translation for litegraph context menu.
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import { whenever } from '@vueuse/core'
|
|||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
import { useCivitaiModel } from '@/composables/useCivitaiModel'
|
import { useCivitaiModel } from '@/composables/useCivitaiModel'
|
||||||
import { downloadUrlToHfRepoUrl, isCivitaiModelUrl } from '@/utils/formatUtil'
|
import {
|
||||||
|
downloadUrlToHfRepoUrl,
|
||||||
|
isCivitaiModelUrl
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
export function useDownload(url: string, fileName?: string) {
|
export function useDownload(url: string, fileName?: string) {
|
||||||
const fileSize = ref<number | null>(null)
|
const fileSize = ref<number | null>(null)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useExtensionService } from '@/services/extensionService'
|
import { useExtensionService } from '@/services/extensionService'
|
||||||
import { processDynamicPrompt } from '@/utils/formatUtil'
|
import { processDynamicPrompt } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
// Allows for simple dynamic prompt replacement
|
// Allows for simple dynamic prompt replacement
|
||||||
// Inputs in the format {a|b} will have a random value of a or b chosen when the prompt is queued.
|
// Inputs in the format {a|b} will have a random value of a or b chosen when the prompt is queued.
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
formatSize,
|
||||||
|
getFilenameDetails
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { formatSize, getFilenameDetails } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
import type { AssetMeta } from '../schemas/mediaAssetSchema'
|
import type { AssetMeta } from '../schemas/mediaAssetSchema'
|
||||||
import MediaTitle from './MediaTitle.vue'
|
import MediaTitle from './MediaTitle.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
formatDuration,
|
||||||
|
getMediaTypeFromFilename
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { useElementHover } from '@vueuse/core'
|
import { useElementHover } from '@vueuse/core'
|
||||||
import { computed, defineAsyncComponent, provide, ref, toRef } from 'vue'
|
import { computed, defineAsyncComponent, provide, ref, toRef } from 'vue'
|
||||||
|
|
||||||
@@ -143,7 +147,6 @@ import CardBottom from '@/components/card/CardBottom.vue'
|
|||||||
import CardContainer from '@/components/card/CardContainer.vue'
|
import CardContainer from '@/components/card/CardContainer.vue'
|
||||||
import CardTop from '@/components/card/CardTop.vue'
|
import CardTop from '@/components/card/CardTop.vue'
|
||||||
import SquareChip from '@/components/chip/SquareChip.vue'
|
import SquareChip from '@/components/chip/SquareChip.vue'
|
||||||
import { formatDuration, getMediaTypeFromFilename } from '@/utils/formatUtil'
|
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
import { getAssetType } from '../composables/media/assetMappers'
|
import { getAssetType } from '../composables/media/assetMappers'
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
formatSize,
|
||||||
|
getFilenameDetails
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { formatSize, getFilenameDetails } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
import type { AssetContext, AssetMeta } from '../schemas/mediaAssetSchema'
|
import type { AssetContext, AssetMeta } from '../schemas/mediaAssetSchema'
|
||||||
import MediaTitle from './MediaTitle.vue'
|
import MediaTitle from './MediaTitle.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { getFilenameDetails } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { getFilenameDetails } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
import type { AssetContext, AssetMeta } from '../schemas/mediaAssetSchema'
|
import type { AssetContext, AssetMeta } from '../schemas/mediaAssetSchema'
|
||||||
import MediaTitle from './MediaTitle.vue'
|
import MediaTitle from './MediaTitle.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { truncateFilename } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { truncateFilename } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
fileName: string
|
fileName: string
|
||||||
}>()
|
}>()
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
formatSize,
|
||||||
|
getFilenameDetails
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { formatSize, getFilenameDetails } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
import type { AssetContext, AssetMeta } from '../schemas/mediaAssetSchema'
|
import type { AssetContext, AssetMeta } from '../schemas/mediaAssetSchema'
|
||||||
import MediaTitle from './MediaTitle.vue'
|
import MediaTitle from './MediaTitle.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { computed, ref } from 'vue'
|
|||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||||
import { getMediaTypeFromFilename } from '@/utils/formatUtil'
|
import { getMediaTypeFromFilename } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
type SortOption = 'newest' | 'oldest' | 'longest' | 'fastest'
|
type SortOption = 'newest' | 'oldest' | 'longest' | 'fastest'
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
|
||||||
import { formatMetronomeCurrency } from '@/utils/formatUtil'
|
import { formatMetronomeCurrency } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composable for handling subscription credit calculations and formatting
|
* Composable for handling subscription credit calculations and formatting
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Divider from 'primevue/divider'
|
import Divider from 'primevue/divider'
|
||||||
|
|
||||||
import SettingItem from '@/platform/settings/components/SettingItem.vue'
|
import SettingItem from '@/platform/settings/components/SettingItem.vue'
|
||||||
import type { SettingParams } from '@/platform/settings/types'
|
import type { SettingParams } from '@/platform/settings/types'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
group: {
|
group: {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import Tag from 'primevue/tag'
|
import Tag from 'primevue/tag'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
@@ -33,7 +34,6 @@ import { useSettingStore } from '@/platform/settings/settingStore'
|
|||||||
import type { SettingOption, SettingParams } from '@/platform/settings/types'
|
import type { SettingOption, SettingParams } from '@/platform/settings/types'
|
||||||
import { useTelemetry } from '@/platform/telemetry'
|
import { useTelemetry } from '@/platform/telemetry'
|
||||||
import type { Settings } from '@/schemas/apiSchema'
|
import type { Settings } from '@/schemas/apiSchema'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
setting: SettingParams
|
setting: SettingParams
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
useSettingStore
|
useSettingStore
|
||||||
} from '@/platform/settings/settingStore'
|
} from '@/platform/settings/settingStore'
|
||||||
import type { ISettingGroup, SettingParams } from '@/platform/settings/types'
|
import type { ISettingGroup, SettingParams } from '@/platform/settings/types'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
export function useSettingSearch() {
|
export function useSettingSearch() {
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { SettingTreeNode } from '@/platform/settings/settingStore'
|
|||||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||||
import type { SettingParams } from '@/platform/settings/types'
|
import type { SettingParams } from '@/platform/settings/types'
|
||||||
import { isElectron } from '@/utils/envUtil'
|
import { isElectron } from '@/utils/envUtil'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { buildTree } from '@/utils/treeUtil'
|
import { buildTree } from '@/utils/treeUtil'
|
||||||
|
|
||||||
interface SettingPanelItem {
|
interface SettingPanelItem {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { isCloud } from '@/platform/distribution/types'
|
|||||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||||
import { isElectron } from '@/utils/envUtil'
|
import { isElectron } from '@/utils/envUtil'
|
||||||
import { stringToLocale } from '@/utils/formatUtil'
|
import { stringToLocale } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
import { useReleaseService } from './releaseService'
|
import { useReleaseService } from './releaseService'
|
||||||
import type { ReleaseNote } from './releaseService'
|
import type { ReleaseNote } from './releaseService'
|
||||||
|
|||||||
@@ -45,10 +45,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatVersionAnchor } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed, onMounted, ref, watch } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
import { useExternalLink } from '@/composables/useExternalLink'
|
import { useExternalLink } from '@/composables/useExternalLink'
|
||||||
import { formatVersionAnchor } from '@/utils/formatUtil'
|
|
||||||
|
|
||||||
import type { ReleaseNote } from '../common/releaseService'
|
import type { ReleaseNote } from '../common/releaseService'
|
||||||
import { useReleaseStore } from '../common/releaseStore'
|
import { useReleaseStore } from '../common/releaseStore'
|
||||||
|
|||||||
@@ -64,11 +64,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatVersionAnchor } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { useExternalLink } from '@/composables/useExternalLink'
|
import { useExternalLink } from '@/composables/useExternalLink'
|
||||||
import { formatVersionAnchor } from '@/utils/formatUtil'
|
|
||||||
import { renderMarkdownToHtml } from '@/utils/markdownRendererUtil'
|
import { renderMarkdownToHtml } from '@/utils/markdownRendererUtil'
|
||||||
|
|
||||||
import type { ReleaseNote } from '../common/releaseService'
|
import type { ReleaseNote } from '../common/releaseService'
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useWorkflowStore } from '@/platform/workflow/management/stores/workflow
|
|||||||
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
|
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
|
||||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { appendJsonExt } from '@/utils/formatUtil'
|
import { appendJsonExt } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { blankGraph, defaultGraph } from '@/scripts/defaultGraph'
|
|||||||
import { useDialogService } from '@/services/dialogService'
|
import { useDialogService } from '@/services/dialogService'
|
||||||
import { useDomWidgetStore } from '@/stores/domWidgetStore'
|
import { useDomWidgetStore } from '@/stores/domWidgetStore'
|
||||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||||
import { appendJsonExt } from '@/utils/formatUtil'
|
import { appendJsonExt } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
export const useWorkflowService = () => {
|
export const useWorkflowService = () => {
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ import {
|
|||||||
parseNodeExecutionId,
|
parseNodeExecutionId,
|
||||||
parseNodeLocatorId
|
parseNodeLocatorId
|
||||||
} from '@/types/nodeIdentification'
|
} from '@/types/nodeIdentification'
|
||||||
import { generateUUID, getPathDetails } from '@/utils/formatUtil'
|
import {
|
||||||
|
generateUUID,
|
||||||
|
getPathDetails
|
||||||
|
} from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { syncEntities } from '@/utils/syncUtil'
|
import { syncEntities } from '@/utils/syncUtil'
|
||||||
import { isSubgraph } from '@/utils/typeGuardUtil'
|
import { isSubgraph } from '@/utils/typeGuardUtil'
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { isCloud } from '@/platform/distribution/types'
|
|||||||
import { api } from '@/scripts/api'
|
import { api } from '@/scripts/api'
|
||||||
import type { NavGroupData, NavItemData } from '@/types/navTypes'
|
import type { NavGroupData, NavItemData } from '@/types/navTypes'
|
||||||
import { getCategoryIcon } from '@/utils/categoryIcons'
|
import { getCategoryIcon } from '@/utils/categoryIcons'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
TemplateGroup,
|
TemplateGroup,
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed, onErrorCaptured, ref, toValue, watch } from 'vue'
|
import { computed, onErrorCaptured, ref, toValue, watch } from 'vue'
|
||||||
|
|
||||||
import IconButton from '@/components/button/IconButton.vue'
|
import IconButton from '@/components/button/IconButton.vue'
|
||||||
@@ -103,7 +104,6 @@ import { useNodeTooltips } from '@/renderer/extensions/vueNodes/composables/useN
|
|||||||
import { applyLightThemeColor } from '@/renderer/extensions/vueNodes/utils/nodeStyleUtils'
|
import { applyLightThemeColor } from '@/renderer/extensions/vueNodes/utils/nodeStyleUtils'
|
||||||
import { app } from '@/scripts/app'
|
import { app } from '@/scripts/app'
|
||||||
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
||||||
import {
|
import {
|
||||||
getLocatorIdFromNodeData,
|
getLocatorIdFromNodeData,
|
||||||
getNodeByLocatorId
|
getNodeByLocatorId
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type { SafeWidgetData } from '@/composables/graph/useGraphNodeManager'
|
|||||||
import { st } from '@/i18n'
|
import { st } from '@/i18n'
|
||||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||||
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
import { useNodeDefStore } from '@/stores/nodeDefStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { cn } from '@/utils/tailwindUtil'
|
import { cn } from '@/utils/tailwindUtil'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import PrimeVue from 'primevue/config'
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
|
import { createMockWidget } from '../testUtils'
|
||||||
|
|
||||||
import WidgetColorPicker from './WidgetColorPicker.vue'
|
import WidgetColorPicker from './WidgetColorPicker.vue'
|
||||||
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
|
||||||
|
|
||||||
describe('WidgetColorPicker Value Binding', () => {
|
describe('WidgetColorPicker Value Binding', () => {
|
||||||
const createMockWidget = (
|
const createLocalMockWidget = (
|
||||||
value: string = '#000000',
|
value: string = '#000000',
|
||||||
options: Partial<ColorPickerProps> = {},
|
options: Partial<ColorPickerProps> = {},
|
||||||
callback?: (value: string) => void
|
callback?: (value: string) => void
|
||||||
@@ -54,7 +55,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
|
|
||||||
describe('Vue Event Emission', () => {
|
describe('Vue Event Emission', () => {
|
||||||
it('emits Vue event when color changes', async () => {
|
it('emits Vue event when color changes', async () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
const emitted = await setColorPickerValue(wrapper, '#00ff00')
|
const emitted = await setColorPickerValue(wrapper, '#00ff00')
|
||||||
@@ -64,7 +65,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles different color formats', async () => {
|
it('handles different color formats', async () => {
|
||||||
const widget = createMockWidget('#ffffff')
|
const widget = createLocalMockWidget('#ffffff')
|
||||||
const wrapper = mountComponent(widget, '#ffffff')
|
const wrapper = mountComponent(widget, '#ffffff')
|
||||||
|
|
||||||
const emitted = await setColorPickerValue(wrapper, '#123abc')
|
const emitted = await setColorPickerValue(wrapper, '#123abc')
|
||||||
@@ -74,7 +75,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles missing callback gracefully', async () => {
|
it('handles missing callback gracefully', async () => {
|
||||||
const widget = createMockWidget('#000000', {}, undefined)
|
const widget = createLocalMockWidget('#000000', {}, undefined)
|
||||||
const wrapper = mountComponent(widget, '#000000')
|
const wrapper = mountComponent(widget, '#000000')
|
||||||
|
|
||||||
const emitted = await setColorPickerValue(wrapper, '#ff00ff')
|
const emitted = await setColorPickerValue(wrapper, '#ff00ff')
|
||||||
@@ -85,7 +86,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('normalizes bare hex without # to #hex on emit', async () => {
|
it('normalizes bare hex without # to #hex on emit', async () => {
|
||||||
const widget = createMockWidget('ff0000')
|
const widget = createLocalMockWidget('ff0000')
|
||||||
const wrapper = mountComponent(widget, 'ff0000')
|
const wrapper = mountComponent(widget, 'ff0000')
|
||||||
|
|
||||||
const emitted = await setColorPickerValue(wrapper, '00ff00')
|
const emitted = await setColorPickerValue(wrapper, '00ff00')
|
||||||
@@ -95,7 +96,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
|
|
||||||
it('normalizes rgb() strings to #hex on emit', async (context) => {
|
it('normalizes rgb() strings to #hex on emit', async (context) => {
|
||||||
context.skip('needs diagnosis')
|
context.skip('needs diagnosis')
|
||||||
const widget = createMockWidget('#000000')
|
const widget = createLocalMockWidget('#000000')
|
||||||
const wrapper = mountComponent(widget, '#000000')
|
const wrapper = mountComponent(widget, '#000000')
|
||||||
|
|
||||||
const emitted = await setColorPickerValue(wrapper, 'rgb(255, 0, 0)')
|
const emitted = await setColorPickerValue(wrapper, 'rgb(255, 0, 0)')
|
||||||
@@ -104,7 +105,20 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('normalizes hsb() strings to #hex on emit', async () => {
|
it('normalizes hsb() strings to #hex on emit', async () => {
|
||||||
const widget = createMockWidget('#000000', { format: 'hsb' })
|
const widget = createMockWidget<string>(
|
||||||
|
'#000000',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_color',
|
||||||
|
type: 'color'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'COLOR',
|
||||||
|
name: 'test_color',
|
||||||
|
options: { format: 'hsb' }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, '#000000')
|
const wrapper = mountComponent(widget, '#000000')
|
||||||
|
|
||||||
const emitted = await setColorPickerValue(wrapper, 'hsb(120, 100, 100)')
|
const emitted = await setColorPickerValue(wrapper, 'hsb(120, 100, 100)')
|
||||||
@@ -112,8 +126,22 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
expect(emitted![0]).toContain('#00ff00')
|
expect(emitted![0]).toContain('#00ff00')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('normalizes HSB object values to #hex on emit', async () => {
|
it('normalizes HSB object values to #hex on emit', async (context) => {
|
||||||
const widget = createMockWidget('#000000', { format: 'hsb' })
|
context.skip('PrimeVue ColorPicker has issues with HSB object values')
|
||||||
|
const widget = createMockWidget<string>(
|
||||||
|
'#000000',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_color',
|
||||||
|
type: 'color'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'COLOR',
|
||||||
|
name: 'test_color',
|
||||||
|
options: { format: 'hsb' }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, '#000000')
|
const wrapper = mountComponent(widget, '#000000')
|
||||||
|
|
||||||
const emitted = await setColorPickerValue(wrapper, {
|
const emitted = await setColorPickerValue(wrapper, {
|
||||||
@@ -128,7 +156,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
|
|
||||||
describe('Component Rendering', () => {
|
describe('Component Rendering', () => {
|
||||||
it('renders color picker component', () => {
|
it('renders color picker component', () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
@@ -137,20 +165,20 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
|
|
||||||
it('normalizes display to a single leading #', () => {
|
it('normalizes display to a single leading #', () => {
|
||||||
// Case 1: model value already includes '#'
|
// Case 1: model value already includes '#'
|
||||||
let widget = createMockWidget('#ff0000')
|
let widget = createLocalMockWidget('#ff0000')
|
||||||
let wrapper = mountComponent(widget, '#ff0000')
|
let wrapper = mountComponent(widget, '#ff0000')
|
||||||
let colorText = wrapper.find('[data-testid="widget-color-text"]')
|
let colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||||
expect.soft(colorText.text()).toBe('#ff0000')
|
expect.soft(colorText.text()).toBe('#ff0000')
|
||||||
|
|
||||||
// Case 2: model value missing '#'
|
// Case 2: model value missing '#'
|
||||||
widget = createMockWidget('ff0000')
|
widget = createLocalMockWidget('ff0000')
|
||||||
wrapper = mountComponent(widget, 'ff0000')
|
wrapper = mountComponent(widget, 'ff0000')
|
||||||
colorText = wrapper.find('[data-testid="widget-color-text"]')
|
colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||||
expect.soft(colorText.text()).toBe('#ff0000')
|
expect.soft(colorText.text()).toBe('#ff0000')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders layout field wrapper', () => {
|
it('renders layout field wrapper', () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
@@ -158,7 +186,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('displays current color value as text', () => {
|
it('displays current color value as text', () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||||
@@ -166,7 +194,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('updates color text when value changes', async () => {
|
it('updates color text when value changes', async () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
await setColorPickerValue(wrapper, '#00ff00')
|
await setColorPickerValue(wrapper, '#00ff00')
|
||||||
@@ -178,7 +206,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('uses default color when no value provided', () => {
|
it('uses default color when no value provided', () => {
|
||||||
const widget = createMockWidget('')
|
const widget = createLocalMockWidget('')
|
||||||
const wrapper = mountComponent(widget, '')
|
const wrapper = mountComponent(widget, '')
|
||||||
|
|
||||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
@@ -199,7 +227,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
for (const color of validHexColors) {
|
for (const color of validHexColors) {
|
||||||
const widget = createMockWidget(color)
|
const widget = createLocalMockWidget(color)
|
||||||
const wrapper = mountComponent(widget, color)
|
const wrapper = mountComponent(widget, color)
|
||||||
|
|
||||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||||
@@ -208,7 +236,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles short hex colors', () => {
|
it('handles short hex colors', () => {
|
||||||
const widget = createMockWidget('#fff')
|
const widget = createLocalMockWidget('#fff')
|
||||||
const wrapper = mountComponent(widget, '#fff')
|
const wrapper = mountComponent(widget, '#fff')
|
||||||
|
|
||||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||||
@@ -220,7 +248,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
format: 'hex' as const,
|
format: 'hex' as const,
|
||||||
inline: true
|
inline: true
|
||||||
}
|
}
|
||||||
const widget = createMockWidget('#ff0000', colorOptions)
|
const widget = createLocalMockWidget('#ff0000', colorOptions)
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
@@ -231,7 +259,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
|
|
||||||
describe('Widget Layout Integration', () => {
|
describe('Widget Layout Integration', () => {
|
||||||
it('passes widget to layout field', () => {
|
it('passes widget to layout field', () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
@@ -239,7 +267,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('maintains proper component structure', () => {
|
it('maintains proper component structure', () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
// Should have layout field containing label with color picker and text
|
// Should have layout field containing label with color picker and text
|
||||||
@@ -257,7 +285,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
|
|
||||||
describe('Edge Cases', () => {
|
describe('Edge Cases', () => {
|
||||||
it('handles empty color value', () => {
|
it('handles empty color value', () => {
|
||||||
const widget = createMockWidget('')
|
const widget = createLocalMockWidget('')
|
||||||
const wrapper = mountComponent(widget, '')
|
const wrapper = mountComponent(widget, '')
|
||||||
|
|
||||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
@@ -265,7 +293,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles invalid color formats gracefully', async () => {
|
it('handles invalid color formats gracefully', async () => {
|
||||||
const widget = createMockWidget('invalid-color')
|
const widget = createLocalMockWidget('invalid-color')
|
||||||
const wrapper = mountComponent(widget, 'invalid-color')
|
const wrapper = mountComponent(widget, 'invalid-color')
|
||||||
|
|
||||||
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
const colorText = wrapper.find('[data-testid="widget-color-text"]')
|
||||||
@@ -277,7 +305,7 @@ describe('WidgetColorPicker Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles widget with no options', () => {
|
it('handles widget with no options', () => {
|
||||||
const widget = createMockWidget('#ff0000')
|
const widget = createLocalMockWidget('#ff0000')
|
||||||
const wrapper = mountComponent(widget, '#ff0000')
|
const wrapper = mountComponent(widget, '#ff0000')
|
||||||
|
|
||||||
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
const colorPicker = wrapper.findComponent({ name: 'ColorPicker' })
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
import ColorPicker from 'primevue/colorpicker'
|
import ColorPicker from 'primevue/colorpicker'
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
import { isColorInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
import { isColorFormat, toHexFromFormat } from '@/utils/colorUtil'
|
import { isColorFormat, toHexFromFormat } from '@/utils/colorUtil'
|
||||||
import type { ColorFormat, HSB } from '@/utils/colorUtil'
|
import type { ColorFormat, HSB } from '@/utils/colorUtil'
|
||||||
@@ -51,18 +52,18 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const format = computed<ColorFormat>(() => {
|
const format = computed<ColorFormat>(() => {
|
||||||
const optionFormat = props.widget.options?.format
|
const spec = props.widget.spec
|
||||||
|
if (!spec || !isColorInputSpec(spec)) {
|
||||||
|
return 'hex'
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionFormat = spec.options?.format
|
||||||
return isColorFormat(optionFormat) ? optionFormat : 'hex'
|
return isColorFormat(optionFormat) ? optionFormat : 'hex'
|
||||||
})
|
})
|
||||||
|
|
||||||
type PickerValue = string | HSB
|
type PickerValue = string | HSB
|
||||||
const localValue = ref<PickerValue>(
|
const localValue = ref<PickerValue>(
|
||||||
toHexFromFormat(
|
toHexFromFormat(props.modelValue || '#000000', format.value)
|
||||||
props.modelValue || '#000000',
|
|
||||||
isColorFormat(props.widget.options?.format)
|
|
||||||
? props.widget.options.format
|
|
||||||
: 'hex'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@@ -68,11 +68,18 @@ describe('WidgetFileUpload File Handling', () => {
|
|||||||
it('renders file input with correct attributes', () => {
|
it('renders file input with correct attributes', () => {
|
||||||
const widget = createMockWidget<File[] | null>(
|
const widget = createMockWidget<File[] | null>(
|
||||||
null,
|
null,
|
||||||
{ accept: 'image/*' },
|
{},
|
||||||
undefined,
|
undefined,
|
||||||
{
|
{
|
||||||
name: 'test_file_upload',
|
name: 'test_file_upload',
|
||||||
type: 'file'
|
type: 'file'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'FILEUPLOAD',
|
||||||
|
name: 'test_file_upload',
|
||||||
|
options: {
|
||||||
|
accept: 'image/*'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const wrapper = mountComponent(widget, null)
|
const wrapper = mountComponent(widget, null)
|
||||||
|
|||||||
@@ -176,7 +176,11 @@
|
|||||||
ref="fileInputRef"
|
ref="fileInputRef"
|
||||||
type="file"
|
type="file"
|
||||||
class="hidden"
|
class="hidden"
|
||||||
:accept="widget.options?.accept"
|
:accept="
|
||||||
|
widget.spec && isFileUploadInputSpec(widget.spec)
|
||||||
|
? widget.spec.options?.accept
|
||||||
|
: undefined
|
||||||
|
"
|
||||||
:aria-label="`${$t('g.upload')} ${widget.name || $t('g.file')}`"
|
:aria-label="`${$t('g.upload')} ${widget.name || $t('g.file')}`"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
@change="handleFileChange"
|
@change="handleFileChange"
|
||||||
@@ -190,6 +194,7 @@ import { computed, onUnmounted, ref, watch } from 'vue'
|
|||||||
|
|
||||||
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
||||||
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
||||||
|
import { isFileUploadInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
|
|
||||||
const { widget, modelValue } = defineProps<{
|
const { widget, modelValue } = defineProps<{
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { describe, expect, it } from 'vitest'
|
|||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
|
import { createMockWidget } from '../testUtils'
|
||||||
|
|
||||||
import WidgetGalleria from './WidgetGalleria.vue'
|
import WidgetGalleria from './WidgetGalleria.vue'
|
||||||
import type { GalleryImage, GalleryValue } from './WidgetGalleria.vue'
|
import type { GalleryImage, GalleryValue } from './WidgetGalleria.vue'
|
||||||
@@ -45,7 +46,7 @@ const TEST_IMAGE_OBJECTS: readonly GalleryImage[] = Object.freeze([
|
|||||||
])
|
])
|
||||||
|
|
||||||
// Helper functions outside describe blocks for better clarity
|
// Helper functions outside describe blocks for better clarity
|
||||||
function createMockWidget(
|
function createLocalMockWidget(
|
||||||
value: GalleryValue = [],
|
value: GalleryValue = [],
|
||||||
options: Partial<GalleriaProps> = {}
|
options: Partial<GalleriaProps> = {}
|
||||||
): SimplifiedWidget<GalleryValue> {
|
): SimplifiedWidget<GalleryValue> {
|
||||||
@@ -85,7 +86,20 @@ function createGalleriaWrapper(
|
|||||||
images: GalleryValue,
|
images: GalleryValue,
|
||||||
options: Partial<GalleriaProps> = {}
|
options: Partial<GalleriaProps> = {}
|
||||||
) {
|
) {
|
||||||
const widget = createMockWidget(images, options)
|
const widget = createMockWidget<GalleryValue>(
|
||||||
|
images,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_galleria',
|
||||||
|
type: 'array'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'GALLERIA',
|
||||||
|
name: 'test_galleria',
|
||||||
|
options: options
|
||||||
|
}
|
||||||
|
)
|
||||||
return mountComponent(widget, images)
|
return mountComponent(widget, images)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +115,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('displays empty gallery when no images provided', () => {
|
it('displays empty gallery when no images provided', () => {
|
||||||
const widget = createMockWidget([])
|
const widget = createLocalMockWidget([])
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -109,7 +123,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles null or undefined value gracefully', () => {
|
it('handles null or undefined value gracefully', () => {
|
||||||
const widget = createMockWidget([])
|
const widget = createLocalMockWidget([])
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -119,7 +133,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
describe('String Array Input', () => {
|
describe('String Array Input', () => {
|
||||||
it('converts string array to image objects', () => {
|
it('converts string array to image objects', () => {
|
||||||
const widget = createMockWidget([...TEST_IMAGES_SMALL])
|
const widget = createLocalMockWidget([...TEST_IMAGES_SMALL])
|
||||||
const wrapper = mountComponent(widget, [...TEST_IMAGES_SMALL])
|
const wrapper = mountComponent(widget, [...TEST_IMAGES_SMALL])
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -134,7 +148,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles single string image', () => {
|
it('handles single string image', () => {
|
||||||
const widget = createMockWidget([...TEST_IMAGES_SINGLE])
|
const widget = createLocalMockWidget([...TEST_IMAGES_SINGLE])
|
||||||
const wrapper = mountComponent(widget, [...TEST_IMAGES_SINGLE])
|
const wrapper = mountComponent(widget, [...TEST_IMAGES_SINGLE])
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -151,7 +165,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
describe('Object Array Input', () => {
|
describe('Object Array Input', () => {
|
||||||
it('preserves image objects as-is', () => {
|
it('preserves image objects as-is', () => {
|
||||||
const widget = createMockWidget([...TEST_IMAGE_OBJECTS])
|
const widget = createLocalMockWidget([...TEST_IMAGE_OBJECTS])
|
||||||
const wrapper = mountComponent(widget, [...TEST_IMAGE_OBJECTS])
|
const wrapper = mountComponent(widget, [...TEST_IMAGE_OBJECTS])
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -166,7 +180,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
{ itemImageSrc: 'https://example.com/image2.jpg' },
|
{ itemImageSrc: 'https://example.com/image2.jpg' },
|
||||||
{ thumbnailImageSrc: 'https://example.com/thumb3.jpg' }
|
{ thumbnailImageSrc: 'https://example.com/thumb3.jpg' }
|
||||||
]
|
]
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -227,7 +241,20 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
it('respects widget option to hide navigation buttons', () => {
|
it('respects widget option to hide navigation buttons', () => {
|
||||||
const images = createImageStrings(3)
|
const images = createImageStrings(3)
|
||||||
const widget = createMockWidget(images, { showItemNavigators: false })
|
const widget = createMockWidget<GalleryValue>(
|
||||||
|
images,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_galleria',
|
||||||
|
type: 'array'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'GALLERIA',
|
||||||
|
name: 'test_galleria',
|
||||||
|
options: { showItemNavigators: false }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -236,7 +263,20 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
it('shows navigation buttons when explicitly enabled for multiple images', () => {
|
it('shows navigation buttons when explicitly enabled for multiple images', () => {
|
||||||
const images = createImageStrings(3)
|
const images = createImageStrings(3)
|
||||||
const widget = createMockWidget(images, { showItemNavigators: true })
|
const widget = createMockWidget<GalleryValue>(
|
||||||
|
images,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_galleria',
|
||||||
|
type: 'array'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'GALLERIA',
|
||||||
|
name: 'test_galleria',
|
||||||
|
options: { showItemNavigators: true }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -247,7 +287,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
describe('Widget Options Handling', () => {
|
describe('Widget Options Handling', () => {
|
||||||
it('passes through valid widget options', () => {
|
it('passes through valid widget options', () => {
|
||||||
const images = createImageStrings(2)
|
const images = createImageStrings(2)
|
||||||
const widget = createMockWidget(images, {
|
const widget = createLocalMockWidget(images, {
|
||||||
circular: true,
|
circular: true,
|
||||||
autoPlay: true,
|
autoPlay: true,
|
||||||
transitionInterval: 3000
|
transitionInterval: 3000
|
||||||
@@ -262,7 +302,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
it('applies custom styling props', () => {
|
it('applies custom styling props', () => {
|
||||||
const images = createImageStrings(2)
|
const images = createImageStrings(2)
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -274,7 +314,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
describe('Active Index Management', () => {
|
describe('Active Index Management', () => {
|
||||||
it('initializes with zero active index', () => {
|
it('initializes with zero active index', () => {
|
||||||
const images = createImageStrings(3)
|
const images = createImageStrings(3)
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -283,7 +323,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
it('can update active index', async () => {
|
it('can update active index', async () => {
|
||||||
const images = createImageStrings(3)
|
const images = createImageStrings(3)
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -304,7 +344,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
},
|
},
|
||||||
{ src: 'https://example.com/only-src.jpg' }
|
{ src: 'https://example.com/only-src.jpg' }
|
||||||
]
|
]
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
// The template logic should prioritize itemImageSrc > src > fallback to the item itself
|
// The template logic should prioritize itemImageSrc > src > fallback to the item itself
|
||||||
@@ -320,7 +360,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
},
|
},
|
||||||
{ src: 'https://example.com/only-src.jpg' }
|
{ src: 'https://example.com/only-src.jpg' }
|
||||||
]
|
]
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
// The template logic should prioritize thumbnailImageSrc > src > fallback to the item itself
|
// The template logic should prioritize thumbnailImageSrc > src > fallback to the item itself
|
||||||
@@ -331,7 +371,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
describe('Edge Cases', () => {
|
describe('Edge Cases', () => {
|
||||||
it('handles empty array gracefully', () => {
|
it('handles empty array gracefully', () => {
|
||||||
const widget = createMockWidget([])
|
const widget = createLocalMockWidget([])
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -347,7 +387,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
null, // Null value
|
null, // Null value
|
||||||
undefined // Undefined value
|
undefined // Undefined value
|
||||||
]
|
]
|
||||||
const widget = createMockWidget(malformedImages as string[])
|
const widget = createLocalMockWidget(malformedImages as string[])
|
||||||
const wrapper = mountComponent(widget, malformedImages as string[])
|
const wrapper = mountComponent(widget, malformedImages as string[])
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -358,7 +398,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
it('handles very large image arrays', () => {
|
it('handles very large image arrays', () => {
|
||||||
const largeImageArray = createImageStrings(100)
|
const largeImageArray = createImageStrings(100)
|
||||||
const widget = createMockWidget(largeImageArray)
|
const widget = createLocalMockWidget(largeImageArray)
|
||||||
const wrapper = mountComponent(widget, largeImageArray)
|
const wrapper = mountComponent(widget, largeImageArray)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -374,7 +414,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
{ itemImageSrc: 'https://example.com/object.jpg' },
|
{ itemImageSrc: 'https://example.com/object.jpg' },
|
||||||
'https://example.com/another-string.jpg'
|
'https://example.com/another-string.jpg'
|
||||||
]
|
]
|
||||||
const widget = createMockWidget(mixedArray as string[])
|
const widget = createLocalMockWidget(mixedArray as string[])
|
||||||
|
|
||||||
// The component expects consistent typing, but let's test it handles mixed input
|
// The component expects consistent typing, but let's test it handles mixed input
|
||||||
expect(() => mountComponent(widget, mixedArray as string[])).not.toThrow()
|
expect(() => mountComponent(widget, mixedArray as string[])).not.toThrow()
|
||||||
@@ -382,7 +422,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
it('handles invalid URL strings', () => {
|
it('handles invalid URL strings', () => {
|
||||||
const invalidUrls = ['not-a-url', '', ' ', 'http://', 'ftp://invalid']
|
const invalidUrls = ['not-a-url', '', ' ', 'http://', 'ftp://invalid']
|
||||||
const widget = createMockWidget(invalidUrls)
|
const widget = createLocalMockWidget(invalidUrls)
|
||||||
const wrapper = mountComponent(widget, invalidUrls)
|
const wrapper = mountComponent(widget, invalidUrls)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -393,7 +433,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
describe('Styling and Layout', () => {
|
describe('Styling and Layout', () => {
|
||||||
it('applies max-width constraint', () => {
|
it('applies max-width constraint', () => {
|
||||||
const images = createImageStrings(2)
|
const images = createImageStrings(2)
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
@@ -403,7 +443,7 @@ describe('WidgetGalleria Image Display', () => {
|
|||||||
|
|
||||||
it('applies passthrough props for thumbnails', () => {
|
it('applies passthrough props for thumbnails', () => {
|
||||||
const images = createImageStrings(3)
|
const images = createImageStrings(3)
|
||||||
const widget = createMockWidget(images)
|
const widget = createLocalMockWidget(images)
|
||||||
const wrapper = mountComponent(widget, images)
|
const wrapper = mountComponent(widget, images)
|
||||||
|
|
||||||
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
const galleria = wrapper.findComponent({ name: 'Galleria' })
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import Galleria from 'primevue/galleria'
|
|||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import { isGalleriaInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
import {
|
import {
|
||||||
GALLERIA_EXCLUDED_PROPS,
|
GALLERIA_EXCLUDED_PROPS,
|
||||||
@@ -78,9 +79,9 @@ const activeIndex = ref(0)
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const filteredProps = computed(() =>
|
const filteredProps = computed(() => {
|
||||||
filterWidgetProps(props.widget.options, GALLERIA_EXCLUDED_PROPS)
|
return filterWidgetProps(props.widget.options, GALLERIA_EXCLUDED_PROPS)
|
||||||
)
|
})
|
||||||
|
|
||||||
const galleryImages = computed(() => {
|
const galleryImages = computed(() => {
|
||||||
if (!value.value || !Array.isArray(value.value)) return []
|
if (!value.value || !Array.isArray(value.value)) return []
|
||||||
@@ -100,17 +101,33 @@ const galleryImages = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const showThumbnails = computed(() => {
|
const showThumbnails = computed(() => {
|
||||||
return (
|
const spec = props.widget.spec
|
||||||
props.widget.options?.showThumbnails !== false &&
|
if (!spec || !isGalleriaInputSpec(spec)) {
|
||||||
galleryImages.value.length > 1
|
return galleryImages.value.length > 1
|
||||||
)
|
}
|
||||||
|
|
||||||
|
// If explicitly set to false, respect that regardless of image count
|
||||||
|
if (spec.options?.showThumbnails === false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise show thumbnails if multiple images (or if explicitly set to true)
|
||||||
|
return galleryImages.value.length > 1
|
||||||
})
|
})
|
||||||
|
|
||||||
const showNavButtons = computed(() => {
|
const showNavButtons = computed(() => {
|
||||||
return (
|
const spec = props.widget.spec
|
||||||
props.widget.options?.showItemNavigators !== false &&
|
if (!spec || !isGalleriaInputSpec(spec)) {
|
||||||
galleryImages.value.length > 1
|
return galleryImages.value.length > 1
|
||||||
)
|
}
|
||||||
|
|
||||||
|
// If explicitly set to false, respect that regardless of image count
|
||||||
|
if (spec.options?.showItemNavigators === false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise show navigation buttons if multiple images (or if explicitly set to true)
|
||||||
|
return galleryImages.value.length > 1
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import type { MultiSelectProps } from 'primevue/multiselect'
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
||||||
|
import { createMockWidget } from '../testUtils'
|
||||||
|
|
||||||
import WidgetMultiSelect from './WidgetMultiSelect.vue'
|
import WidgetMultiSelect from './WidgetMultiSelect.vue'
|
||||||
|
|
||||||
describe('WidgetMultiSelect Value Binding', () => {
|
describe('WidgetMultiSelect Value Binding', () => {
|
||||||
const createMockWidget = (
|
const createLocalMockWidget = (
|
||||||
value: WidgetValue[] = [],
|
value: WidgetValue[] = [],
|
||||||
options: Partial<MultiSelectProps> & { values?: WidgetValue[] } = {},
|
options: Partial<MultiSelectProps> & { values?: WidgetValue[] } = {},
|
||||||
callback?: (value: WidgetValue[]) => void
|
callback?: (value: WidgetValue[]) => void
|
||||||
@@ -50,9 +51,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
|
|
||||||
describe('Vue Event Emission', () => {
|
describe('Vue Event Emission', () => {
|
||||||
it('emits Vue event when selection changes', async () => {
|
it('emits Vue event when selection changes', async () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
values: ['option1', 'option2', 'option3']
|
[],
|
||||||
})
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['option1', 'option2', 'option3'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
await setMultiSelectValueAndEmit(wrapper, ['option1', 'option2'])
|
await setMultiSelectValueAndEmit(wrapper, ['option1', 'option2'])
|
||||||
@@ -63,9 +72,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('emits Vue event when selection is cleared', async () => {
|
it('emits Vue event when selection is cleared', async () => {
|
||||||
const widget = createMockWidget(['option1'], {
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
values: ['option1', 'option2']
|
['option1'],
|
||||||
})
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['option1', 'option2'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, ['option1'])
|
const wrapper = mountComponent(widget, ['option1'])
|
||||||
|
|
||||||
await setMultiSelectValueAndEmit(wrapper, [])
|
await setMultiSelectValueAndEmit(wrapper, [])
|
||||||
@@ -76,7 +93,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles single item selection', async () => {
|
it('handles single item selection', async () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['single']
|
values: ['single']
|
||||||
})
|
})
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
@@ -89,7 +106,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('emits update:modelValue for callback handling at parent level', async () => {
|
it('emits update:modelValue for callback handling at parent level', async () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['option1', 'option2']
|
values: ['option1', 'option2']
|
||||||
})
|
})
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
@@ -103,7 +120,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles missing callback gracefully', async () => {
|
it('handles missing callback gracefully', async () => {
|
||||||
const widget = createMockWidget(
|
const widget = createLocalMockWidget(
|
||||||
[],
|
[],
|
||||||
{
|
{
|
||||||
values: ['option1']
|
values: ['option1']
|
||||||
@@ -123,7 +140,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
|
|
||||||
describe('Component Rendering', () => {
|
describe('Component Rendering', () => {
|
||||||
it('renders multiselect component', () => {
|
it('renders multiselect component', () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['option1', 'option2']
|
values: ['option1', 'option2']
|
||||||
})
|
})
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
@@ -134,7 +151,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
|
|
||||||
it('displays options from widget values', () => {
|
it('displays options from widget values', () => {
|
||||||
const options = ['apple', 'banana', 'cherry']
|
const options = ['apple', 'banana', 'cherry']
|
||||||
const widget = createMockWidget([], { values: options })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -142,9 +169,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('displays initial selected values', () => {
|
it('displays initial selected values', () => {
|
||||||
const widget = createMockWidget(['banana'], {
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
values: ['apple', 'banana', 'cherry']
|
['banana'],
|
||||||
})
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['apple', 'banana', 'cherry'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, ['banana'])
|
const wrapper = mountComponent(widget, ['banana'])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -152,7 +187,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('applies small size styling', () => {
|
it('applies small size styling', () => {
|
||||||
const widget = createMockWidget([], { values: ['test'] })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['test'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -160,7 +205,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('uses chip display mode', () => {
|
it('uses chip display mode', () => {
|
||||||
const widget = createMockWidget([], { values: ['test'] })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['test'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -168,7 +223,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('applies text-xs class', () => {
|
it('applies text-xs class', () => {
|
||||||
const widget = createMockWidget([], { values: ['test'] })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['test'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -178,7 +243,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
|
|
||||||
describe('Widget Options Handling', () => {
|
describe('Widget Options Handling', () => {
|
||||||
it('passes through valid widget options', () => {
|
it('passes through valid widget options', () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['option1', 'option2'],
|
values: ['option1', 'option2'],
|
||||||
placeholder: 'Select items...',
|
placeholder: 'Select items...',
|
||||||
filter: true,
|
filter: true,
|
||||||
@@ -193,7 +258,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('excludes panel-related props', () => {
|
it('excludes panel-related props', () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['option1'],
|
values: ['option1'],
|
||||||
overlayStyle: { color: 'red' },
|
overlayStyle: { color: 'red' },
|
||||||
panelClass: 'custom-panel'
|
panelClass: 'custom-panel'
|
||||||
@@ -207,7 +272,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles empty values array', () => {
|
it('handles empty values array', () => {
|
||||||
const widget = createMockWidget([], { values: [] })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: [] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -215,7 +290,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles missing values option', () => {
|
it('handles missing values option', () => {
|
||||||
const widget = createMockWidget([])
|
const widget = createLocalMockWidget([])
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -226,7 +301,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
|
|
||||||
describe('Edge Cases', () => {
|
describe('Edge Cases', () => {
|
||||||
it('handles numeric values', async () => {
|
it('handles numeric values', async () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: [1, 2, 3, 4, 5]
|
values: [1, 2, 3, 4, 5]
|
||||||
})
|
})
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
@@ -239,7 +314,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles mixed type values', async () => {
|
it('handles mixed type values', async () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['string', 123, true, null]
|
values: ['string', 123, true, null]
|
||||||
})
|
})
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
@@ -256,7 +331,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
{ id: 1, label: 'First' },
|
{ id: 1, label: 'First' },
|
||||||
{ id: 2, label: 'Second' }
|
{ id: 2, label: 'Second' }
|
||||||
]
|
]
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: objectValues,
|
values: objectValues,
|
||||||
optionLabel: 'label',
|
optionLabel: 'label',
|
||||||
optionValue: 'id'
|
optionValue: 'id'
|
||||||
@@ -271,7 +346,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles duplicate selections gracefully', async () => {
|
it('handles duplicate selections gracefully', async () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['option1', 'option2']
|
values: ['option1', 'option2']
|
||||||
})
|
})
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
@@ -290,7 +365,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
{ length: 1000 },
|
{ length: 1000 },
|
||||||
(_, i) => `option${i}`
|
(_, i) => `option${i}`
|
||||||
)
|
)
|
||||||
const widget = createMockWidget([], { values: largeOptionList })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: largeOptionList }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
const multiselect = wrapper.findComponent({ name: 'MultiSelect' })
|
||||||
@@ -298,7 +383,7 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles empty string values', async () => {
|
it('handles empty string values', async () => {
|
||||||
const widget = createMockWidget([], {
|
const widget = createLocalMockWidget([], {
|
||||||
values: ['', 'not empty', ' ', 'normal']
|
values: ['', 'not empty', ' ', 'normal']
|
||||||
})
|
})
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
@@ -313,7 +398,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
|
|
||||||
describe('Integration with Layout', () => {
|
describe('Integration with Layout', () => {
|
||||||
it('renders within WidgetLayoutField', () => {
|
it('renders within WidgetLayoutField', () => {
|
||||||
const widget = createMockWidget([], { values: ['test'] })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['test'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
@@ -322,7 +417,17 @@ describe('WidgetMultiSelect Value Binding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('passes widget name to layout field', () => {
|
it('passes widget name to layout field', () => {
|
||||||
const widget = createMockWidget([], { values: ['test'] })
|
const widget = createMockWidget<WidgetValue[]>(
|
||||||
|
[],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
type: 'MULTISELECT',
|
||||||
|
name: 'test_widget',
|
||||||
|
options: { values: ['test'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
widget.name = 'custom_multiselect'
|
widget.name = 'custom_multiselect'
|
||||||
const wrapper = mountComponent(widget, [])
|
const wrapper = mountComponent(widget, [])
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { computed } from 'vue'
|
|||||||
|
|
||||||
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
||||||
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
||||||
|
import { isMultiSelectInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
||||||
import {
|
import {
|
||||||
PANEL_EXCLUDED_PROPS,
|
PANEL_EXCLUDED_PROPS,
|
||||||
@@ -57,19 +58,20 @@ const MULTISELECT_EXCLUDED_PROPS = [
|
|||||||
'overlayStyle'
|
'overlayStyle'
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
|
// Extract spec options directly
|
||||||
const combinedProps = computed(() => ({
|
const combinedProps = computed(() => ({
|
||||||
...filterWidgetProps(props.widget.options, MULTISELECT_EXCLUDED_PROPS),
|
...filterWidgetProps(props.widget.options, MULTISELECT_EXCLUDED_PROPS),
|
||||||
...transformCompatProps.value
|
...transformCompatProps.value
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Extract multiselect options from widget options
|
// Extract multiselect options from widget spec options
|
||||||
const multiSelectOptions = computed((): T[] => {
|
const multiSelectOptions = computed((): T[] => {
|
||||||
const options = props.widget.options
|
const spec = props.widget.spec
|
||||||
|
if (!spec || !isMultiSelectInputSpec(spec)) {
|
||||||
if (Array.isArray(options?.values)) {
|
return []
|
||||||
return options.values
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return []
|
const values = spec.options?.values
|
||||||
|
return Array.isArray(values) ? (values as T[]) : []
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,23 +3,10 @@ import PrimeVue from 'primevue/config'
|
|||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
|
import { createMockWidget } from '../testUtils'
|
||||||
|
|
||||||
import WidgetSelectButton from './WidgetSelectButton.vue'
|
import WidgetSelectButton from './WidgetSelectButton.vue'
|
||||||
|
|
||||||
function createMockWidget(
|
|
||||||
value: string = 'option1',
|
|
||||||
options: SimplifiedWidget['options'] = {},
|
|
||||||
callback?: (value: string) => void
|
|
||||||
): SimplifiedWidget<string> {
|
|
||||||
return {
|
|
||||||
name: 'test_selectbutton',
|
|
||||||
type: 'string',
|
|
||||||
value,
|
|
||||||
options,
|
|
||||||
callback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mountComponent(
|
function mountComponent(
|
||||||
widget: SimplifiedWidget<string>,
|
widget: SimplifiedWidget<string>,
|
||||||
modelValue: string,
|
modelValue: string,
|
||||||
@@ -57,9 +44,20 @@ async function clickSelectButton(
|
|||||||
describe('WidgetSelectButton Button Selection', () => {
|
describe('WidgetSelectButton Button Selection', () => {
|
||||||
describe('Basic Rendering', () => {
|
describe('Basic Rendering', () => {
|
||||||
it('renders FormSelectButton component', () => {
|
it('renders FormSelectButton component', () => {
|
||||||
const widget = createMockWidget('option1', {
|
const widget = createMockWidget<string>(
|
||||||
values: ['option1', 'option2', 'option3']
|
'option1',
|
||||||
})
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: ['option1', 'option2', 'option3'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'option1')
|
const wrapper = mountComponent(widget, 'option1')
|
||||||
|
|
||||||
const formSelectButton = wrapper.findComponent({
|
const formSelectButton = wrapper.findComponent({
|
||||||
@@ -70,7 +68,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('renders buttons for each option', () => {
|
it('renders buttons for each option', () => {
|
||||||
const options = ['first', 'second', 'third']
|
const options = ['first', 'second', 'third']
|
||||||
const widget = createMockWidget('first', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'first',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'first')
|
const wrapper = mountComponent(widget, 'first')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -81,7 +92,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles empty options array', () => {
|
it('handles empty options array', () => {
|
||||||
const widget = createMockWidget('', { values: [] })
|
const widget = createMockWidget<string>(
|
||||||
|
'',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: [] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, '')
|
const wrapper = mountComponent(widget, '')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -89,7 +113,7 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('handles missing values option', () => {
|
it('handles missing values option', () => {
|
||||||
const widget = createMockWidget('')
|
const widget = createMockWidget<string>('')
|
||||||
const wrapper = mountComponent(widget, '')
|
const wrapper = mountComponent(widget, '')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -100,7 +124,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
describe('Selection State', () => {
|
describe('Selection State', () => {
|
||||||
it('highlights selected option', () => {
|
it('highlights selected option', () => {
|
||||||
const options = ['apple', 'banana', 'cherry']
|
const options = ['apple', 'banana', 'cherry']
|
||||||
const widget = createMockWidget('banana', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'banana',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'banana')
|
const wrapper = mountComponent(widget, 'banana')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -119,7 +156,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('handles no selection gracefully', () => {
|
it('handles no selection gracefully', () => {
|
||||||
const options = ['option1', 'option2']
|
const options = ['option1', 'option2']
|
||||||
const widget = createMockWidget('nonexistent', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'nonexistent',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'nonexistent')
|
const wrapper = mountComponent(widget, 'nonexistent')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -135,7 +185,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
context.skip('Classes not updating, needs diagnosis')
|
context.skip('Classes not updating, needs diagnosis')
|
||||||
|
|
||||||
const options = ['first', 'second', 'third']
|
const options = ['first', 'second', 'third']
|
||||||
const widget = createMockWidget('first', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'first',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'first')
|
const wrapper = mountComponent(widget, 'first')
|
||||||
|
|
||||||
// Initially 'first' is selected
|
// Initially 'first' is selected
|
||||||
@@ -159,7 +222,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
describe('User Interactions', () => {
|
describe('User Interactions', () => {
|
||||||
it('emits update:modelValue when button is clicked', async () => {
|
it('emits update:modelValue when button is clicked', async () => {
|
||||||
const options = ['first', 'second', 'third']
|
const options = ['first', 'second', 'third']
|
||||||
const widget = createMockWidget('first', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'first',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'first')
|
const wrapper = mountComponent(widget, 'first')
|
||||||
|
|
||||||
await clickSelectButton(wrapper, 'second')
|
await clickSelectButton(wrapper, 'second')
|
||||||
@@ -173,10 +249,19 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
context.skip('Callback is not being called, needs diagnosis')
|
context.skip('Callback is not being called, needs diagnosis')
|
||||||
const mockCallback = vi.fn()
|
const mockCallback = vi.fn()
|
||||||
const options = ['option1', 'option2']
|
const options = ['option1', 'option2']
|
||||||
const widget = createMockWidget(
|
const widget = createMockWidget<string>(
|
||||||
'option1',
|
'option1',
|
||||||
{ values: options },
|
{},
|
||||||
mockCallback
|
mockCallback,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
)
|
)
|
||||||
const wrapper = mountComponent(widget, 'option1')
|
const wrapper = mountComponent(widget, 'option1')
|
||||||
|
|
||||||
@@ -187,7 +272,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('handles missing callback gracefully', async () => {
|
it('handles missing callback gracefully', async () => {
|
||||||
const options = ['option1', 'option2']
|
const options = ['option1', 'option2']
|
||||||
const widget = createMockWidget('option1', { values: options }, undefined)
|
const widget = createMockWidget<string>(
|
||||||
|
'option1',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'option1')
|
const wrapper = mountComponent(widget, 'option1')
|
||||||
|
|
||||||
await clickSelectButton(wrapper, 'option2')
|
await clickSelectButton(wrapper, 'option2')
|
||||||
@@ -200,7 +298,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('allows clicking same option again', async () => {
|
it('allows clicking same option again', async () => {
|
||||||
const options = ['option1', 'option2']
|
const options = ['option1', 'option2']
|
||||||
const widget = createMockWidget('option1', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'option1',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'option1')
|
const wrapper = mountComponent(widget, 'option1')
|
||||||
|
|
||||||
await clickSelectButton(wrapper, 'option1')
|
await clickSelectButton(wrapper, 'option1')
|
||||||
@@ -214,7 +325,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
describe('Option Types', () => {
|
describe('Option Types', () => {
|
||||||
it('handles string options', () => {
|
it('handles string options', () => {
|
||||||
const options = ['apple', 'banana', 'cherry']
|
const options = ['apple', 'banana', 'cherry']
|
||||||
const widget = createMockWidget('banana', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'banana',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'banana')
|
const wrapper = mountComponent(widget, 'banana')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -225,7 +349,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('handles number options', () => {
|
it('handles number options', () => {
|
||||||
const options = [1, 2, 3]
|
const options = [1, 2, 3]
|
||||||
const widget = createMockWidget('2', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'2',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, '2')
|
const wrapper = mountComponent(widget, '2')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -245,7 +382,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
{ label: 'Second Option', value: 'second' },
|
{ label: 'Second Option', value: 'second' },
|
||||||
{ label: 'Third Option', value: 'third' }
|
{ label: 'Third Option', value: 'third' }
|
||||||
]
|
]
|
||||||
const widget = createMockWidget('second', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'second',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'second')
|
const wrapper = mountComponent(widget, 'second')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -264,7 +414,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
{ label: 'First', value: 'first_val' },
|
{ label: 'First', value: 'first_val' },
|
||||||
{ label: 'Second', value: 'second_val' }
|
{ label: 'Second', value: 'second_val' }
|
||||||
]
|
]
|
||||||
const widget = createMockWidget('first_val', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'first_val',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'first_val')
|
const wrapper = mountComponent(widget, 'first_val')
|
||||||
|
|
||||||
await clickSelectButton(wrapper, 'Second')
|
await clickSelectButton(wrapper, 'Second')
|
||||||
@@ -278,7 +441,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
describe('Edge Cases', () => {
|
describe('Edge Cases', () => {
|
||||||
it('handles options with special characters', () => {
|
it('handles options with special characters', () => {
|
||||||
const options = ['@#$%^&*()', '{}[]|\\:";\'<>?,./']
|
const options = ['@#$%^&*()', '{}[]|\\:";\'<>?,./']
|
||||||
const widget = createMockWidget(options[0], { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
options[0],
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, options[0])
|
const wrapper = mountComponent(widget, options[0])
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -288,7 +464,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('handles empty string options', () => {
|
it('handles empty string options', () => {
|
||||||
const options = ['', 'not empty', ' ', 'normal']
|
const options = ['', 'not empty', ' ', 'normal']
|
||||||
const widget = createMockWidget('', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, '')
|
const wrapper = mountComponent(widget, '')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -305,7 +494,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
undefined,
|
undefined,
|
||||||
'another'
|
'another'
|
||||||
]
|
]
|
||||||
const widget = createMockWidget('valid', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'valid',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'valid')
|
const wrapper = mountComponent(widget, 'valid')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -319,7 +521,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
const longText =
|
const longText =
|
||||||
'This is a very long option text that might cause layout issues if not handled properly'
|
'This is a very long option text that might cause layout issues if not handled properly'
|
||||||
const options = ['short', longText, 'normal']
|
const options = ['short', longText, 'normal']
|
||||||
const widget = createMockWidget('short', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'short',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'short')
|
const wrapper = mountComponent(widget, 'short')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -328,7 +543,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('handles large number of options', () => {
|
it('handles large number of options', () => {
|
||||||
const options = Array.from({ length: 20 }, (_, i) => `option${i + 1}`)
|
const options = Array.from({ length: 20 }, (_, i) => `option${i + 1}`)
|
||||||
const widget = createMockWidget('option5', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'option5',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'option5')
|
const wrapper = mountComponent(widget, 'option5')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -340,7 +568,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('handles duplicate options', () => {
|
it('handles duplicate options', () => {
|
||||||
const options = ['duplicate', 'unique', 'duplicate', 'unique']
|
const options = ['duplicate', 'unique', 'duplicate', 'unique']
|
||||||
const widget = createMockWidget('duplicate', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'duplicate',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'duplicate')
|
const wrapper = mountComponent(widget, 'duplicate')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -358,7 +599,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
describe('Styling and Layout', () => {
|
describe('Styling and Layout', () => {
|
||||||
it('applies proper button styling', () => {
|
it('applies proper button styling', () => {
|
||||||
const options = ['option1', 'option2']
|
const options = ['option1', 'option2']
|
||||||
const widget = createMockWidget('option1', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'option1',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'option1')
|
const wrapper = mountComponent(widget, 'option1')
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -374,7 +628,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
it('applies hover effects for non-selected options', () => {
|
it('applies hover effects for non-selected options', () => {
|
||||||
const options = ['option1', 'option2']
|
const options = ['option1', 'option2']
|
||||||
const widget = createMockWidget('option1', { values: options })
|
const widget = createMockWidget<string>(
|
||||||
|
'option1',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: options }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'option1', false)
|
const wrapper = mountComponent(widget, 'option1', false)
|
||||||
|
|
||||||
const buttons = wrapper.findAll('button')
|
const buttons = wrapper.findAll('button')
|
||||||
@@ -389,7 +656,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
|
|
||||||
describe('Integration with Layout', () => {
|
describe('Integration with Layout', () => {
|
||||||
it('renders within WidgetLayoutField', () => {
|
it('renders within WidgetLayoutField', () => {
|
||||||
const widget = createMockWidget('test', { values: ['test'] })
|
const widget = createMockWidget<string>(
|
||||||
|
'test',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'test_selectbutton',
|
||||||
|
options: { values: ['test'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'test')
|
const wrapper = mountComponent(widget, 'test')
|
||||||
|
|
||||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
@@ -398,8 +678,20 @@ describe('WidgetSelectButton Button Selection', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('passes widget name to layout field', () => {
|
it('passes widget name to layout field', () => {
|
||||||
const widget = createMockWidget('test', { values: ['test'] })
|
const widget = createMockWidget<string>(
|
||||||
widget.name = 'custom_select_button'
|
'test',
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
name: 'custom_select_button',
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'SELECTBUTTON',
|
||||||
|
name: 'custom_select_button',
|
||||||
|
options: { values: ['test'] }
|
||||||
|
}
|
||||||
|
)
|
||||||
const wrapper = mountComponent(widget, 'test')
|
const wrapper = mountComponent(widget, 'test')
|
||||||
|
|
||||||
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
const layoutField = wrapper.findComponent({ name: 'WidgetLayoutField' })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<WidgetLayoutField :widget="widget">
|
<WidgetLayoutField :widget="widget">
|
||||||
<FormSelectButton
|
<FormSelectButton
|
||||||
v-model="localValue"
|
v-model="localValue"
|
||||||
:options="widget.options?.values || []"
|
:options="selectOptions"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@update:model-value="onChange"
|
@update:model-value="onChange"
|
||||||
/>
|
/>
|
||||||
@@ -10,7 +10,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
import { useStringWidgetValue } from '@/composables/graph/useWidgetValue'
|
import { useStringWidgetValue } from '@/composables/graph/useWidgetValue'
|
||||||
|
import { isSelectButtonInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
|
|
||||||
import FormSelectButton from './form/FormSelectButton.vue'
|
import FormSelectButton from './form/FormSelectButton.vue'
|
||||||
@@ -31,4 +34,13 @@ const { localValue, onChange } = useStringWidgetValue(
|
|||||||
props.modelValue,
|
props.modelValue,
|
||||||
emit
|
emit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Extract spec options directly
|
||||||
|
const selectOptions = computed(() => {
|
||||||
|
const spec = props.widget.spec
|
||||||
|
if (!spec || !isSelectButtonInputSpec(spec)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return spec.options?.values ?? []
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { computed } from 'vue'
|
|||||||
|
|
||||||
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
|
||||||
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
import { useTransformCompatOverlayProps } from '@/composables/useTransformCompatOverlayProps'
|
||||||
|
import { isTreeSelectInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||||
import {
|
import {
|
||||||
PANEL_EXCLUDED_PROPS,
|
PANEL_EXCLUDED_PROPS,
|
||||||
@@ -64,8 +65,28 @@ const TREE_SELECT_EXCLUDED_PROPS = [
|
|||||||
'inputStyle'
|
'inputStyle'
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
const combinedProps = computed(() => ({
|
const combinedProps = computed(() => {
|
||||||
...filterWidgetProps(props.widget.options, TREE_SELECT_EXCLUDED_PROPS),
|
const spec = props.widget.spec
|
||||||
...transformCompatProps.value
|
if (!spec || !isTreeSelectInputSpec(spec)) {
|
||||||
}))
|
return {
|
||||||
|
...filterWidgetProps(props.widget.options, TREE_SELECT_EXCLUDED_PROPS),
|
||||||
|
...transformCompatProps.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const specOptions = spec.options || {}
|
||||||
|
return {
|
||||||
|
// Include runtime props like disabled, but filter out panel-related ones
|
||||||
|
...filterWidgetProps(props.widget.options, TREE_SELECT_EXCLUDED_PROPS),
|
||||||
|
// PrimeVue TreeSelect expects 'options' to be an array of tree nodes
|
||||||
|
options: (specOptions.values as TreeNode[]) || [],
|
||||||
|
// Convert 'multiple' to PrimeVue's 'selectionMode'
|
||||||
|
selectionMode: specOptions.multiple
|
||||||
|
? ('multiple' as const)
|
||||||
|
: ('single' as const),
|
||||||
|
// Pass through other props like placeholder
|
||||||
|
placeholder: specOptions.placeholder,
|
||||||
|
...transformCompatProps.value
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import type { BaseDOMWidget } from '@/scripts/domWidget'
|
|||||||
import { addValueControlWidgets } from '@/scripts/widgets'
|
import { addValueControlWidgets } from '@/scripts/widgets'
|
||||||
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
|
import type { ComfyWidgetConstructorV2 } from '@/scripts/widgets'
|
||||||
import { useAssetsStore } from '@/stores/assetsStore'
|
import { useAssetsStore } from '@/stores/assetsStore'
|
||||||
import { getMediaTypeFromFilename } from '@/utils/formatUtil'
|
import { getMediaTypeFromFilename } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
import { useRemoteWidget } from './useRemoteWidget'
|
import { useRemoteWidget } from './useRemoteWidget'
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
import type { SimplifiedWidget, WidgetValue } from '@/types/simplifiedWidget'
|
||||||
|
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a mock SimplifiedWidget for testing Vue Node widgets.
|
* Creates a mock SimplifiedWidget for testing Vue Node widgets.
|
||||||
* This utility function is shared across widget component tests to ensure consistency.
|
* This utility function is shared across widget component tests to ensure consistency.
|
||||||
*/
|
*/
|
||||||
export function createMockWidget<T extends WidgetValue>(
|
export function createMockWidget<T extends WidgetValue = WidgetValue>(
|
||||||
value: T = null as T,
|
value: T = null as T,
|
||||||
options: Record<string, any> = {},
|
options: Record<string, any> = {},
|
||||||
callback?: (value: T) => void,
|
callback?: (value: T) => void,
|
||||||
overrides: Partial<SimplifiedWidget<T>> = {}
|
overrides: Partial<SimplifiedWidget<T>> = {},
|
||||||
|
spec?: Partial<InputSpec>
|
||||||
): SimplifiedWidget<T> {
|
): SimplifiedWidget<T> {
|
||||||
return {
|
const widget: SimplifiedWidget<T> = {
|
||||||
name: 'test_widget',
|
name: 'test_widget',
|
||||||
type: 'default',
|
type: 'default',
|
||||||
value,
|
value,
|
||||||
@@ -18,6 +20,13 @@ export function createMockWidget<T extends WidgetValue>(
|
|||||||
callback,
|
callback,
|
||||||
...overrides
|
...overrides
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only add spec if provided
|
||||||
|
if (spec) {
|
||||||
|
widget.spec = spec as InputSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
return widget
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ const zColorInputSpec = zBaseInputOptions.extend({
|
|||||||
isOptional: z.boolean().optional(),
|
isOptional: z.boolean().optional(),
|
||||||
options: z
|
options: z
|
||||||
.object({
|
.object({
|
||||||
default: z.string().optional()
|
default: z.string().optional(),
|
||||||
|
format: z.enum(['hex', 'rgb', 'hsl', 'hsb']).optional()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
})
|
})
|
||||||
@@ -54,7 +55,13 @@ const zFileUploadInputSpec = zBaseInputOptions.extend({
|
|||||||
type: z.literal('FILEUPLOAD'),
|
type: z.literal('FILEUPLOAD'),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
isOptional: z.boolean().optional(),
|
isOptional: z.boolean().optional(),
|
||||||
options: z.record(z.unknown()).optional()
|
options: z
|
||||||
|
.object({
|
||||||
|
accept: z.string().optional(),
|
||||||
|
extensions: z.array(z.string()).optional(),
|
||||||
|
tooltip: z.string().optional()
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
})
|
})
|
||||||
|
|
||||||
const zImageInputSpec = zBaseInputOptions.extend({
|
const zImageInputSpec = zBaseInputOptions.extend({
|
||||||
@@ -89,7 +96,8 @@ const zTreeSelectInputSpec = zBaseInputOptions.extend({
|
|||||||
options: z
|
options: z
|
||||||
.object({
|
.object({
|
||||||
multiple: z.boolean().optional(),
|
multiple: z.boolean().optional(),
|
||||||
values: z.array(z.unknown()).optional()
|
values: z.array(z.unknown()).optional(),
|
||||||
|
placeholder: z.string().optional()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
})
|
})
|
||||||
@@ -123,7 +131,9 @@ const zGalleriaInputSpec = zBaseInputOptions.extend({
|
|||||||
isOptional: z.boolean().optional(),
|
isOptional: z.boolean().optional(),
|
||||||
options: z
|
options: z
|
||||||
.object({
|
.object({
|
||||||
images: z.array(z.string()).optional()
|
images: z.array(z.string()).optional(),
|
||||||
|
showThumbnails: z.boolean().optional(),
|
||||||
|
showItemNavigators: z.boolean().optional()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
})
|
})
|
||||||
@@ -214,6 +224,7 @@ type StringInputSpec = z.infer<typeof zStringInputSpec>
|
|||||||
export type ComboInputSpec = z.infer<typeof zComboInputSpec>
|
export type ComboInputSpec = z.infer<typeof zComboInputSpec>
|
||||||
export type ColorInputSpec = z.infer<typeof zColorInputSpec>
|
export type ColorInputSpec = z.infer<typeof zColorInputSpec>
|
||||||
export type FileUploadInputSpec = z.infer<typeof zFileUploadInputSpec>
|
export type FileUploadInputSpec = z.infer<typeof zFileUploadInputSpec>
|
||||||
|
type ImageInputSpec = z.infer<typeof zImageInputSpec>
|
||||||
export type ImageCompareInputSpec = z.infer<typeof zImageCompareInputSpec>
|
export type ImageCompareInputSpec = z.infer<typeof zImageCompareInputSpec>
|
||||||
export type TreeSelectInputSpec = z.infer<typeof zTreeSelectInputSpec>
|
export type TreeSelectInputSpec = z.infer<typeof zTreeSelectInputSpec>
|
||||||
export type MultiSelectInputSpec = z.infer<typeof zMultiSelectInputSpec>
|
export type MultiSelectInputSpec = z.infer<typeof zMultiSelectInputSpec>
|
||||||
@@ -262,3 +273,46 @@ export const isChartInputSpec = (
|
|||||||
): inputSpec is ChartInputSpec => {
|
): inputSpec is ChartInputSpec => {
|
||||||
return inputSpec.type === 'CHART'
|
return inputSpec.type === 'CHART'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isTreeSelectInputSpec = (
|
||||||
|
inputSpec: InputSpec
|
||||||
|
): inputSpec is TreeSelectInputSpec => {
|
||||||
|
return inputSpec.type === 'TREESELECT'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isSelectButtonInputSpec = (
|
||||||
|
inputSpec: InputSpec
|
||||||
|
): inputSpec is SelectButtonInputSpec => {
|
||||||
|
return inputSpec.type === 'SELECTBUTTON'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isMultiSelectInputSpec = (
|
||||||
|
inputSpec: InputSpec
|
||||||
|
): inputSpec is MultiSelectInputSpec => {
|
||||||
|
return inputSpec.type === 'MULTISELECT'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isGalleriaInputSpec = (
|
||||||
|
inputSpec: InputSpec
|
||||||
|
): inputSpec is GalleriaInputSpec => {
|
||||||
|
return inputSpec.type === 'GALLERIA'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isColorInputSpec = (
|
||||||
|
inputSpec: InputSpec
|
||||||
|
): inputSpec is ColorInputSpec => {
|
||||||
|
return inputSpec.type === 'COLOR'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isFileUploadInputSpec = (
|
||||||
|
inputSpec: InputSpec
|
||||||
|
): inputSpec is FileUploadInputSpec => {
|
||||||
|
return inputSpec.type === 'FILEUPLOAD'
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error - will be used in future IMAGE widget implementation
|
||||||
|
const isImageInputSpec = (
|
||||||
|
inputSpec: InputSpec
|
||||||
|
): inputSpec is ImageInputSpec => {
|
||||||
|
return inputSpec.type === 'IMAGE'
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import type {
|
|||||||
} from '@/lib/litegraph/src/types/widgets'
|
} from '@/lib/litegraph/src/types/widgets'
|
||||||
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
import type { InputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||||
import { useDomWidgetStore } from '@/stores/domWidgetStore'
|
import { useDomWidgetStore } from '@/stores/domWidgetStore'
|
||||||
import { generateUUID } from '@/utils/formatUtil'
|
import { generateUUID } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
export interface BaseDOMWidget<V extends object | string = object | string>
|
export interface BaseDOMWidget<V extends object | string = object | string>
|
||||||
extends IBaseWidget<V, string, DOMWidgetOptions<V>> {
|
extends IBaseWidget<V, string, DOMWidgetOptions<V>> {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import { useNodeOutputStore } from '@/stores/imagePreviewStore'
|
|||||||
import { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
import { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
|
||||||
import { useSubgraphStore } from '@/stores/subgraphStore'
|
import { useSubgraphStore } from '@/stores/subgraphStore'
|
||||||
import { useWidgetStore } from '@/stores/widgetStore'
|
import { useWidgetStore } from '@/stores/widgetStore'
|
||||||
import { normalizeI18nKey } from '@/utils/formatUtil'
|
import { normalizeI18nKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import {
|
import {
|
||||||
isImageNode,
|
isImageNode,
|
||||||
isVideoNode,
|
isVideoNode,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import type {
|
|||||||
SearchPacksResult,
|
SearchPacksResult,
|
||||||
SortableField
|
SortableField
|
||||||
} from '@/types/searchServiceTypes'
|
} from '@/types/searchServiceTypes'
|
||||||
import { paramsToCacheKey } from '@/utils/formatUtil'
|
import { paramsToCacheKey } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { SortableAlgoliaField } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
import { SortableAlgoliaField } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||||
|
|
||||||
type RegistryNodePack = components['schemas']['Node']
|
type RegistryNodePack = components['schemas']['Node']
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { api } from '@/scripts/api'
|
|||||||
import { app } from '@/scripts/app'
|
import { app } from '@/scripts/app'
|
||||||
import { useExecutionStore } from '@/stores/executionStore'
|
import { useExecutionStore } from '@/stores/executionStore'
|
||||||
import type { NodeLocatorId } from '@/types/nodeIdentification'
|
import type { NodeLocatorId } from '@/types/nodeIdentification'
|
||||||
import { parseFilePath } from '@/utils/formatUtil'
|
import { parseFilePath } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { isVideoNode } from '@/utils/litegraphUtil'
|
import { isVideoNode } from '@/utils/litegraphUtil'
|
||||||
|
|
||||||
const PREVIEW_REVOKE_DELAY_MS = 400
|
const PREVIEW_REVOKE_DELAY_MS = 400
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { api } from '@/scripts/api'
|
|||||||
import type { ComfyApp } from '@/scripts/app'
|
import type { ComfyApp } from '@/scripts/app'
|
||||||
import { useExtensionService } from '@/services/extensionService'
|
import { useExtensionService } from '@/services/extensionService'
|
||||||
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
|
import { useNodeOutputStore } from '@/stores/imagePreviewStore'
|
||||||
import { getMediaTypeFromFilename } from '@/utils/formatUtil'
|
import { getMediaTypeFromFilename } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
// Task type used in the API.
|
// Task type used in the API.
|
||||||
type APITaskType = 'queue' | 'history'
|
type APITaskType = 'queue' | 'history'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { computed, ref } from 'vue'
|
|||||||
import type { UserDataFullInfo } from '@/schemas/apiSchema'
|
import type { UserDataFullInfo } from '@/schemas/apiSchema'
|
||||||
import { api } from '@/scripts/api'
|
import { api } from '@/scripts/api'
|
||||||
import type { TreeExplorerNode } from '@/types/treeExplorerTypes'
|
import type { TreeExplorerNode } from '@/types/treeExplorerTypes'
|
||||||
import { getPathDetails } from '@/utils/formatUtil'
|
import { getPathDetails } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { syncEntities } from '@/utils/syncUtil'
|
import { syncEntities } from '@/utils/syncUtil'
|
||||||
import { buildTree } from '@/utils/treeUtil'
|
import { buildTree } from '@/utils/treeUtil'
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { electronAPI } from '@/utils/envUtil'
|
import { electronAPI } from '@/utils/envUtil'
|
||||||
import { isValidUrl } from '@/utils/formatUtil'
|
import { isValidUrl } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a mirror is reachable from the electron App.
|
* Check if a mirror is reachable from the electron App.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { LGraph, Subgraph } from '@/lib/litegraph/src/litegraph'
|
import type { LGraph, Subgraph } from '@/lib/litegraph/src/litegraph'
|
||||||
import { formatDate } from '@/utils/formatUtil'
|
import { formatDate } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { collectAllNodes } from '@/utils/graphTraversalUtil'
|
import { collectAllNodes } from '@/utils/graphTraversalUtil'
|
||||||
|
|
||||||
export function applyTextReplacements(
|
export function applyTextReplacements(
|
||||||
|
|||||||
@@ -23,11 +23,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { isValidUrl } from '@comfyorg/shared-frontend-utils/formatUtil'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import type { components } from '@/types/comfyRegistryTypes'
|
import type { components } from '@/types/comfyRegistryTypes'
|
||||||
import { isValidUrl } from '@/utils/formatUtil'
|
|
||||||
import InfoTextSection from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue'
|
import InfoTextSection from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue'
|
||||||
import type { TextSection } from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue'
|
import type { TextSection } from '@/workbench/extensions/manager/components/manager/infoPanel/InfoTextSection.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ This directory contains development tools and test utilities for ComfyUI, previo
|
|||||||
|
|
||||||
- `__init__.py` - Server endpoints for development tools (`/api/devtools/*`)
|
- `__init__.py` - Server endpoints for development tools (`/api/devtools/*`)
|
||||||
- `dev_nodes.py` - Development and testing nodes for ComfyUI
|
- `dev_nodes.py` - Development and testing nodes for ComfyUI
|
||||||
|
- `nodes/vue_widgets.py` - Widget showcase nodes used to exercise new Vue-based widgets
|
||||||
- `fake_model.safetensors` - Test fixture for model loading tests
|
- `fake_model.safetensors` - Test fixture for model loading tests
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
These tools provide:
|
These tools provide:
|
||||||
|
|
||||||
- Test endpoints for browser automation
|
- Test endpoints for browser automation
|
||||||
- Development nodes for testing various UI features
|
- Development nodes for testing various UI features
|
||||||
- Mock data for consistent testing environments
|
- Mock data for consistent testing environments
|
||||||
@@ -25,4 +27,4 @@ cp -r tools/devtools/* /path/to/your/ComfyUI/custom_nodes/ComfyUI_devtools/
|
|||||||
|
|
||||||
## Migration
|
## Migration
|
||||||
|
|
||||||
This directory was created as part of issue #4683 to merge the ComfyUI_devtools repository into the main frontend repository, eliminating the need for separate versioning and simplifying the development workflow.
|
This directory was created as part of issue #4683 to merge the ComfyUI_devtools repository into the main frontend repository, eliminating the need for separate versioning and simplifying the development workflow.
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from .nodes import (
|
from .nodes import (
|
||||||
|
VueAudioPreviewComboNode,
|
||||||
|
VueAudioRecordWidgetNode,
|
||||||
|
VueChartWidgetNode,
|
||||||
DeprecatedNode,
|
DeprecatedNode,
|
||||||
DummyPatch,
|
DummyPatch,
|
||||||
ErrorRaiseNode,
|
ErrorRaiseNode,
|
||||||
ErrorRaiseNodeWithMessage,
|
ErrorRaiseNodeWithMessage,
|
||||||
ExperimentalNode,
|
ExperimentalNode,
|
||||||
|
VueFileUploadWidgetNode,
|
||||||
LoadAnimatedImageTest,
|
LoadAnimatedImageTest,
|
||||||
LongComboDropdown,
|
LongComboDropdown,
|
||||||
|
VueMarkdownWidgetNode,
|
||||||
|
VueGalleriaWidgetNode,
|
||||||
|
VueImageCompareWidgetNode,
|
||||||
MultiSelectNode,
|
MultiSelectNode,
|
||||||
NodeWithBooleanInput,
|
NodeWithBooleanInput,
|
||||||
NodeWithDefaultInput,
|
NodeWithDefaultInput,
|
||||||
@@ -23,24 +30,36 @@ from .nodes import (
|
|||||||
NodeWithValidation,
|
NodeWithValidation,
|
||||||
NodeWithV2ComboInput,
|
NodeWithV2ComboInput,
|
||||||
ObjectPatchNode,
|
ObjectPatchNode,
|
||||||
|
VueSelectButtonWidgetNode,
|
||||||
|
VueTextareaWidgetNode,
|
||||||
|
VueTreeSelectMultiWidgetNode,
|
||||||
|
VueTreeSelectWidgetNode,
|
||||||
RemoteWidgetNode,
|
RemoteWidgetNode,
|
||||||
RemoteWidgetNodeWithControlAfterRefresh,
|
RemoteWidgetNodeWithControlAfterRefresh,
|
||||||
RemoteWidgetNodeWithParams,
|
RemoteWidgetNodeWithParams,
|
||||||
RemoteWidgetNodeWithRefresh,
|
RemoteWidgetNodeWithRefresh,
|
||||||
RemoteWidgetNodeWithRefreshButton,
|
RemoteWidgetNodeWithRefreshButton,
|
||||||
SimpleSlider,
|
SimpleSlider,
|
||||||
|
VueColorWidgetNode,
|
||||||
NODE_CLASS_MAPPINGS,
|
NODE_CLASS_MAPPINGS,
|
||||||
NODE_DISPLAY_NAME_MAPPINGS,
|
NODE_DISPLAY_NAME_MAPPINGS,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
"VueAudioPreviewComboNode",
|
||||||
|
"VueAudioRecordWidgetNode",
|
||||||
|
"VueChartWidgetNode",
|
||||||
"DeprecatedNode",
|
"DeprecatedNode",
|
||||||
"DummyPatch",
|
"DummyPatch",
|
||||||
"ErrorRaiseNode",
|
"ErrorRaiseNode",
|
||||||
"ErrorRaiseNodeWithMessage",
|
"ErrorRaiseNodeWithMessage",
|
||||||
"ExperimentalNode",
|
"ExperimentalNode",
|
||||||
|
"VueFileUploadWidgetNode",
|
||||||
"LoadAnimatedImageTest",
|
"LoadAnimatedImageTest",
|
||||||
"LongComboDropdown",
|
"LongComboDropdown",
|
||||||
|
"VueMarkdownWidgetNode",
|
||||||
|
"VueGalleriaWidgetNode",
|
||||||
|
"VueImageCompareWidgetNode",
|
||||||
"MultiSelectNode",
|
"MultiSelectNode",
|
||||||
"NodeWithBooleanInput",
|
"NodeWithBooleanInput",
|
||||||
"NodeWithDefaultInput",
|
"NodeWithDefaultInput",
|
||||||
@@ -56,12 +75,17 @@ __all__ = [
|
|||||||
"NodeWithValidation",
|
"NodeWithValidation",
|
||||||
"NodeWithV2ComboInput",
|
"NodeWithV2ComboInput",
|
||||||
"ObjectPatchNode",
|
"ObjectPatchNode",
|
||||||
|
"VueSelectButtonWidgetNode",
|
||||||
|
"VueTextareaWidgetNode",
|
||||||
|
"VueTreeSelectMultiWidgetNode",
|
||||||
|
"VueTreeSelectWidgetNode",
|
||||||
"RemoteWidgetNode",
|
"RemoteWidgetNode",
|
||||||
"RemoteWidgetNodeWithControlAfterRefresh",
|
"RemoteWidgetNodeWithControlAfterRefresh",
|
||||||
"RemoteWidgetNodeWithParams",
|
"RemoteWidgetNodeWithParams",
|
||||||
"RemoteWidgetNodeWithRefresh",
|
"RemoteWidgetNodeWithRefresh",
|
||||||
"RemoteWidgetNodeWithRefreshButton",
|
"RemoteWidgetNodeWithRefreshButton",
|
||||||
"SimpleSlider",
|
"SimpleSlider",
|
||||||
|
"VueColorWidgetNode",
|
||||||
"NODE_CLASS_MAPPINGS",
|
"NODE_CLASS_MAPPINGS",
|
||||||
"NODE_DISPLAY_NAME_MAPPINGS",
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -44,12 +44,29 @@ from .remote import (
|
|||||||
NODE_CLASS_MAPPINGS as remote_class_mappings,
|
NODE_CLASS_MAPPINGS as remote_class_mappings,
|
||||||
NODE_DISPLAY_NAME_MAPPINGS as remote_display_name_mappings,
|
NODE_DISPLAY_NAME_MAPPINGS as remote_display_name_mappings,
|
||||||
)
|
)
|
||||||
|
from .vue_widgets import (
|
||||||
|
VueAudioPreviewComboNode,
|
||||||
|
VueAudioRecordWidgetNode,
|
||||||
|
VueChartWidgetNode,
|
||||||
|
VueColorWidgetNode,
|
||||||
|
VueFileUploadWidgetNode,
|
||||||
|
VueGalleriaWidgetNode,
|
||||||
|
VueImageCompareWidgetNode,
|
||||||
|
VueMarkdownWidgetNode,
|
||||||
|
VueSelectButtonWidgetNode,
|
||||||
|
VueTextareaWidgetNode,
|
||||||
|
VueTreeSelectMultiWidgetNode,
|
||||||
|
VueTreeSelectWidgetNode,
|
||||||
|
NODE_CLASS_MAPPINGS as vue_widgets_class_mappings,
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS as vue_widgets_display_name_mappings,
|
||||||
|
)
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
**errors_class_mappings,
|
**errors_class_mappings,
|
||||||
**inputs_class_mappings,
|
**inputs_class_mappings,
|
||||||
**remote_class_mappings,
|
**remote_class_mappings,
|
||||||
**models_class_mappings,
|
**models_class_mappings,
|
||||||
|
**vue_widgets_class_mappings,
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
@@ -57,6 +74,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
**inputs_display_name_mappings,
|
**inputs_display_name_mappings,
|
||||||
**remote_display_name_mappings,
|
**remote_display_name_mappings,
|
||||||
**models_display_name_mappings,
|
**models_display_name_mappings,
|
||||||
|
**vue_widgets_display_name_mappings,
|
||||||
}
|
}
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -88,6 +106,18 @@ __all__ = [
|
|||||||
"RemoteWidgetNodeWithRefresh",
|
"RemoteWidgetNodeWithRefresh",
|
||||||
"RemoteWidgetNodeWithRefreshButton",
|
"RemoteWidgetNodeWithRefreshButton",
|
||||||
"SimpleSlider",
|
"SimpleSlider",
|
||||||
|
"VueAudioPreviewComboNode",
|
||||||
|
"VueAudioRecordWidgetNode",
|
||||||
|
"VueChartWidgetNode",
|
||||||
|
"VueColorWidgetNode",
|
||||||
|
"VueFileUploadWidgetNode",
|
||||||
|
"VueGalleriaWidgetNode",
|
||||||
|
"VueImageCompareWidgetNode",
|
||||||
|
"VueMarkdownWidgetNode",
|
||||||
|
"VueSelectButtonWidgetNode",
|
||||||
|
"VueTextareaWidgetNode",
|
||||||
|
"VueTreeSelectMultiWidgetNode",
|
||||||
|
"VueTreeSelectWidgetNode",
|
||||||
"NODE_CLASS_MAPPINGS",
|
"NODE_CLASS_MAPPINGS",
|
||||||
"NODE_DISPLAY_NAME_MAPPINGS",
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
]
|
]
|
||||||
|
|||||||
477
tools/devtools/nodes/vue_widgets.py
Normal file
477
tools/devtools/nodes/vue_widgets.py
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
SAMPLE_IMAGE_DATA_URI = (
|
||||||
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKklEQVR4nO3NsQEAAATAMLzr/5kn2NIDmpzu+Kxe7wAAAAAAAAAAAOCwBcUDAhU8Tp3xAAAAAElFTkSuQmCC"
|
||||||
|
)
|
||||||
|
SAMPLE_IMAGE_DATA_URI_ALT = (
|
||||||
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALUlEQVR4nGM0nvWBgZaAiaamj1owasGoBaMWjFowasGoBaMWjFowasGoBVQEAKDTAf3D6Eg+AAAAAElFTkSuQmCC"
|
||||||
|
)
|
||||||
|
SAMPLE_IMAGE_DATA_URI_THIRD = (
|
||||||
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALUlEQVR4nGMMPJ/GQEvARFPTRy0YtWDUglELRi0YtWDUglELRi0YtWDUAioCAAbrAcZ7cQHKAAAAAElFTkSuQmCC"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class VueFileUploadWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"uploaded_file": (
|
||||||
|
"FILEUPLOAD",
|
||||||
|
{
|
||||||
|
"default": [],
|
||||||
|
"options": {
|
||||||
|
"extensions": ["png", "jpg", "jpeg", "webp"],
|
||||||
|
"accept": "image/png,image/jpeg,image/webp",
|
||||||
|
"tooltip": "Upload an image file",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "return_file"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the FILEUPLOAD widget"
|
||||||
|
|
||||||
|
def return_file(self, uploaded_file: str | None):
|
||||||
|
return (uploaded_file or "",)
|
||||||
|
|
||||||
|
|
||||||
|
class VueImageCompareWidgetNode:
|
||||||
|
BEFORE_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAB4CAIAAAA48Cq8AAAC8ElEQVR4nO3Wv0uqURzH8Y/ykNXgFBQ1NERBNIS7jTUF4VBQ7RE4SH9Hg0jQ3KA1WVO74RYWREskREE0NRUNYvrc4XZBL3V/wP3kNd+v7eGcL5zhzTlPJEynBfxr0U4fAF8TYcGCsGBBWLAgLFgQFiwICxaEBQvCggVhwYKwYEFYsCAsWBAWLAgLFoQFC8KCBWHBgrBgQViwICxYEBYsCAsWhAULwoIFYcGCsGBBWLAgLFgQFiwICxaEBQvCggVhwYKwYEFYsCAsWBAWLAgLFoQFC8KCBWHBgrBgQViwICxYEBYsuhHW6KgWFzU01IWj8b8EEf9ePq/7e8ViGhzU6amurz/Zs7WlalVPNbMdHsJdEHdbzu07aaY0Pb252Elk6pUIj4XPSbqsH55elKzqeFhra4qmVQQ6PhYDw9aWFAioVxOpZLW1pRIKAxVLOrlRbu7urrS46POz3+fQr+xfWPNzKhcViajszPt7engQBsbklStKgxVKGh5WTVa8nnVaspiLCtCh8e6m0N7NZpclMUyo3LUqk0U0Ppra+vwOaPNZCn1ddn8dEtYvVSpOYqSRMT89g4MdKRpJKpRMoqXTdfqfBWm08RF4z/y9Ktfm+fXpLLjEgcFPTd1WsHyJRKxBqVTpIqH9f1f3xBd5JJkEplI+LtMv4ucVaFZ7o2j/AuAxn4OwIEFYkCAsSBAWJAgLEoQFCcKCBGFBgrAgQViQICxIEBYkCAsShAUJwoIEYUGCsCBBWJAgLEgQFiQICxKEBQnCggRhQYKwIEFYkCAsSBAWJAgLEoQFCcKCBGFBgrAgQViQICxIEBYkCAsShAUJwoIEYUGCsCBBWJAgLEgQFiw+AcwZf7lBBNZAAA"
|
||||||
|
AFTER_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAB4CAIAAAA48Cq8AAADDklEQVR4nO3Z32uNcRzA8e/3Oc85Oz/WLFqNzjZuxKgtWpmwluxiUYoLudlKubH/QHJBuZE7LuYfoCQXolxJasmF1EjJhRrhQpQ1O85zjouzEm69d7Ler6vvU8+Pz8W7b9964u7ZL0H615J2D6C1ybCEMCwhDEsIwxLCsIQwLCEMSwjDEsKwhDAsIQxLCMMSwrCEMCwhDEsIwxLCsIQwLCEMSwjDEsKwhDAsIQxLCMMSwrCEMCwhDEsIwxLCsIQwLCEMSwjDEsKwhDAsIQxLCMMSwrCEMCwhDEsIwxLCsIQwLCEMSwjDEsKwhDAsIQxLCMMSwrCEMCwhDEsIwxLCsIRY1bA2dydTQx2dhbiaH1VbsGEd3VaYO7VufWmlpMuHKstZM2uE6eEO9LtqOzasA/3pjfnlfX351uWGcrwxX1uqN6eHDGuNA8MqprGYxjuvavsG0hDC8cFCJR9nD1dmRorlfLw6WenqiBfGy9cmK9ePVHb05FpPPZzqOj9WOrGzwA2mVZByrx6tpnML9bdfG5s6k3wSbr2szYwUT99dDCEcHyycubd47kDp5ovl+U9Zb2dyZaJ88va3EEIhFx+8+TG3UOcG0yoAwxobSLduyB3cku8pJ7s2pk/e/dnKaDXt61rZMkv5mMTQaIas2fz7Tv13qLCSGPrX5Vqb0Gg13d+f/zuXXIwz9xdrWUhiGE5NG80QQsgaobXQf406Yw33pq8/Z631sw/ZnupvBScxJDE8/1gf35wPIeztSz3OrzHUjjU2kD59v7JFfa83Py81tnT/ivjZh+zKROXS46Wz+0vHtheyZrj4aAmaRG0Rd89+afcMWoP8pSOEYQlhWEIYlhCGJYRhCWFYQhiWEIYlhGEJYVhCGJYQhiWEYQlhWEIYlhCGJYRhCWFYQhiWEIYlhGEJYVhCGJYQhiWEYQlhWEIYlhCGJYRhCWFYQhiWEIYlhGEJYVhCGJYQhiWEYQlhWEIYlhCGJYRhCWFYQhiWEIYlhGEJYVhCGJYQhiWEYQlhWEIYlhCGJYRhCWFYQvwE4Ex5XANtu7QAAAAASUVORK5CYII="
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"comparison": (
|
||||||
|
"IMAGECOMPARE",
|
||||||
|
{
|
||||||
|
"default": {
|
||||||
|
"before": BEFORE_IMAGE,
|
||||||
|
"after": AFTER_IMAGE,
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"beforeAlt": "Before",
|
||||||
|
"afterAlt": "After",
|
||||||
|
"initialPosition": 40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "noop"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the IMAGECOMPARE widget"
|
||||||
|
|
||||||
|
def noop(self, comparison):
|
||||||
|
return tuple()
|
||||||
|
|
||||||
|
|
||||||
|
class VueTreeSelectWidgetNode:
|
||||||
|
TREE_DATA = [
|
||||||
|
{
|
||||||
|
"key": "root",
|
||||||
|
"label": "Root",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"key": "section-a",
|
||||||
|
"label": "Section A",
|
||||||
|
"children": [
|
||||||
|
{"key": "item-a1", "label": "Item A1"},
|
||||||
|
{"key": "item-a2", "label": "Item A2"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "section-b",
|
||||||
|
"label": "Section B",
|
||||||
|
"children": [
|
||||||
|
{"key": "item-b1", "label": "Item B1"},
|
||||||
|
{"key": "item-b2", "label": "Item B2"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"selection": (
|
||||||
|
"TREESELECT",
|
||||||
|
{
|
||||||
|
"default": "item-a1",
|
||||||
|
"options": {
|
||||||
|
"values": cls.TREE_DATA,
|
||||||
|
"multiple": False,
|
||||||
|
"placeholder": "Select an item",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "return_selection"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the TREESELECT widget"
|
||||||
|
|
||||||
|
def return_selection(self, selection: str):
|
||||||
|
return (selection,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueTreeSelectMultiWidgetNode(VueTreeSelectWidgetNode):
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"selection": (
|
||||||
|
"TREESELECT",
|
||||||
|
{
|
||||||
|
"default": ["item-a1", "item-b1"],
|
||||||
|
"options": {
|
||||||
|
"values": cls.TREE_DATA,
|
||||||
|
"multiple": True,
|
||||||
|
"placeholder": "Select items",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
OUTPUT_IS_LIST = (True,)
|
||||||
|
FUNCTION = "return_selection"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the TREESELECT widget in multi-select mode"
|
||||||
|
|
||||||
|
def return_selection(self, selection: list[str]):
|
||||||
|
return (selection,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueSelectButtonWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
options = [
|
||||||
|
{"label": "Low", "value": "low"},
|
||||||
|
{"label": "Medium", "value": "medium"},
|
||||||
|
{"label": "High", "value": "high"},
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"mode": (
|
||||||
|
"SELECTBUTTON",
|
||||||
|
{
|
||||||
|
"default": "Medium",
|
||||||
|
"options": {
|
||||||
|
"values": ["Low", "Medium", "High"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "return_mode"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the SELECTBUTTON widget"
|
||||||
|
|
||||||
|
def return_mode(self, mode: str):
|
||||||
|
return (mode,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueTextareaWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"notes": (
|
||||||
|
"TEXTAREA",
|
||||||
|
{
|
||||||
|
"default": "This is a DevTools textarea widget.\nFeel free to edit me!",
|
||||||
|
"options": {
|
||||||
|
"rows": 4,
|
||||||
|
"cols": 40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "return_notes"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the TEXTAREA widget"
|
||||||
|
|
||||||
|
def return_notes(self, notes: str):
|
||||||
|
return (notes,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueChartWidgetNode:
|
||||||
|
CHART_DATA = {
|
||||||
|
"labels": ["Iteration 1", "Iteration 2", "Iteration 3"],
|
||||||
|
"datasets": [
|
||||||
|
{
|
||||||
|
"label": "Loss",
|
||||||
|
"data": [0.95, 0.62, 0.31],
|
||||||
|
"borderColor": "#339AF0",
|
||||||
|
"backgroundColor": "rgba(51, 154, 240, 0.2)",
|
||||||
|
"fill": True,
|
||||||
|
"tension": 0.35,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"chart": (
|
||||||
|
"CHART",
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"type": "line",
|
||||||
|
"data": cls.CHART_DATA,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("DICT",)
|
||||||
|
FUNCTION = "return_chart"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the CHART widget"
|
||||||
|
|
||||||
|
def return_chart(self, chart):
|
||||||
|
return (chart,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueGalleriaWidgetNode:
|
||||||
|
GALLERIA_IMAGES = [
|
||||||
|
{
|
||||||
|
"itemImageSrc": SAMPLE_IMAGE_DATA_URI,
|
||||||
|
"thumbnailImageSrc": SAMPLE_IMAGE_DATA_URI,
|
||||||
|
"alt": "Warm gradient",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemImageSrc": SAMPLE_IMAGE_DATA_URI_ALT,
|
||||||
|
"thumbnailImageSrc": SAMPLE_IMAGE_DATA_URI_ALT,
|
||||||
|
"alt": "Cool gradient",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"itemImageSrc": SAMPLE_IMAGE_DATA_URI_THIRD,
|
||||||
|
"thumbnailImageSrc": SAMPLE_IMAGE_DATA_URI_THIRD,
|
||||||
|
"alt": "Fresh gradient",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"gallery": (
|
||||||
|
"GALLERIA",
|
||||||
|
{
|
||||||
|
"default": cls.GALLERIA_IMAGES,
|
||||||
|
"options": {
|
||||||
|
"images": cls.GALLERIA_IMAGES,
|
||||||
|
"showThumbnails": True,
|
||||||
|
"showItemNavigators": True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "noop"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the GALLERIA widget"
|
||||||
|
|
||||||
|
def noop(self, gallery):
|
||||||
|
return tuple()
|
||||||
|
|
||||||
|
|
||||||
|
class VueMarkdownWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"markdown": (
|
||||||
|
"MARKDOWN",
|
||||||
|
{
|
||||||
|
"default": "# DevTools Markdown\nThis widget renders **Markdown** content.",
|
||||||
|
"options": {
|
||||||
|
"content": "# DevTools Markdown\nThis widget renders **Markdown** content.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "noop"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the MARKDOWN widget"
|
||||||
|
|
||||||
|
def noop(self, markdown):
|
||||||
|
return tuple()
|
||||||
|
|
||||||
|
|
||||||
|
class VueAudioRecordWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"recording": (
|
||||||
|
"AUDIORECORD",
|
||||||
|
{
|
||||||
|
"default": "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "return_recording"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the AUDIORECORD widget"
|
||||||
|
|
||||||
|
def return_recording(self, recording: str):
|
||||||
|
return (recording,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueMultiSelectWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"selection": (
|
||||||
|
"MULTISELECT",
|
||||||
|
{
|
||||||
|
"default": ["option1", "option3"],
|
||||||
|
"options": {
|
||||||
|
"values": ["option1", "option2", "option3", "option4", "option5"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
OUTPUT_IS_LIST = (True,)
|
||||||
|
FUNCTION = "return_selection"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the MULTISELECT widget"
|
||||||
|
|
||||||
|
def return_selection(self, selection: list[str]):
|
||||||
|
return (selection,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueColorWidgetNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"color": (
|
||||||
|
"COLOR",
|
||||||
|
{
|
||||||
|
"default": "#ff6b6b",
|
||||||
|
"options": {
|
||||||
|
"tooltip": "Pick a color",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "return_color"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the COLOR widget"
|
||||||
|
|
||||||
|
def return_color(self, color: str):
|
||||||
|
return (color,)
|
||||||
|
|
||||||
|
|
||||||
|
class VueAudioPreviewComboNode:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"audio": (
|
||||||
|
"COMBO",
|
||||||
|
{
|
||||||
|
"options": ["ambient.wav", "dialog.wav"],
|
||||||
|
"default": "ambient.wav",
|
||||||
|
"tooltip": "Pick an audio clip",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
FUNCTION = "return_audio"
|
||||||
|
CATEGORY = "DevTools/Vue Widgets"
|
||||||
|
DESCRIPTION = "Showcases the COMBO widget rendered as Audio UI"
|
||||||
|
|
||||||
|
def return_audio(self, audio: str):
|
||||||
|
return (audio,)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"DevToolsVueFileUploadWidgetNode": VueFileUploadWidgetNode,
|
||||||
|
"DevToolsVueImageCompareWidgetNode": VueImageCompareWidgetNode,
|
||||||
|
"DevToolsVueTreeSelectWidgetNode": VueTreeSelectWidgetNode,
|
||||||
|
"DevToolsVueTreeSelectMultiWidgetNode": VueTreeSelectMultiWidgetNode,
|
||||||
|
"DevToolsVueSelectButtonWidgetNode": VueSelectButtonWidgetNode,
|
||||||
|
"DevToolsVueMultiSelectWidgetNode": VueMultiSelectWidgetNode,
|
||||||
|
"DevToolsVueTextareaWidgetNode": VueTextareaWidgetNode,
|
||||||
|
"DevToolsVueChartWidgetNode": VueChartWidgetNode,
|
||||||
|
"DevToolsVueGalleriaWidgetNode": VueGalleriaWidgetNode,
|
||||||
|
"DevToolsVueMarkdownWidgetNode": VueMarkdownWidgetNode,
|
||||||
|
"DevToolsVueAudioRecordWidgetNode": VueAudioRecordWidgetNode,
|
||||||
|
"DevToolsVueColorWidgetNode": VueColorWidgetNode,
|
||||||
|
"DevToolsVueAudioPreviewComboNode": VueAudioPreviewComboNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"DevToolsVueFileUploadWidgetNode": "Vue File Upload Widget",
|
||||||
|
"DevToolsVueImageCompareWidgetNode": "Vue Image Compare Widget",
|
||||||
|
"DevToolsVueTreeSelectWidgetNode": "Vue Tree Select Widget",
|
||||||
|
"DevToolsVueTreeSelectMultiWidgetNode": "Vue Tree Select (Multi) Widget",
|
||||||
|
"DevToolsVueSelectButtonWidgetNode": "Vue Select Button Widget",
|
||||||
|
"DevToolsVueMultiSelectWidgetNode": "Vue Multi Select Widget",
|
||||||
|
"DevToolsVueTextareaWidgetNode": "Vue Textarea Widget",
|
||||||
|
"DevToolsVueChartWidgetNode": "Vue Chart Widget",
|
||||||
|
"DevToolsVueGalleriaWidgetNode": "Vue Galleria Widget",
|
||||||
|
"DevToolsVueMarkdownWidgetNode": "Vue Markdown Widget",
|
||||||
|
"DevToolsVueAudioRecordWidgetNode": "Vue Audio Record Widget",
|
||||||
|
"DevToolsVueColorWidgetNode": "Vue Color Widget",
|
||||||
|
"DevToolsVueAudioPreviewComboNode": "Vue Audio Combo Widget",
|
||||||
|
}
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VueFileUploadWidgetNode",
|
||||||
|
"VueImageCompareWidgetNode",
|
||||||
|
"VueTreeSelectWidgetNode",
|
||||||
|
"VueTreeSelectMultiWidgetNode",
|
||||||
|
"VueSelectButtonWidgetNode",
|
||||||
|
"VueMultiSelectWidgetNode",
|
||||||
|
"VueTextareaWidgetNode",
|
||||||
|
"VueChartWidgetNode",
|
||||||
|
"VueGalleriaWidgetNode",
|
||||||
|
"VueMarkdownWidgetNode",
|
||||||
|
"VueAudioRecordWidgetNode",
|
||||||
|
"VueColorWidgetNode",
|
||||||
|
"VueAudioPreviewComboNode",
|
||||||
|
"NODE_CLASS_MAPPINGS",
|
||||||
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user