import type { Meta, StoryObj } from '@storybook/vue3-vite' import type { FormItem as FormItemType } from '@/types/settingTypes' import FormItem from './FormItem.vue' const meta: Meta = { title: 'Components/Common/FormItem', component: FormItem as any, parameters: { layout: 'centered', docs: { description: { component: 'FormItem is a generalized form component that dynamically renders different input types based on configuration. Supports text, number, boolean, combo, slider, knob, color, image, and custom renderer inputs with proper labeling and accessibility.' } } }, argTypes: { item: { control: 'object', description: 'FormItem configuration object defining the input type and properties' }, formValue: { control: 'text', description: 'The current form value (v-model)', defaultValue: '' }, id: { control: 'text', description: 'Optional HTML id for the form input', defaultValue: undefined }, labelClass: { control: 'text', description: 'Additional CSS classes for the label', defaultValue: undefined } }, tags: ['autodocs'] } export default meta type Story = StoryObj export const TextInput: Story = { render: (args: any) => ({ components: { FormItem }, setup() { return { args } }, data() { return { value: args.formValue || 'Default text value', textItem: { name: 'Workflow Name', type: 'text', tooltip: 'Enter a descriptive name for your workflow', attrs: { placeholder: 'e.g., SDXL Portrait Generation' } } as FormItemType } }, methods: { updateValue(newValue: string) { console.log('Text value updated:', newValue) this.value = newValue } }, template: `
Text input form item with tooltip:
Current value: "{{ value }}"
` }), args: { formValue: 'My Workflow' }, parameters: { docs: { description: { story: 'Text input FormItem with tooltip and placeholder. Hover over the info icon to see the tooltip.' } } } } export const NumberInput: Story = { render: () => ({ components: { FormItem }, data() { return { value: 7.5, numberItem: { name: 'CFG Scale', type: 'number', tooltip: 'Classifier-free guidance scale controls how closely the AI follows your prompt', attrs: { min: 1, max: 30, step: 0.5, showButtons: true } } as FormItemType } }, methods: { updateValue(newValue: number) { console.log('CFG scale updated:', newValue) this.value = newValue } }, template: `
Number input with controls and constraints:
Current CFG scale: {{ value }}
` }), parameters: { docs: { description: { story: 'Number input FormItem with min/max constraints and increment buttons for CFG scale parameter.' } } } } export const BooleanToggle: Story = { render: () => ({ components: { FormItem }, data() { return { value: false, booleanItem: { name: 'Enable GPU Acceleration', type: 'boolean', tooltip: 'Use GPU for faster processing when available' } as FormItemType } }, methods: { updateValue(newValue: boolean) { console.log('GPU acceleration toggled:', newValue) this.value = newValue } }, template: `
Boolean toggle switch form item:
GPU acceleration: {{ value ? 'Enabled' : 'Disabled' }}
` }), parameters: { docs: { description: { story: 'Boolean FormItem using ToggleSwitch component for enable/disable settings.' } } } } export const ComboSelect: Story = { render: () => ({ components: { FormItem }, data() { return { value: 'euler_a', comboItem: { name: 'Sampling Method', type: 'combo', tooltip: 'Algorithm used for denoising during generation', options: [ 'euler_a', 'euler', 'heun', 'dpm_2', 'dpm_2_ancestral', 'lms', 'dpm_fast', 'dpm_adaptive', 'dpmpp_2s_ancestral', 'dpmpp_sde', 'dpmpp_2m' ] } as FormItemType } }, methods: { updateValue(newValue: string) { console.log('Sampling method updated:', newValue) this.value = newValue } }, template: `
Combo select with sampling methods:
Selected: {{ value }}
` }), parameters: { docs: { description: { story: 'Combo select FormItem with ComfyUI sampling methods showing dropdown selection.' } } } } export const SliderInput: Story = { render: () => ({ components: { FormItem }, data() { return { value: 0.7, sliderItem: { name: 'Denoise Strength', type: 'slider', tooltip: 'How much to denoise the input image (0 = no change, 1 = complete redraw)', attrs: { min: 0, max: 1, step: 0.01 } } as FormItemType } }, methods: { updateValue(newValue: number) { console.log('Denoise strength updated:', newValue) this.value = newValue } }, template: `
Slider input with precise decimal control:
Denoise: {{ (value * 100).toFixed(0) }}%
` }), parameters: { docs: { description: { story: 'Slider FormItem for denoise strength with percentage display and fine-grained control.' } } } } export const KnobInput: Story = { render: () => ({ components: { FormItem }, data() { return { value: 20, knobItem: { name: 'Sampling Steps', type: 'knob', tooltip: 'Number of denoising steps - more steps = higher quality but slower generation', attrs: { min: 1, max: 150, step: 1 } } as FormItemType } }, methods: { updateValue(newValue: number) { console.log('Steps updated:', newValue) this.value = newValue } }, template: `
Knob input for sampling steps:
Steps: {{ value }} ({{ value < 10 ? 'Very Fast' : value < 30 ? 'Fast' : value < 50 ? 'Balanced' : 'High Quality' }})
` }), parameters: { docs: { description: { story: 'Knob FormItem for sampling steps with quality indicator based on step count.' } } } } export const MultipleFormItems: Story = { render: () => ({ components: { FormItem }, data() { return { widthValue: 512, heightValue: 512, stepsValue: 20, cfgValue: 7.5, samplerValue: 'euler_a', hiresValue: false } }, computed: { formItems() { return [ { name: 'Width', type: 'number', tooltip: 'Image width in pixels', attrs: { min: 64, max: 2048, step: 64 } }, { name: 'Height', type: 'number', tooltip: 'Image height in pixels', attrs: { min: 64, max: 2048, step: 64 } }, { name: 'Sampling Steps', type: 'knob', tooltip: 'Number of denoising steps', attrs: { min: 1, max: 150, step: 1 } }, { name: 'CFG Scale', type: 'slider', tooltip: 'Classifier-free guidance scale', attrs: { min: 1, max: 30, step: 0.5 } }, { name: 'Sampler', type: 'combo', tooltip: 'Sampling algorithm', options: ['euler_a', 'euler', 'heun', 'dpm_2', 'dpmpp_2m'] }, { name: 'High-res Fix', type: 'boolean', tooltip: 'Enable high-resolution generation' } ] as FormItemType[] }, allSettings() { return { width: this.widthValue, height: this.heightValue, steps: this.stepsValue, cfg: this.cfgValue, sampler: this.samplerValue, enableHires: this.hiresValue } } }, template: `

ComfyUI Generation Settings

Multiple form items demonstrating different input types in a realistic settings panel.

Current Settings:
{{ JSON.stringify(allSettings, null, 2) }}
` }), parameters: { docs: { description: { story: 'Multiple FormItems demonstrating all major input types in a realistic ComfyUI settings panel.' } } } } export const WithCustomLabels: Story = { render: () => ({ components: { FormItem }, data() { return { value: 'custom_model.safetensors', customItem: { name: 'Model File', type: 'text', tooltip: 'Select the checkpoint model file to use for generation', attrs: { placeholder: 'Select or enter model filename...' } } as FormItemType } }, methods: { updateValue(newValue: string) { console.log('Model file updated:', newValue) this.value = newValue } }, template: `
FormItem with custom label styling and slots:
Selected model: {{ value || 'None' }}
` }), parameters: { docs: { description: { story: 'FormItem with custom label styling and prefix/suffix slots for enhanced UI elements.' } } } } export const ColorPicker: Story = { render: () => ({ components: { FormItem }, data() { return { value: '#3b82f6', colorItem: { name: 'Theme Accent Color', type: 'color', tooltip: 'Primary accent color for the interface theme' } as FormItemType } }, methods: { updateValue(newValue: string) { console.log('Color updated:', newValue) this.value = newValue } }, template: `
Color picker form item:
Preview:
{{ value }}
` }), parameters: { docs: { description: { story: 'Color picker FormItem with live preview showing the selected color value.' } } } } export const ComboWithComplexOptions: Story = { render: () => ({ components: { FormItem }, data() { return { value: 'medium', comboItem: { name: 'Quality Preset', type: 'combo', tooltip: 'Predefined quality settings that adjust multiple parameters', options: [ { text: 'Draft (Fast)', value: 'draft' }, { text: 'Medium Quality', value: 'medium' }, { text: 'High Quality', value: 'high' }, { text: 'Ultra (Slow)', value: 'ultra' } ] } as FormItemType } }, methods: { updateValue(newValue: string) { console.log('Quality preset updated:', newValue) this.value = newValue } }, computed: { presetDescription() { const descriptions = { draft: 'Fast generation with 10 steps, suitable for previews', medium: 'Balanced quality with 20 steps, good for most use cases', high: 'High quality with 40 steps, slower but better results', ultra: 'Maximum quality with 80 steps, very slow but best results' } return (descriptions as any)[this.value] || 'Unknown preset' } }, template: `
Combo with complex option objects:
{{ presetDescription }}
` }), parameters: { docs: { description: { story: 'Complex combo FormItem with object options showing text/value pairs and descriptions.' } } } }