@polymarbot/nuxt-layer-shadcn-ui 0.3.9 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/app/components/ui/Accordion/index.stories.ts +43 -15
  2. package/app/components/ui/AdminLayout/index.stories.ts +6 -14
  3. package/app/components/ui/Alert/index.stories.ts +32 -2
  4. package/app/components/ui/AlertDialog/index.stories.ts +114 -5
  5. package/app/components/ui/AsyncDataTable/index.stories.ts +36 -2
  6. package/app/components/ui/Avatar/index.stories.ts +58 -4
  7. package/app/components/ui/Badge/index.stories.ts +48 -3
  8. package/app/components/ui/Breadcrumb/index.stories.ts +8 -19
  9. package/app/components/ui/Button/index.stories.ts +116 -7
  10. package/app/components/ui/ButtonGroup/index.stories.ts +63 -4
  11. package/app/components/ui/Card/index.stories.ts +40 -14
  12. package/app/components/ui/Checkbox/index.stories.ts +53 -3
  13. package/app/components/ui/CopyButton/index.stories.ts +77 -5
  14. package/app/components/ui/DataTable/index.stories.ts +184 -11
  15. package/app/components/ui/DatePicker/index.stories.ts +56 -7
  16. package/app/components/ui/DateRangePicker/index.stories.ts +40 -5
  17. package/app/components/ui/Divider/index.stories.ts +18 -15
  18. package/app/components/ui/Drawer/index.stories.ts +115 -16
  19. package/app/components/ui/Drawer/index.vue +27 -12
  20. package/app/components/ui/Dropdown/index.stories.ts +72 -54
  21. package/app/components/ui/Dropdown/index.vue +5 -8
  22. package/app/components/ui/Dropdown/types.ts +3 -8
  23. package/app/components/ui/FormItem/index.stories.ts +33 -45
  24. package/app/components/ui/Help/index.stories.ts +34 -2
  25. package/app/components/ui/Icon/index.stories.ts +41 -2
  26. package/app/components/ui/Input/index.stories.ts +73 -31
  27. package/app/components/ui/Input/index.vue +1 -0
  28. package/app/components/ui/InputCurrency/index.stories.ts +20 -65
  29. package/app/components/ui/InputNumber/index.stories.ts +31 -58
  30. package/app/components/ui/InputOtp/index.stories.ts +41 -9
  31. package/app/components/ui/InputPercent/index.stories.ts +3 -7
  32. package/app/components/ui/InputRange/index.stories.ts +51 -4
  33. package/app/components/ui/Loading/index.stories.ts +16 -1
  34. package/app/components/ui/Markdown/index.stories.ts +9 -0
  35. package/app/components/ui/Modal/index.stories.ts +133 -16
  36. package/app/components/ui/Modal/index.vue +27 -12
  37. package/app/components/ui/ModalContent/index.stories.ts +35 -11
  38. package/app/components/ui/PageCard/index.stories.ts +154 -56
  39. package/app/components/ui/Pagination/index.stories.ts +75 -41
  40. package/app/components/ui/Pagination/index.vue +4 -1
  41. package/app/components/ui/Popover/index.stories.ts +73 -27
  42. package/app/components/ui/Popover/index.vue +67 -4
  43. package/app/components/ui/Popover/types.ts +5 -2
  44. package/app/components/ui/Qrcode/index.stories.ts +32 -2
  45. package/app/components/ui/RadioCardGroup/index.stories.ts +45 -6
  46. package/app/components/ui/RadioGroup/index.stories.ts +64 -52
  47. package/app/components/ui/ScrollArea/index.stories.ts +21 -23
  48. package/app/components/ui/SearchSelect/index.stories.ts +73 -24
  49. package/app/components/ui/Select/index.stories.ts +121 -6
  50. package/app/components/ui/Select/index.vue +7 -6
  51. package/app/components/ui/Skeleton/index.stories.ts +34 -2
  52. package/app/components/ui/Slider/index.stories.ts +67 -4
  53. package/app/components/ui/Surface/index.stories.ts +86 -5
  54. package/app/components/ui/Surface/index.vue +115 -2
  55. package/app/components/ui/Surface/types.ts +2 -0
  56. package/app/components/ui/Switch/index.stories.ts +46 -0
  57. package/app/components/ui/Tabs/index.stories.ts +61 -64
  58. package/app/components/ui/Tag/index.stories.ts +45 -3
  59. package/app/components/ui/Textarea/index.stories.ts +61 -32
  60. package/app/components/ui/Toast/index.stories.ts +77 -3
  61. package/app/components/ui/Tooltip/index.stories.ts +60 -2
  62. package/app/components/ui/WebLink/index.stories.ts +53 -15
  63. package/package.json +2 -2
@@ -7,6 +7,7 @@ import {
7
7
  SheetFooter,
8
8
  SheetHeader,
9
9
  SheetTitle,
10
+ SheetTrigger,
10
11
  } from '../../shadcn/sheet'
11
12
  import type { DrawerProps } from './types'
12
13
 
@@ -41,28 +42,32 @@ const resolvedCancelText = computed(
41
42
  () => props.cancelText || t('common.actions.cancel'),
42
43
  )
43
44
 
44
- const sheetOpen = computed({
45
- get: () => props.visible ?? false,
46
- set: (value: boolean) => {
47
- if (!value && props.visible && !props.loading) {
48
- onCancel()
49
- }
50
- },
45
+ const sheetOpen = ref(props.visible ?? false)
46
+
47
+ watch(() => props.visible, value => {
48
+ if (value !== undefined) sheetOpen.value = value
51
49
  })
52
50
 
53
- watch(() => props.visible, visible => {
54
- if (visible) emit('open')
51
+ watch(sheetOpen, value => {
52
+ emit('update:visible', value)
53
+ if (value) emit('open')
55
54
  else emit('close')
56
55
  })
57
56
 
57
+ function onOpenUpdate (value: boolean) {
58
+ if (!value && props.loading) return
59
+ if (value) sheetOpen.value = true
60
+ else onCancel()
61
+ }
62
+
58
63
  function onConfirm () {
59
64
  emit('confirm')
60
- emit('update:visible', false)
65
+ sheetOpen.value = false
61
66
  }
62
67
 
63
68
  function onCancel () {
64
69
  emit('cancel')
65
- emit('update:visible', false)
70
+ sheetOpen.value = false
66
71
  }
67
72
 
68
73
  function onPointerDownOutside (event: Event) {
@@ -75,7 +80,17 @@ const contentClass = computed(() =>
75
80
  </script>
76
81
 
77
82
  <template>
78
- <Sheet v-model:open="sheetOpen">
83
+ <Sheet
84
+ :open="sheetOpen"
85
+ @update:open="onOpenUpdate"
86
+ >
87
+ <SheetTrigger
88
+ v-if="$slots.trigger"
89
+ asChild
90
+ >
91
+ <slot name="trigger" />
92
+ </SheetTrigger>
93
+
79
94
  <SheetContent
80
95
  :side="side"
81
96
  :class="contentClass"
@@ -3,6 +3,10 @@ import type { DropdownItem } from './types'
3
3
  import Button from '../Button/index.vue'
4
4
  import Dropdown from './index.vue'
5
5
 
6
+ const triggers = [ 'click', 'hover' ] as const
7
+ const sides = [ 'top', 'bottom', 'left', 'right' ] as const
8
+ const aligns = [ 'start', 'center', 'end' ] as const
9
+
6
10
  const basicMenus: DropdownItem[] = [
7
11
  { label: 'Edit', icon: 'pencil' },
8
12
  { label: 'Duplicate', icon: 'copy' },
@@ -57,9 +61,9 @@ const meta = {
57
61
  component: Dropdown,
58
62
  argTypes: {
59
63
  menus: { control: 'object' },
60
- trigger: { control: 'inline-radio', options: [ 'click', 'hover' ]},
61
- side: { control: 'select', options: [ 'top', 'bottom', 'left', 'right' ]},
62
- align: { control: 'select', options: [ 'start', 'center', 'end' ]},
64
+ trigger: { control: 'inline-radio', options: triggers },
65
+ side: { control: 'select', options: sides },
66
+ align: { control: 'select', options: aligns },
63
67
  sideOffset: { control: 'number' },
64
68
  },
65
69
  args: {
@@ -89,71 +93,63 @@ export const Default: Story = {}
89
93
 
90
94
  export const HoverTrigger: Story = {
91
95
  parameters: noControls,
92
- render: () => ({
93
- components: { Dropdown, Button },
94
- setup: () => ({ basicMenus }),
95
- template: `
96
- <Dropdown :menus="basicMenus" trigger="hover">
97
- <Button variant="outline">Hover me</Button>
98
- </Dropdown>
99
- `,
100
- }),
101
- }
102
-
103
- export const ClickTrigger: Story = {
104
- parameters: noControls,
105
- render: () => ({
106
- components: { Dropdown, Button },
107
- setup: () => ({ basicMenus }),
108
- template: `
109
- <Dropdown :menus="basicMenus" trigger="click">
110
- <Button variant="outline">Click me</Button>
111
- </Dropdown>
112
- `,
113
- }),
96
+ args: {
97
+ menus: basicMenus,
98
+ trigger: 'hover',
99
+ },
114
100
  }
115
101
 
116
102
  export const WithDisabledItems: Story = {
117
103
  parameters: noControls,
118
- render: () => ({
119
- components: { Dropdown, Button },
120
- setup: () => ({ accountMenus }),
121
- template: `
122
- <Dropdown :menus="accountMenus" trigger="click">
123
- <Button variant="outline">Account</Button>
124
- </Dropdown>
125
- `,
126
- }),
104
+ args: {
105
+ menus: accountMenus,
106
+ trigger: 'click',
107
+ },
127
108
  }
128
109
 
129
110
  export const WithLinks: Story = {
130
111
  parameters: noControls,
131
- render: () => ({
132
- components: { Dropdown, Button },
133
- setup: () => ({ linkMenus }),
134
- template: `
135
- <Dropdown :menus="linkMenus" trigger="click">
136
- <Button variant="outline">Resources</Button>
137
- </Dropdown>
138
- `,
139
- }),
112
+ args: {
113
+ menus: linkMenus,
114
+ trigger: 'click',
115
+ },
140
116
  }
141
117
 
142
118
  export const WithGroups: Story = {
143
119
  parameters: noControls,
144
- render: () => ({
145
- components: { Dropdown, Button },
146
- setup: () => ({ groupedMenus }),
147
- template: `
148
- <Dropdown :menus="groupedMenus" trigger="click">
149
- <Button variant="outline">Menu with Groups</Button>
150
- </Dropdown>
151
- `,
152
- }),
120
+ args: {
121
+ menus: groupedMenus,
122
+ trigger: 'click',
123
+ },
153
124
  }
154
125
 
155
126
  export const CustomSlots: Story = {
156
- parameters: noControls,
127
+ parameters: {
128
+ ...noControls,
129
+ docs: {
130
+ source: {
131
+ code: `
132
+ <template>
133
+ <Dropdown :menus="customMenus" trigger="click">
134
+ <Button variant="outline">Custom Slots</Button>
135
+ <template #profile="{ item }">
136
+ <div class="flex flex-col gap-1 px-2 py-1.5">
137
+ <span class="font-semibold text-sm">{{ item.title }}</span>
138
+ <span class="text-xs text-muted-foreground">{{ item.email }}</span>
139
+ </div>
140
+ </template>
141
+ <template #logout>
142
+ <span class="flex items-center gap-2 text-danger font-semibold">
143
+ <span>🚪</span>
144
+ <span>Custom Logout</span>
145
+ </span>
146
+ </template>
147
+ </Dropdown>
148
+ </template>
149
+ `.trim(),
150
+ },
151
+ },
152
+ },
157
153
  render: () => ({
158
154
  components: { Dropdown, Button },
159
155
  setup: () => ({ customMenus }),
@@ -178,7 +174,29 @@ export const CustomSlots: Story = {
178
174
  }
179
175
 
180
176
  export const PopupSlot: Story = {
181
- parameters: noControls,
177
+ parameters: {
178
+ ...noControls,
179
+ docs: {
180
+ source: {
181
+ code: `
182
+ <template>
183
+ <Dropdown trigger="click">
184
+ <Button variant="outline">Custom Popup</Button>
185
+ <template #popup="{ hide }">
186
+ <div class="flex flex-col gap-2 p-3 min-w-[220px]">
187
+ <div class="text-sm font-semibold">Custom content</div>
188
+ <p class="text-sm text-muted-foreground">
189
+ Use the <code>popup</code> slot to render arbitrary content inside the menu.
190
+ </p>
191
+ <Button size="sm" @click="hide">Close</Button>
192
+ </div>
193
+ </template>
194
+ </Dropdown>
195
+ </template>
196
+ `.trim(),
197
+ },
198
+ },
199
+ },
182
200
  render: () => ({
183
201
  components: { Dropdown, Button },
184
202
  template: `
@@ -47,12 +47,11 @@ const actionColorVariants = cva('', {
47
47
  defaultVariants: { color: 'default' },
48
48
  })
49
49
 
50
+ defineOptions({ inheritAttrs: false })
51
+
50
52
  const props = withDefaults(defineProps<DropdownProps>(), {
51
53
  menus: () => [],
52
54
  trigger: 'hover',
53
- side: undefined,
54
- align: undefined,
55
- sideOffset: undefined,
56
55
  class: undefined,
57
56
  })
58
57
 
@@ -138,9 +137,7 @@ onBeforeUnmount(() => {
138
137
  <slot />
139
138
  </DropdownMenuTrigger>
140
139
  <DropdownMenuContent
141
- :side="side"
142
- :align="align"
143
- :sideOffset="sideOffset"
140
+ v-bind="$attrs"
144
141
  :class="props.class"
145
142
  @mouseenter="handleMenuEnter"
146
143
  @mouseleave="handleMenuLeave"
@@ -192,7 +189,7 @@ onBeforeUnmount(() => {
192
189
  <Icon
193
190
  v-if="item.active"
194
191
  name="check"
195
- class="ml-auto size-4"
192
+ class="size-4 ml-auto"
196
193
  />
197
194
  </DropdownMenuItem>
198
195
  <!-- Built-in: action (default) -->
@@ -208,7 +205,7 @@ onBeforeUnmount(() => {
208
205
  unstyled
209
206
  :href="item.href"
210
207
  :target="item.target"
211
- class="flex w-full items-center gap-2"
208
+ class="gap-2 flex w-full items-center"
212
209
  @click="handleItemAction(item, $event)"
213
210
  >
214
211
  <Icon
@@ -1,3 +1,4 @@
1
+ import type { DropdownMenuContentProps } from 'reka-ui'
1
2
  import type { Component } from 'vue'
2
3
 
3
4
  export type DropdownItemColor = 'default' | 'primary' | 'success' | 'info' | 'help' | 'warn' | 'danger'
@@ -71,17 +72,11 @@ export type DropdownItem
71
72
  | DropdownCustomActionItem
72
73
  | DropdownCustomLabelItem
73
74
 
74
- export interface DropdownProps {
75
+ export interface DropdownProps extends /* @vue-ignore */ DropdownMenuContentProps {
75
76
  /** Menu items to display in the dropdown (not required when using popup slot) */
76
77
  menus?: DropdownItem[]
77
- /** Trigger mode for showing the dropdown */
78
+ /** Trigger mode for showing the dropdown. Defaults to 'hover'. */
78
79
  trigger?: 'click' | 'hover'
79
- /** Preferred side of the trigger to render against. */
80
- side?: 'top' | 'bottom' | 'left' | 'right'
81
- /** Alignment against the trigger. */
82
- align?: 'start' | 'center' | 'end'
83
- /** Distance in pixels from the trigger. */
84
- sideOffset?: number
85
80
  /** Extra class for the dropdown content container. */
86
81
  class?: ClassValue
87
82
  }
@@ -45,66 +45,54 @@ export const Default: Story = {}
45
45
 
46
46
  export const Required: Story = {
47
47
  parameters: noControls,
48
- render: () => ({
49
- components: { FormItem, Input },
50
- setup () {
51
- const email = ref('')
52
- return { email }
53
- },
54
- template: `
55
- <div class="max-w-md">
56
- <FormItem label="Email" required>
57
- <Input v-model="email" placeholder="Enter your email" />
58
- </FormItem>
59
- </div>
60
- `,
61
- }),
48
+ args: {
49
+ label: 'Email',
50
+ required: true,
51
+ },
62
52
  }
63
53
 
64
54
  export const WithError: Story = {
65
55
  parameters: noControls,
66
- render: () => ({
67
- components: { FormItem, Input },
68
- template: `
69
- <div class="max-w-md">
70
- <FormItem label="Username" required error="Username is already taken">
71
- <Input model-value="admin" />
72
- </FormItem>
73
- </div>
74
- `,
75
- }),
56
+ args: {
57
+ label: 'Username',
58
+ required: true,
59
+ error: 'Username is already taken',
60
+ },
76
61
  }
77
62
 
78
63
  export const WithDescription: Story = {
79
64
  parameters: noControls,
80
- render: () => ({
81
- components: { FormItem, Input },
82
- template: `
83
- <div class="max-w-md">
84
- <FormItem label="Password" description="Must be at least 8 characters long">
85
- <Input type="password" placeholder="Enter password" />
86
- </FormItem>
87
- </div>
88
- `,
89
- }),
65
+ args: {
66
+ label: 'Password',
67
+ description: 'Must be at least 8 characters long',
68
+ },
90
69
  }
91
70
 
92
71
  export const Horizontal: Story = {
93
72
  parameters: noControls,
94
- render: () => ({
95
- components: { FormItem, Input },
96
- template: `
97
- <div class="max-w-md">
98
- <FormItem label="Display Name" orientation="horizontal">
99
- <Input placeholder="Enter display name" />
100
- </FormItem>
101
- </div>
102
- `,
103
- }),
73
+ args: {
74
+ label: 'Display Name',
75
+ orientation: 'horizontal',
76
+ },
104
77
  }
105
78
 
106
79
  export const Responsive: Story = {
107
- parameters: noControls,
80
+ parameters: {
81
+ ...noControls,
82
+ docs: {
83
+ source: {
84
+ code: `
85
+ <template>
86
+ <div class="@container/field-group resize-x overflow-auto rounded border border-dashed border-border bg-card p-4" style="min-width: 200px;">
87
+ <FormItem label="Address" orientation="responsive" description="Your mailing address">
88
+ <Input placeholder="Enter address" />
89
+ </FormItem>
90
+ </div>
91
+ </template>
92
+ `.trim(),
93
+ },
94
+ },
95
+ },
108
96
  render: () => ({
109
97
  components: { FormItem, Input },
110
98
  template: `
@@ -9,10 +9,12 @@ const meta = {
9
9
  argTypes: {
10
10
  text: { control: 'text' },
11
11
  position: { control: 'select', options: positions },
12
+ class: { control: 'text' },
12
13
  },
13
14
  args: {
14
15
  text: 'This is a helpful tooltip',
15
16
  position: 'top',
17
+ class: '',
16
18
  },
17
19
  render: args => ({
18
20
  components: { Help },
@@ -29,7 +31,23 @@ const noControls = { controls: { disable: true }} satisfies Story['parameters']
29
31
  export const Default: Story = {}
30
32
 
31
33
  export const Positions: Story = {
32
- parameters: noControls,
34
+ parameters: {
35
+ ...noControls,
36
+ docs: {
37
+ source: {
38
+ code: `
39
+ <template>
40
+ <div class="flex items-center gap-8 py-10 justify-center">
41
+ <Help text="Top tooltip" position="top" />
42
+ <Help text="Bottom tooltip" position="bottom" />
43
+ <Help text="Left tooltip" position="left" />
44
+ <Help text="Right tooltip" position="right" />
45
+ </div>
46
+ </template>
47
+ `.trim(),
48
+ },
49
+ },
50
+ },
33
51
  render: () => ({
34
52
  components: { Help },
35
53
  template: `
@@ -56,7 +74,21 @@ export const Positions: Story = {
56
74
  }
57
75
 
58
76
  export const InlineWithLabel: Story = {
59
- parameters: noControls,
77
+ parameters: {
78
+ ...noControls,
79
+ docs: {
80
+ source: {
81
+ code: `
82
+ <template>
83
+ <div class="flex items-center gap-1">
84
+ <span class="text-sm font-medium">API Key</span>
85
+ <Help text="Your unique API key for authentication" />
86
+ </div>
87
+ </template>
88
+ `.trim(),
89
+ },
90
+ },
91
+ },
60
92
  render: () => ({
61
93
  components: { Help },
62
94
  template: `
@@ -15,9 +15,11 @@ const meta = {
15
15
  component: Icon,
16
16
  argTypes: {
17
17
  name: { control: 'text' },
18
+ class: { control: 'text' },
18
19
  },
19
20
  args: {
20
21
  name: 'house',
22
+ class: '',
21
23
  },
22
24
  render: args => ({
23
25
  components: { Icon },
@@ -34,7 +36,27 @@ const noControls = { controls: { disable: true }} satisfies Story['parameters']
34
36
  export const Default: Story = {}
35
37
 
36
38
  export const CommonIcons: Story = {
37
- parameters: noControls,
39
+ parameters: {
40
+ ...noControls,
41
+ docs: {
42
+ source: {
43
+ code: `
44
+ <template>
45
+ <div class="grid grid-cols-8 gap-4">
46
+ <div
47
+ v-for="name in commonIcons"
48
+ :key="name"
49
+ class="flex flex-col items-center gap-2 rounded-md border p-3"
50
+ >
51
+ <Icon :name="name" class="size-5" />
52
+ <span class="text-xs text-muted-foreground">{{ name }}</span>
53
+ </div>
54
+ </div>
55
+ </template>
56
+ `.trim(),
57
+ },
58
+ },
59
+ },
38
60
  render: () => ({
39
61
  components: { Icon },
40
62
  setup: () => ({ commonIcons }),
@@ -54,7 +76,24 @@ export const CommonIcons: Story = {
54
76
  }
55
77
 
56
78
  export const Sizes: Story = {
57
- parameters: noControls,
79
+ parameters: {
80
+ ...noControls,
81
+ docs: {
82
+ source: {
83
+ code: `
84
+ <template>
85
+ <div class="flex items-end gap-4">
86
+ <Icon name="star" class="size-3" />
87
+ <Icon name="star" class="size-4" />
88
+ <Icon name="star" class="size-5" />
89
+ <Icon name="star" class="size-6" />
90
+ <Icon name="star" class="size-8" />
91
+ </div>
92
+ </template>
93
+ `.trim(),
94
+ },
95
+ },
96
+ },
58
97
  render: () => ({
59
98
  components: { Icon },
60
99
  template: `
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import EventLog from '#storybook/EventLog.vue'
2
3
  import Icon from '../Icon/index.vue'
3
4
  import Input from './index.vue'
4
5
 
@@ -11,6 +12,7 @@ const meta = {
11
12
  disabled: { control: 'boolean' },
12
13
  readonly: { control: 'boolean' },
13
14
  invalid: { control: 'boolean' },
15
+ class: { control: 'text' },
14
16
  },
15
17
  args: {
16
18
  modelValue: '',
@@ -18,12 +20,13 @@ const meta = {
18
20
  disabled: false,
19
21
  readonly: false,
20
22
  invalid: false,
23
+ class: 'max-w-sm',
21
24
  },
22
25
  render: args => ({
23
26
  components: { Input },
24
27
  setup: () => ({ args }),
25
28
  template: `
26
- <Input v-bind="args" placeholder="Type something..." class="max-w-sm" />
29
+ <Input v-bind="args" placeholder="Type something..." />
27
30
  `,
28
31
  }),
29
32
  } satisfies Meta<typeof Input>
@@ -35,25 +38,23 @@ const noControls = { controls: { disable: true }} satisfies Story['parameters']
35
38
 
36
39
  export const Default: Story = {}
37
40
 
38
- export const Controlled: Story = {
39
- parameters: noControls,
40
- render: () => ({
41
- components: { Input },
42
- setup () {
43
- const value = ref('')
44
- return { value }
45
- },
46
- template: `
47
- <div class="max-w-sm space-y-2">
48
- <Input v-model="value" placeholder="Enter your name..." />
49
- <div class="text-sm text-muted-foreground">Value: {{ value }}</div>
50
- </div>
51
- `,
52
- }),
53
- }
54
-
55
41
  export const WithPrefix: Story = {
56
- parameters: noControls,
42
+ parameters: {
43
+ ...noControls,
44
+ docs: {
45
+ source: {
46
+ code: `
47
+ <template>
48
+ <Input v-model="value" placeholder="Search...">
49
+ <template #prefix>
50
+ <Icon name="search" />
51
+ </template>
52
+ </Input>
53
+ </template>
54
+ `.trim(),
55
+ },
56
+ },
57
+ },
57
58
  render: () => ({
58
59
  components: { Input, Icon },
59
60
  setup () {
@@ -74,7 +75,22 @@ export const WithPrefix: Story = {
74
75
  }
75
76
 
76
77
  export const WithSuffix: Story = {
77
- parameters: noControls,
78
+ parameters: {
79
+ ...noControls,
80
+ docs: {
81
+ source: {
82
+ code: `
83
+ <template>
84
+ <Input v-model="value" placeholder="Email">
85
+ <template #suffix>
86
+ <Icon name="mail" />
87
+ </template>
88
+ </Input>
89
+ </template>
90
+ `.trim(),
91
+ },
92
+ },
93
+ },
78
94
  render: () => ({
79
95
  components: { Input, Icon },
80
96
  setup () {
@@ -95,7 +111,14 @@ export const WithSuffix: Story = {
95
111
  }
96
112
 
97
113
  export const Password: Story = {
98
- parameters: noControls,
114
+ parameters: {
115
+ ...noControls,
116
+ docs: {
117
+ source: {
118
+ code: '<Input v-model="value" type="password" placeholder="Password" />',
119
+ },
120
+ },
121
+ },
99
122
  render: () => ({
100
123
  components: { Input },
101
124
  setup () {
@@ -113,24 +136,43 @@ export const Password: Story = {
113
136
 
114
137
  export const Disabled: Story = {
115
138
  parameters: noControls,
116
- render: () => ({
117
- components: { Input },
118
- template: '<Input disabled placeholder="Disabled input" class="max-w-sm" />',
119
- }),
139
+ args: {
140
+ disabled: true,
141
+ modelValue: 'Disabled input',
142
+ },
120
143
  }
121
144
 
122
145
  export const Readonly: Story = {
123
146
  parameters: noControls,
124
- render: () => ({
125
- components: { Input },
126
- template: '<Input readonly modelValue="Read-only value" class="max-w-sm" />',
127
- }),
147
+ args: {
148
+ readonly: true,
149
+ modelValue: 'Read-only value',
150
+ },
128
151
  }
129
152
 
130
153
  export const Invalid: Story = {
154
+ parameters: noControls,
155
+ args: {
156
+ invalid: true,
157
+ modelValue: 'Invalid value',
158
+ },
159
+ }
160
+
161
+ export const EventHandling: Story = {
131
162
  parameters: noControls,
132
163
  render: () => ({
133
- components: { Input },
134
- template: '<Input invalid modelValue="Invalid value" class="max-w-sm" />',
164
+ components: { Input, EventLog },
165
+ setup: () => ({ value: ref('') }),
166
+ template: `
167
+ <EventLog v-slot="{ record }">
168
+ <Input
169
+ v-model="value"
170
+ class="max-w-sm"
171
+ placeholder="Type and blur to see events"
172
+ @update:modelValue="(v) => record('update:modelValue', v)"
173
+ @change="(v) => record('change', v)"
174
+ />
175
+ </EventLog>
176
+ `,
135
177
  }),
136
178
  }