feat(V3 UI style): color picker + file upload + input text + multi select + select + select button + slider + textarea + tree select

This commit is contained in:
Rizumu Ayaka
2025-09-02 22:47:07 +08:00
parent 242a4a4454
commit 60ce863b60
11 changed files with 75 additions and 35 deletions

View File

@@ -1,17 +1,24 @@
<!-- Needs custom color picker for alpha support -->
<template>
<div class="flex flex-col gap-1">
<label v-if="widget.name" class="text-sm opacity-80">{{
widget.name
}}</label>
<ColorPicker
v-model="localValue"
v-bind="filteredProps"
:disabled="readonly"
inline
@update:model-value="onChange"
/>
</div>
<WidgetLayoutField :widget="widget">
<label
:class="
cn(WidgetInputBaseClass, 'flex items-center gap-2 w-full px-4 py-2')
"
>
<ColorPicker
v-model="localValue"
v-bind="filteredProps"
:disabled="readonly"
class="w-8 h-4 !rounded-full overflow-hidden border-none"
:pt="{
preview: '!w-full !h-full !border-none'
}"
@update:model-value="onChange"
/>
<span class="text-xs">#{{ localValue }}</span>
</label>
</WidgetLayoutField>
</template>
<script setup lang="ts">
@@ -20,11 +27,15 @@ import { computed } from 'vue'
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
import {
PANEL_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
const props = defineProps<{
widget: SimplifiedWidget<string>
modelValue: string

View File

@@ -157,6 +157,7 @@
<Button
label="Browse Files"
size="small"
severity="secondary"
class="text-xs"
:disabled="readonly"
@click="triggerFileInput"

View File

@@ -4,7 +4,7 @@
v-model="localValue"
v-bind="filteredProps"
:disabled="readonly"
class="w-full text-xs py-2 px-4 bg-zinc-500/10 border-none outline outline-1 outline-offset-[-1px] outline-zinc-300/10 !rounded-lg hover:outline-blue-500/80"
:class="cn(WidgetInputBaseClass, 'w-full text-xs py-2 px-4')"
size="small"
@update:model-value="onChange"
/>
@@ -17,11 +17,13 @@ import { computed } from 'vue'
import { useStringWidgetValue } from '@/composables/graph/useWidgetValue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
import {
INPUT_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
const props = defineProps<{

View File

@@ -2,10 +2,11 @@
<WidgetLayoutField :widget="widget">
<MultiSelect
v-model="localValue"
v-bind="filteredProps"
:options="widget.options?.values || []"
:disabled="readonly"
class="w-full text-xs"
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
size="small"
display="chip"
:pt="{
option: 'text-xs'
}"
@@ -16,15 +17,12 @@
<script setup lang="ts">
import MultiSelect from 'primevue/multiselect'
import { computed } from 'vue'
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import {
PANEL_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
import { cn } from '@/utils/tailwindUtil'
import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
const props = defineProps<{
@@ -44,14 +42,4 @@ const { localValue, onChange } = useWidgetValue({
defaultValue: [],
emit
})
// MultiSelect specific excluded props include overlay styles
const MULTISELECT_EXCLUDED_PROPS = [
...PANEL_EXCLUDED_PROPS,
'overlayStyle'
] as const
const filteredProps = computed(() =>
filterWidgetProps(props.widget.options, MULTISELECT_EXCLUDED_PROPS)
)
</script>

View File

@@ -5,7 +5,7 @@
:options="selectOptions"
v-bind="filteredProps"
:disabled="readonly"
class="w-full text-xs bg-[#F9F8F4] dark-theme:bg-[#0E0E12] border-[#E1DED5] dark-theme:border-[#15161C] !rounded-lg"
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
size="small"
:pt="{
option: 'text-xs'
@@ -21,11 +21,13 @@ import { computed } from 'vue'
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
import {
PANEL_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
const props = defineProps<{

View File

@@ -1,7 +1,9 @@
<template>
<WidgetLayoutField :widget="widget">
<div
class="flex items-center gap-2 w-full rounded-lg pl-4 pr-2 bg-[#F9F8F4] dark-theme:bg-[#0E0E12] border-[#E1DED5] dark-theme:border-[#15161C] border-solid border"
:class="
cn(WidgetInputBaseClass, 'flex items-center gap-2 w-full pl-4 pr-2')
"
>
<Slider
v-model="localValue"
@@ -33,11 +35,13 @@ import { computed, ref, watch } from 'vue'
import { useNumberWidgetValue } from '@/composables/graph/useWidgetValue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
import {
STANDARD_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
const props = defineProps<{

View File

@@ -3,7 +3,8 @@
v-model="localValue"
v-bind="filteredProps"
:disabled="readonly"
class="w-full text-xs"
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
:placeholder="widget.name || ''"
size="small"
rows="3"
@update:model-value="onChange"
@@ -16,11 +17,14 @@ import { computed } from 'vue'
import { useStringWidgetValue } from '@/composables/graph/useWidgetValue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
import {
INPUT_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
import { WidgetInputBaseClass } from './layout'
const props = defineProps<{
widget: SimplifiedWidget<string>
modelValue: string

View File

@@ -4,7 +4,7 @@
v-model="localValue"
v-bind="filteredProps"
:disabled="readonly"
class="w-full text-xs"
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
size="small"
@update:model-value="onChange"
/>
@@ -17,11 +17,13 @@ import { computed } from 'vue'
import { useWidgetValue } from '@/composables/graph/useWidgetValue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
import {
PANEL_EXCLUDED_PROPS,
filterWidgetProps
} from '@/utils/widgetPropFilter'
import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
const props = defineProps<{

View File

@@ -1,6 +1,11 @@
<template>
<div
class="p-1 bg-zinc-500/10 rounded-lg outline outline-1 outline-offset-[-1px] outline-zinc-300/10 inline-flex justify-center items-center gap-1 hover:outline-blue-500/80"
:class="
cn(
WidgetInputBaseClass,
'p-1 inline-flex justify-center items-center gap-1'
)
"
>
<button
v-for="(option, index) in options"
@@ -33,6 +38,8 @@
<script setup lang="ts">
import { cn } from '@/utils/tailwindUtil'
import { WidgetInputBaseClass } from '../layout'
interface Props {
modelValue: string | null | undefined
options: any[]

View File

@@ -0,0 +1,14 @@
export const WidgetInputBaseClass = [
// Background
'bg-zinc-500/10',
// Outline
'border-none',
'outline',
'outline-1',
'outline-offset-[-1px]',
'outline-zinc-300/10',
// Rounded
'!rounded-lg',
// Hover
'hover:outline-blue-500/80'
].join(' ')

View File

@@ -25,6 +25,10 @@ const TYPE_TO_ENUM_MAP: Record<string, string> = {
COMBO: WidgetType.COMBO,
selectbutton: WidgetType.SELECTBUTTON,
SELECTBUTTON: WidgetType.SELECTBUTTON,
multiselect: WidgetType.MULTISELECT,
MULTISELECT: WidgetType.MULTISELECT,
treeselect: WidgetType.TREESELECT,
TREESELECT: WidgetType.TREESELECT,
// Boolean
toggle: WidgetType.TOGGLESWITCH,
@@ -34,6 +38,7 @@ const TYPE_TO_ENUM_MAP: Record<string, string> = {
// Multiline text
multiline: WidgetType.TEXTAREA,
textarea: WidgetType.TEXTAREA,
TEXTAREA: WidgetType.TEXTAREA,
customtext: WidgetType.TEXTAREA,
MARKDOWN: WidgetType.MARKDOWN,