@polymarbot/nuxt-layer-shadcn-ui 0.1.9 → 0.2.0-w

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 (103) hide show
  1. package/app/assets/styles/colors.css +10 -10
  2. package/app/assets/styles/globals.css +1 -0
  3. package/app/assets/styles/z-index.css +27 -0
  4. package/app/components/ui/Accordion/index.stories.ts +60 -56
  5. package/app/components/ui/Accordion/index.vue +1 -1
  6. package/app/components/ui/AdminLayout/SidebarMenus.vue +0 -2
  7. package/app/components/ui/AdminLayout/index.stories.ts +9 -8
  8. package/app/components/ui/Alert/index.stories.ts +28 -26
  9. package/app/components/ui/Alert/index.vue +6 -6
  10. package/app/components/ui/Alert/types.ts +2 -1
  11. package/app/components/ui/AlertDialog/index.stories.ts +85 -50
  12. package/app/components/ui/AsyncDataTable/index.stories.ts +53 -36
  13. package/app/components/ui/Avatar/index.stories.ts +56 -51
  14. package/app/components/ui/Avatar/index.vue +1 -1
  15. package/app/components/ui/Avatar/types.ts +5 -2
  16. package/app/components/ui/Badge/index.stories.ts +41 -41
  17. package/app/components/ui/Badge/index.vue +1 -1
  18. package/app/components/ui/Badge/types.ts +3 -1
  19. package/app/components/ui/Breadcrumb/index.stories.ts +48 -37
  20. package/app/components/ui/Breadcrumb/index.vue +1 -1
  21. package/app/components/ui/Button/index.stories.ts +94 -90
  22. package/app/components/ui/Button/index.vue +1 -1
  23. package/app/components/ui/Button/types.ts +4 -1
  24. package/app/components/ui/ButtonGroup/index.stories.ts +61 -49
  25. package/app/components/ui/Card/index.stories.ts +55 -47
  26. package/app/components/ui/Card/index.vue +1 -1
  27. package/app/components/ui/Checkbox/index.stories.ts +69 -46
  28. package/app/components/ui/Checkbox/index.vue +1 -1
  29. package/app/components/ui/CopyButton/index.stories.ts +89 -31
  30. package/app/components/ui/DataTable/index.stories.ts +218 -168
  31. package/app/components/ui/DataTable/index.vue +1 -1
  32. package/app/components/ui/DatePicker/index.stories.ts +131 -37
  33. package/app/components/ui/DateRangePicker/index.stories.ts +107 -33
  34. package/app/components/ui/Divider/index.stories.ts +46 -24
  35. package/app/components/ui/Divider/index.vue +1 -1
  36. package/app/components/ui/Drawer/index.stories.ts +131 -81
  37. package/app/components/ui/Drawer/index.vue +1 -7
  38. package/app/components/ui/Drawer/types.ts +1 -1
  39. package/app/components/ui/Dropdown/index.stories.ts +134 -89
  40. package/app/components/ui/Dropdown/index.vue +5 -1
  41. package/app/components/ui/Dropdown/types.ts +1 -1
  42. package/app/components/ui/FormItem/index.stories.ts +87 -43
  43. package/app/components/ui/FormItem/index.vue +1 -1
  44. package/app/components/ui/Help/index.stories.ts +46 -35
  45. package/app/components/ui/Icon/index.stories.ts +41 -43
  46. package/app/components/ui/Input/index.stories.ts +95 -41
  47. package/app/components/ui/Input/index.vue +1 -1
  48. package/app/components/ui/InputCurrency/index.stories.ts +89 -49
  49. package/app/components/ui/InputNumber/index.stories.ts +93 -29
  50. package/app/components/ui/InputNumber/index.vue +1 -1
  51. package/app/components/ui/InputOtp/index.stories.ts +6 -7
  52. package/app/components/ui/InputOtp/index.vue +1 -1
  53. package/app/components/ui/InputPercent/index.stories.ts +6 -7
  54. package/app/components/ui/InputRange/index.stories.ts +6 -7
  55. package/app/components/ui/Loading/index.stories.ts +19 -19
  56. package/app/components/ui/Markdown/index.stories.ts +7 -10
  57. package/app/components/ui/Modal/index.stories.ts +135 -80
  58. package/app/components/ui/Modal/index.vue +1 -6
  59. package/app/components/ui/Modal/types.ts +1 -1
  60. package/app/components/ui/ModalContent/index.stories.ts +54 -26
  61. package/app/components/ui/ModalContent/index.vue +2 -2
  62. package/app/components/ui/PageCard/index.stories.ts +177 -67
  63. package/app/components/ui/Pagination/index.stories.ts +68 -51
  64. package/app/components/ui/Pagination/index.vue +2 -2
  65. package/app/components/ui/Popover/index.stories.ts +47 -45
  66. package/app/components/ui/Popover/index.vue +1 -1
  67. package/app/components/ui/Qrcode/index.stories.ts +42 -34
  68. package/app/components/ui/RadioCardGroup/index.stories.ts +23 -32
  69. package/app/components/ui/RadioCardGroup/index.vue +1 -1
  70. package/app/components/ui/RadioGroup/index.stories.ts +123 -0
  71. package/app/components/ui/RadioGroup/index.vue +73 -0
  72. package/app/components/ui/RadioGroup/types.ts +13 -0
  73. package/app/components/ui/ScrollArea/index.stories.ts +69 -37
  74. package/app/components/ui/ScrollArea/index.vue +1 -1
  75. package/app/components/ui/SearchSelect/index.stories.ts +104 -66
  76. package/app/components/ui/Select/index.stories.ts +152 -98
  77. package/app/components/ui/Select/index.vue +3 -3
  78. package/app/components/ui/Skeleton/index.stories.ts +27 -30
  79. package/app/components/ui/Skeleton/index.vue +1 -1
  80. package/app/components/ui/Slider/index.stories.ts +73 -31
  81. package/app/components/ui/Slider/index.vue +1 -1
  82. package/app/components/ui/Surface/index.stories.ts +47 -21
  83. package/app/components/ui/Surface/index.vue +39 -28
  84. package/app/components/ui/Surface/types.ts +2 -2
  85. package/app/components/ui/Switch/index.stories.ts +6 -7
  86. package/app/components/ui/Switch/index.vue +1 -1
  87. package/app/components/ui/Tabs/index.stories.ts +103 -61
  88. package/app/components/ui/Tabs/index.vue +1 -1
  89. package/app/components/ui/Tag/index.stories.ts +42 -25
  90. package/app/components/ui/Tag/index.vue +39 -28
  91. package/app/components/ui/Tag/types.ts +2 -2
  92. package/app/components/ui/Textarea/index.stories.ts +73 -9
  93. package/app/components/ui/Textarea/index.vue +1 -1
  94. package/app/components/ui/Toast/index.stories.ts +71 -18
  95. package/app/components/ui/Toast/index.vue +1 -1
  96. package/app/components/ui/Tooltip/index.stories.ts +45 -38
  97. package/app/components/ui/Tooltip/index.vue +1 -1
  98. package/app/components/ui/WebLink/index.stories.ts +76 -41
  99. package/app/components/ui/WebLink/index.vue +1 -1
  100. package/package.json +2 -2
  101. package/app/components/ui/Radio/index.stories.ts +0 -71
  102. package/app/components/ui/Radio/index.vue +0 -10
  103. package/app/components/ui/Radio/types.ts +0 -3
@@ -1,55 +1,113 @@
1
1
  import type { Meta, StoryObj } from '@storybook/vue3'
2
2
  import CopyButton from './index.vue'
3
3
 
4
+ const variants = [ 'default', 'destructive', 'outline', 'secondary', 'ghost', 'link' ] as const
5
+ const sizes = [ 'sm', 'default', 'lg', 'icon-sm', 'icon', 'icon-lg' ] as const
6
+
4
7
  const meta = {
5
8
  title: 'UI/CopyButton',
6
9
  component: CopyButton,
7
10
  argTypes: {
8
11
  copy: { control: 'text' },
9
- variant: { control: 'select', options: [ 'default', 'destructive', 'outline', 'secondary', 'ghost' ]},
10
- size: { control: 'select', options: [ 'default', 'sm', 'lg', 'icon' ]},
12
+ beforeCopyText: { control: 'text' },
13
+ afterCopyText: { control: 'text' },
14
+ variant: { control: 'select', options: variants },
15
+ size: { control: 'select', options: sizes },
16
+ rounded: { control: 'boolean' },
17
+ disabled: { control: 'boolean' },
18
+ loading: { control: 'boolean' },
11
19
  },
12
20
  args: {
13
21
  copy: 'Hello, World!',
22
+ beforeCopyText: '',
23
+ afterCopyText: '',
14
24
  variant: 'outline',
25
+ size: 'default',
26
+ rounded: false,
27
+ disabled: false,
28
+ loading: false,
15
29
  },
30
+ render: args => ({
31
+ components: { CopyButton },
32
+ setup: () => ({ args }),
33
+ template: '<CopyButton v-bind="args">Copy Text</CopyButton>',
34
+ }),
16
35
  } satisfies Meta<typeof CopyButton>
17
36
 
18
37
  export default meta
19
38
  type Story = StoryObj<typeof meta>
20
39
 
21
- export const Default: Story = {
22
- render: args => ({
40
+ export const Default: Story = {}
41
+
42
+ export const IconOnly: Story = {
43
+ render: () => ({
44
+ components: { CopyButton },
45
+ template: `
46
+ <div class="flex items-center gap-4">
47
+ <CopyButton copy="Hello, World!" variant="outline" />
48
+ <CopyButton copy="Hello, World!" variant="ghost" />
49
+ <CopyButton copy="Hello, World!" variant="secondary" />
50
+ </div>
51
+ `,
52
+ }),
53
+ }
54
+
55
+ export const WithLabel: Story = {
56
+ render: () => ({
23
57
  components: { CopyButton },
24
- setup: () => ({ args }),
25
58
  template: `
26
- <div class="space-y-10">
27
- <section>
28
- <h3 class="mb-4 text-lg font-medium">Icon Only (default)</h3>
29
- <div class="flex items-center gap-4">
30
- <CopyButton v-bind="args" />
31
- <CopyButton v-bind="args" variant="ghost" />
32
- <CopyButton v-bind="args" variant="secondary" />
33
- </div>
34
- </section>
35
-
36
- <section>
37
- <h3 class="mb-4 text-lg font-medium">With Label</h3>
38
- <div class="flex items-center gap-4">
39
- <CopyButton v-bind="args">Copy Text</CopyButton>
40
- <CopyButton v-bind="args" variant="secondary">Copy Address</CopyButton>
41
- </div>
42
- </section>
43
-
44
- <section>
45
- <h3 class="mb-4 text-lg font-medium">Sizes</h3>
46
- <div class="flex items-center gap-4">
47
- <CopyButton v-bind="args" size="sm">Small</CopyButton>
48
- <CopyButton v-bind="args" size="default">Default</CopyButton>
49
- <CopyButton v-bind="args" size="lg">Large</CopyButton>
50
- </div>
51
- </section>
59
+ <div class="flex items-center gap-4">
60
+ <CopyButton copy="Hello, World!" variant="outline">Copy Text</CopyButton>
61
+ <CopyButton copy="123 Main St" variant="secondary">Copy Address</CopyButton>
52
62
  </div>
53
63
  `,
54
64
  }),
55
65
  }
66
+
67
+ export const Variants: Story = {
68
+ render: () => ({
69
+ components: { CopyButton },
70
+ setup: () => ({ variants }),
71
+ template: `
72
+ <div class="flex flex-wrap items-center gap-3">
73
+ <CopyButton
74
+ v-for="v in variants"
75
+ :key="v"
76
+ :variant="v"
77
+ copy="Hello, World!"
78
+ >
79
+ {{ v }}
80
+ </CopyButton>
81
+ </div>
82
+ `,
83
+ }),
84
+ }
85
+
86
+ export const Sizes: Story = {
87
+ render: () => ({
88
+ components: { CopyButton },
89
+ template: `
90
+ <div class="flex items-center gap-4">
91
+ <CopyButton copy="Hello, World!" variant="outline" size="sm">Small</CopyButton>
92
+ <CopyButton copy="Hello, World!" variant="outline" size="default">Default</CopyButton>
93
+ <CopyButton copy="Hello, World!" variant="outline" size="lg">Large</CopyButton>
94
+ </div>
95
+ `,
96
+ }),
97
+ }
98
+
99
+ export const CustomTooltipText: Story = {
100
+ render: () => ({
101
+ components: { CopyButton },
102
+ template: `
103
+ <CopyButton
104
+ copy="secret-token-123"
105
+ before-copy-text="Click to copy token"
106
+ after-copy-text="Token copied!"
107
+ variant="outline"
108
+ >
109
+ Copy Token
110
+ </CopyButton>
111
+ `,
112
+ }),
113
+ }
@@ -1,9 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/vue3'
2
2
  import type { DataTableColumn } from './types'
3
- import DataTableRaw from './index.vue'
4
-
5
- // Cast generic component for Storybook compatibility
6
- const DataTable = DataTableRaw as any
3
+ import DataTable from './index.vue'
7
4
 
8
5
  interface User {
9
6
  id: number
@@ -30,37 +27,122 @@ const basicColumns: DataTableColumn[] = [
30
27
  { field: 'status', title: 'Status', width: '100px' },
31
28
  ]
32
29
 
30
+ const sortableColumns: DataTableColumn[] = [
31
+ { field: 'name', title: 'Name', width: '120px', sortable: true },
32
+ { field: 'email', title: 'Email', minWidth: '200px' },
33
+ { field: 'role', title: 'Role', width: '100px', sortable: true },
34
+ { field: 'amount', title: 'Amount', width: '120px', type: 'currency', sortable: true },
35
+ { field: 'createdAt', title: 'Created', width: '140px', type: 'date', sortable: true },
36
+ ]
37
+
38
+ const typeColumns: DataTableColumn[] = [
39
+ { field: 'name', title: 'Name', width: '120px' },
40
+ { field: 'amount', title: 'Amount', width: '120px', type: 'currency' },
41
+ { field: 'createdAt', title: 'Date', width: '140px', type: 'date' },
42
+ { field: 'status', title: 'Empty', width: '100px', type: 'empty' },
43
+ ]
44
+
45
+ const slotColumns: DataTableColumn[] = [
46
+ { field: 'name', title: 'Name', width: '120px' },
47
+ { field: 'email', title: 'Email', minWidth: '200px' },
48
+ { field: 'status', title: 'Status', width: '120px' },
49
+ { field: 'amount', title: 'Amount', width: '120px' },
50
+ ]
51
+
52
+ const frozenColumns: DataTableColumn[] = [
53
+ { field: 'name', title: 'Name', width: '120px', fixed: 'left' },
54
+ { field: 'email', title: 'Email', width: '250px', fixed: 'left' },
55
+ { field: 'role', title: 'Role', width: '150px' },
56
+ { field: 'status', title: 'Status', width: '150px' },
57
+ { field: 'amount', title: 'Amount', width: '150px' },
58
+ { field: 'createdAt', title: 'Created', width: '180px', type: 'date' },
59
+ { field: 'id', title: 'Actions', width: '100px', fixed: 'right' },
60
+ ]
61
+
33
62
  const meta = {
34
63
  title: 'UI/DataTable',
35
- component: DataTable,
64
+ component: DataTable as any,
36
65
  argTypes: {
66
+ selectionMode: { control: 'select', options: [ undefined, 'single', 'multiple' ]},
37
67
  loading: { control: 'boolean' },
68
+ sortBy: { control: 'text' },
69
+ sortOrder: { control: 'select', options: [ null, 1, -1 ]},
38
70
  },
39
71
  args: {
72
+ selectionMode: undefined,
40
73
  loading: false,
74
+ sortBy: null,
75
+ sortOrder: null,
41
76
  },
77
+ render: args => ({
78
+ components: { DataTable: DataTable as any },
79
+ setup: () => ({ args, data: sampleData, basicColumns }),
80
+ template: `
81
+ <div class="w-full">
82
+ <DataTable :data="data" :columns="basicColumns" v-bind="args" />
83
+ </div>
84
+ `,
85
+ }),
42
86
  } satisfies Meta
43
87
 
44
88
  export default meta
45
89
  type Story = StoryObj<typeof meta>
46
90
 
47
- export const Default: Story = {
48
- render: args => ({
49
- components: { DataTable },
91
+ export const Default: Story = {}
92
+
93
+ export const SingleSelection: Story = {
94
+ render: () => ({
95
+ components: { DataTable: DataTable as any },
96
+ setup () {
97
+ const selection = ref<User | null>(null)
98
+ return { data: sampleData, basicColumns, selection }
99
+ },
100
+ template: `
101
+ <div class="w-full">
102
+ <DataTable
103
+ :data="data"
104
+ :columns="basicColumns"
105
+ selectionMode="single"
106
+ v-model:selection="selection"
107
+ />
108
+ <div class="mt-2 text-sm text-muted-foreground">
109
+ Selected: {{ selection?.name ?? 'none' }}
110
+ </div>
111
+ </div>
112
+ `,
113
+ }),
114
+ }
115
+
116
+ export const MultipleSelection: Story = {
117
+ render: () => ({
118
+ components: { DataTable: DataTable as any },
119
+ setup () {
120
+ const selection = ref<User[]>([])
121
+ return { data: sampleData, basicColumns, selection }
122
+ },
123
+ template: `
124
+ <div class="w-full">
125
+ <DataTable
126
+ :data="data"
127
+ :columns="basicColumns"
128
+ selectionMode="multiple"
129
+ v-model:selection="selection"
130
+ />
131
+ <div class="mt-2 text-sm text-muted-foreground">
132
+ Selected: {{ selection.length > 0 ? selection.map(r => r.name).join(', ') : 'none' }}
133
+ </div>
134
+ </div>
135
+ `,
136
+ }),
137
+ }
138
+
139
+ export const Sortable: Story = {
140
+ render: () => ({
141
+ components: { DataTable: DataTable as any },
50
142
  setup () {
51
- const singleSelection = ref(null)
52
- const multipleSelection = ref([])
53
143
  const sortBy = ref<string | null>(null)
54
144
  const sortOrder = ref<number | null>(null)
55
145
 
56
- const sortableColumns: DataTableColumn[] = [
57
- { field: 'name', title: 'Name', width: '120px', sortable: true },
58
- { field: 'email', title: 'Email', minWidth: '200px' },
59
- { field: 'role', title: 'Role', width: '100px', sortable: true },
60
- { field: 'amount', title: 'Amount', width: '120px', type: 'currency', sortable: true },
61
- { field: 'createdAt', title: 'Created', width: '140px', type: 'date', sortable: true },
62
- ]
63
-
64
146
  const sortedData = computed(() => {
65
147
  if (!sortBy.value || !sortOrder.value) return sampleData
66
148
  return [ ...sampleData ].sort((a, b) => {
@@ -72,159 +154,127 @@ export const Default: Story = {
72
154
  })
73
155
  })
74
156
 
75
- const typeColumns: DataTableColumn[] = [
76
- { field: 'name', title: 'Name', width: '120px' },
77
- { field: 'amount', title: 'Amount', width: '120px', type: 'currency' },
78
- { field: 'createdAt', title: 'Date', width: '140px', type: 'date' },
79
- { field: 'status', title: 'Empty', width: '100px', type: 'empty' },
80
- ]
81
-
82
- const slotColumns: DataTableColumn[] = [
83
- { field: 'name', title: 'Name', width: '120px' },
84
- { field: 'email', title: 'Email', minWidth: '200px' },
85
- { field: 'status', title: 'Status', width: '120px' },
86
- { field: 'amount', title: 'Amount', width: '120px' },
87
- ]
88
-
89
- const frozenColumns: DataTableColumn[] = [
90
- { field: 'name', title: 'Name', width: '120px', fixed: 'left' },
91
- { field: 'email', title: 'Email', width: '250px', fixed: 'left' },
92
- { field: 'role', title: 'Role', width: '150px' },
93
- { field: 'status', title: 'Status', width: '150px' },
94
- { field: 'amount', title: 'Amount', width: '150px', type: 'currency' },
95
- { field: 'createdAt', title: 'Created', width: '180px', type: 'date' },
96
- { field: 'id', title: 'Actions', width: '100px', fixed: 'right' },
97
- ]
98
-
99
- return {
100
- args,
101
- data: sampleData,
102
- basicColumns,
103
- sortableColumns,
104
- typeColumns,
105
- slotColumns,
106
- frozenColumns,
107
- singleSelection,
108
- multipleSelection,
109
- sortBy,
110
- sortOrder,
111
- sortedData,
112
- }
157
+ return { sortableColumns, sortBy, sortOrder, sortedData }
113
158
  },
114
159
  template: `
115
- <div class="w-full space-y-10">
116
- <!-- Controlled -->
117
- <section>
118
- <h3 class="mb-4 text-lg font-medium">Controlled</h3>
119
- <DataTable :data="data" :columns="basicColumns" v-bind="args" />
120
- </section>
121
-
122
- <!-- Single Selection -->
123
- <section>
124
- <h3 class="mb-4 text-lg font-medium">Single Selection</h3>
125
- <DataTable
126
- :data="data"
127
- :columns="basicColumns"
128
- selectionMode="single"
129
- v-model:selection="singleSelection"
130
- />
131
- <div class="mt-2 text-sm text-muted-foreground">
132
- Selected: {{ singleSelection?.name ?? 'none' }}
133
- </div>
134
- </section>
135
-
136
- <!-- Multiple Selection -->
137
- <section>
138
- <h3 class="mb-4 text-lg font-medium">Multiple Selection</h3>
139
- <DataTable
140
- :data="data"
141
- :columns="basicColumns"
142
- selectionMode="multiple"
143
- v-model:selection="multipleSelection"
144
- />
145
- <div class="mt-2 text-sm text-muted-foreground">
146
- Selected: {{ multipleSelection.length > 0 ? multipleSelection.map(r => r.name).join(', ') : 'none' }}
147
- </div>
148
- </section>
149
-
150
- <!-- Sortable -->
151
- <section>
152
- <h3 class="mb-4 text-lg font-medium">Sortable Columns</h3>
153
- <DataTable
154
- :data="sortedData"
155
- :columns="sortableColumns"
156
- v-model:sortBy="sortBy"
157
- v-model:sortOrder="sortOrder"
158
- />
159
- <div class="mt-2 text-sm text-muted-foreground">
160
- Sort: {{ sortBy ?? 'none' }} / {{ sortOrder ?? 'none' }}
161
- </div>
162
- </section>
163
-
164
- <!-- Column Types -->
165
- <section>
166
- <h3 class="mb-4 text-lg font-medium">Column Types (text, currency, date, empty)</h3>
167
- <DataTable :data="data" :columns="typeColumns" />
168
- </section>
169
-
170
- <!-- Custom Slots -->
171
- <section>
172
- <h3 class="mb-4 text-lg font-medium">Custom Cell Slots</h3>
173
- <DataTable :data="data" :columns="slotColumns">
174
- <template #status="{ value }">
175
- <span
176
- :class="[
177
- 'inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium',
178
- value === 'active'
179
- ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
180
- : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200',
181
- ]"
182
- >
183
- {{ value }}
184
- </span>
185
- </template>
186
- <template #amount="{ value }">
187
- <span class="font-mono font-medium">\${{ Number(value).toFixed(2) }}</span>
188
- </template>
189
- </DataTable>
190
- </section>
191
-
192
- <!-- Empty State -->
193
- <section>
194
- <h3 class="mb-4 text-lg font-medium">Empty State</h3>
195
- <DataTable :data="[]" :columns="basicColumns" />
196
- </section>
197
-
198
- <!-- Footer Slot -->
199
- <section>
200
- <h3 class="mb-4 text-lg font-medium">Footer Slot</h3>
201
- <DataTable :data="data" :columns="slotColumns">
202
- <template #footer>
203
- <tr class="h-12 border-t text-sm font-medium">
204
- <td class="px-4">Total</td>
205
- <td class="px-4" colspan="2"></td>
206
- <td class="px-4 font-mono">\${{ data.reduce((sum, r) => sum + r.amount, 0).toFixed(2) }}</td>
207
- </tr>
208
- </template>
209
- </DataTable>
210
- </section>
211
-
212
- <!-- Frozen Columns -->
213
- <section>
214
- <h3 class="mb-4 text-lg font-medium">Frozen Columns (scroll horizontally)</h3>
215
- <div class="max-w-[600px]">
216
- <DataTable
217
- :data="data"
218
- :columns="frozenColumns"
219
- selectionMode="multiple"
220
- v-model:selection="multipleSelection"
160
+ <div class="w-full">
161
+ <DataTable
162
+ :data="sortedData"
163
+ :columns="sortableColumns"
164
+ v-model:sortBy="sortBy"
165
+ v-model:sortOrder="sortOrder"
166
+ />
167
+ <div class="mt-2 text-sm text-muted-foreground">
168
+ Sort: {{ sortBy ?? 'none' }} / {{ sortOrder ?? 'none' }}
169
+ </div>
170
+ </div>
171
+ `,
172
+ }),
173
+ }
174
+
175
+ export const ColumnTypes: Story = {
176
+ render: () => ({
177
+ components: { DataTable: DataTable as any },
178
+ setup: () => ({ data: sampleData, typeColumns }),
179
+ template: `
180
+ <div class="w-full">
181
+ <DataTable :data="data" :columns="typeColumns" />
182
+ </div>
183
+ `,
184
+ }),
185
+ }
186
+
187
+ export const CustomSlots: Story = {
188
+ render: () => ({
189
+ components: { DataTable: DataTable as any },
190
+ setup: () => ({ data: sampleData, slotColumns }),
191
+ template: `
192
+ <div class="w-full">
193
+ <DataTable :data="data" :columns="slotColumns">
194
+ <template #status="{ value }">
195
+ <span
196
+ :class="[
197
+ 'inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium',
198
+ value === 'active'
199
+ ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
200
+ : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200',
201
+ ]"
221
202
  >
222
- <template #id>
223
- <button class="text-sm text-primary underline">Edit</button>
224
- </template>
225
- </DataTable>
226
- </div>
227
- </section>
203
+ {{ value }}
204
+ </span>
205
+ </template>
206
+ <template #amount="{ value }">
207
+ <span class="font-mono font-medium">\${{ Number(value).toFixed(2) }}</span>
208
+ </template>
209
+ </DataTable>
210
+ </div>
211
+ `,
212
+ }),
213
+ }
214
+
215
+ export const EmptyState: Story = {
216
+ render: () => ({
217
+ components: { DataTable: DataTable as any },
218
+ setup: () => ({ basicColumns }),
219
+ template: `
220
+ <div class="w-full">
221
+ <DataTable :data="[]" :columns="basicColumns" />
222
+ </div>
223
+ `,
224
+ }),
225
+ }
226
+
227
+ export const FooterSlot: Story = {
228
+ render: () => ({
229
+ components: { DataTable: DataTable as any },
230
+ setup: () => ({ data: sampleData, slotColumns }),
231
+ template: `
232
+ <div class="w-full">
233
+ <DataTable :data="data" :columns="slotColumns">
234
+ <template #footer>
235
+ <tr class="h-12 border-t text-sm font-medium">
236
+ <td class="px-4">Total</td>
237
+ <td class="px-4" colspan="2"></td>
238
+ <td class="px-4 font-mono">\${{ data.reduce((sum, r) => sum + r.amount, 0).toFixed(2) }}</td>
239
+ </tr>
240
+ </template>
241
+ </DataTable>
242
+ </div>
243
+ `,
244
+ }),
245
+ }
246
+
247
+ export const FrozenColumns: Story = {
248
+ render: () => ({
249
+ components: { DataTable: DataTable as any },
250
+ setup () {
251
+ const selection = ref<User[]>([])
252
+ return { data: sampleData, frozenColumns, selection }
253
+ },
254
+ template: `
255
+ <div class="max-w-[600px]">
256
+ <DataTable
257
+ :data="data"
258
+ :columns="frozenColumns"
259
+ selectionMode="multiple"
260
+ v-model:selection="selection"
261
+ >
262
+ <template #id>
263
+ <button class="text-sm text-primary underline">Edit</button>
264
+ </template>
265
+ </DataTable>
266
+ </div>
267
+ `,
268
+ }),
269
+ }
270
+
271
+ export const Loading: Story = {
272
+ render: () => ({
273
+ components: { DataTable: DataTable as any },
274
+ setup: () => ({ data: sampleData, basicColumns }),
275
+ template: `
276
+ <div class="w-full">
277
+ <DataTable :data="data" :columns="basicColumns" loading />
228
278
  </div>
229
279
  `,
230
280
  }),
@@ -10,7 +10,7 @@ import {
10
10
  TableHead,
11
11
  TableHeader,
12
12
  TableRow,
13
- } from '@polymarbot/nuxt-layer-shadcn-ui/app/components/shadcn/table'
13
+ } from '../../shadcn/table'
14
14
  import type {
15
15
  DataTableColumn,
16
16
  DataTableProps,