@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,8 +1,11 @@
1
1
  import type { Meta, StoryObj } from '@storybook/vue3'
2
2
  import Button from '../Button/index.vue'
3
3
  import Input from '../Input/index.vue'
4
+ import type { DrawerSide } from './types'
4
5
  import Drawer from './index.vue'
5
6
 
7
+ const sides: DrawerSide[] = [ 'top', 'right', 'bottom', 'left' ]
8
+
6
9
  const meta = {
7
10
  title: 'UI/Drawer',
8
11
  component: Drawer,
@@ -14,110 +17,157 @@ const meta = {
14
17
  showClose: { control: 'boolean' },
15
18
  hideHeader: { control: 'boolean' },
16
19
  hideFooter: { control: 'boolean' },
17
- side: {
18
- control: 'select',
19
- options: [ 'top', 'right', 'bottom', 'left' ],
20
- },
20
+ side: { control: 'select', options: sides },
21
+ title: { control: 'text' },
22
+ description: { control: 'text' },
23
+ confirmText: { control: 'text' },
24
+ cancelText: { control: 'text' },
21
25
  },
22
26
  args: {
23
27
  loading: false,
24
28
  disabled: false,
25
29
  confirmDisabled: false,
26
- showCancel: false,
30
+ showCancel: true,
27
31
  showClose: true,
28
32
  hideHeader: false,
29
33
  hideFooter: false,
30
34
  side: 'right',
35
+ title: 'Drawer Title',
36
+ description: '',
37
+ confirmText: 'OK',
38
+ cancelText: 'Cancel',
31
39
  },
32
- } satisfies Meta
40
+ render: args => ({
41
+ components: { Drawer, Button, Input },
42
+ setup () {
43
+ const visible = ref(false)
44
+ return { args, visible }
45
+ },
46
+ template: `
47
+ <div>
48
+ <Button @click="visible = true">Open Drawer</Button>
49
+ <Drawer v-model:visible="visible" v-bind="args">
50
+ <p>This is the drawer content.</p>
51
+ <Input class="mt-4" placeholder="Try interacting with this input" />
52
+ </Drawer>
53
+ </div>
54
+ `,
55
+ }),
56
+ } satisfies Meta<typeof Drawer>
33
57
 
34
58
  export default meta
35
59
  type Story = StoryObj<typeof meta>
36
60
 
37
- export const Default: Story = {
38
- render: args => ({
61
+ export const Default: Story = {}
62
+
63
+ export const WithDescription: Story = {
64
+ render: () => ({
39
65
  components: { Drawer, Button, Input },
40
66
  setup () {
41
- const controlled = ref(false)
42
- const withDescription = ref(false)
67
+ const visible = ref(false)
68
+ return { visible }
69
+ },
70
+ template: `
71
+ <div>
72
+ <Button @click="visible = true">Open Drawer</Button>
73
+ <Drawer
74
+ v-model:visible="visible"
75
+ title="Edit Profile"
76
+ description="Update your personal information. Changes will be visible to other users immediately."
77
+ showCancel
78
+ confirmText="Save"
79
+ >
80
+ <div class="space-y-3">
81
+ <Input placeholder="Name" />
82
+ <Input placeholder="Email" />
83
+ </div>
84
+ </Drawer>
85
+ </div>
86
+ `,
87
+ }),
88
+ }
89
+
90
+ export const ScrollableContent: Story = {
91
+ render: () => ({
92
+ components: { Drawer, Button },
93
+ setup () {
94
+ const visible = ref(false)
95
+ return { visible }
96
+ },
97
+ template: `
98
+ <div>
99
+ <Button @click="visible = true">Open Drawer</Button>
100
+ <Drawer v-model:visible="visible" title="Terms of Service" showCancel confirmText="Accept">
101
+ <div class="space-y-4">
102
+ <p v-for="i in 20" :key="i">
103
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
104
+ </p>
105
+ </div>
106
+ </Drawer>
107
+ </div>
108
+ `,
109
+ }),
110
+ }
111
+
112
+ export const Sides: Story = {
113
+ render: () => ({
114
+ components: { Drawer, Button },
115
+ setup () {
43
116
  const sideTop = ref(false)
44
117
  const sideRight = ref(false)
45
118
  const sideBottom = ref(false)
46
119
  const sideLeft = ref(false)
47
- const scrollable = ref(false)
48
- return { args, controlled, withDescription, sideTop, sideRight, sideBottom, sideLeft, scrollable }
120
+ return { sideTop, sideRight, sideBottom, sideLeft }
49
121
  },
50
122
  template: `
51
- <div class="space-y-10">
52
- <!-- Controlled -->
53
- <section>
54
- <h3 class="mb-4 text-lg font-medium">Controlled</h3>
55
- <Button @click="controlled = true">Open Drawer</Button>
56
- <Drawer
57
- v-model:visible="controlled"
58
- v-bind="args"
59
- title="Drawer Title"
60
- confirmText="OK"
61
- cancelText="Cancel"
62
- >
63
- <p>This is the drawer content.</p>
64
- <Input class="mt-4" placeholder="Try interacting with this input" />
65
- </Drawer>
66
- </section>
67
-
68
- <!-- With Description -->
69
- <section>
70
- <h3 class="mb-4 text-lg font-medium">With Description</h3>
71
- <Button @click="withDescription = true">Open Drawer</Button>
72
- <Drawer
73
- v-model:visible="withDescription"
74
- title="Edit Profile"
75
- description="Update your personal information. Changes will be visible to other users immediately."
76
- showCancel
77
- confirmText="Save"
78
- >
79
- <div class="space-y-3">
80
- <Input placeholder="Name" />
81
- <Input placeholder="Email" />
82
- </div>
83
- </Drawer>
84
- </section>
85
-
86
- <!-- Scrollable Content -->
87
- <section>
88
- <h3 class="mb-4 text-lg font-medium">Scrollable Content</h3>
89
- <Button @click="scrollable = true">Open Drawer</Button>
90
- <Drawer v-model:visible="scrollable" title="Terms of Service" showCancel confirmText="Accept">
91
- <div class="space-y-4">
92
- <p v-for="i in 20" :key="i">
93
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
94
- </p>
95
- </div>
96
- </Drawer>
97
- </section>
123
+ <div>
124
+ <div class="flex gap-2">
125
+ <Button variant="outline" @click="sideTop = true">Top</Button>
126
+ <Button variant="outline" @click="sideRight = true">Right</Button>
127
+ <Button variant="outline" @click="sideBottom = true">Bottom</Button>
128
+ <Button variant="outline" @click="sideLeft = true">Left</Button>
129
+ </div>
130
+ <Drawer v-model:visible="sideTop" side="top" title="Top Drawer">
131
+ <p>Slides in from the top.</p>
132
+ </Drawer>
133
+ <Drawer v-model:visible="sideRight" side="right" title="Right Drawer">
134
+ <p>Slides in from the right.</p>
135
+ </Drawer>
136
+ <Drawer v-model:visible="sideBottom" side="bottom" title="Bottom Drawer">
137
+ <p>Slides in from the bottom.</p>
138
+ </Drawer>
139
+ <Drawer v-model:visible="sideLeft" side="left" title="Left Drawer">
140
+ <p>Slides in from the left.</p>
141
+ </Drawer>
142
+ </div>
143
+ `,
144
+ }),
145
+ }
98
146
 
99
- <!-- Side Variants -->
100
- <section>
101
- <h3 class="mb-4 text-lg font-medium">Side</h3>
102
- <div class="flex gap-2">
103
- <Button variant="outline" @click="sideTop = true">Top</Button>
104
- <Button variant="outline" @click="sideRight = true">Right</Button>
105
- <Button variant="outline" @click="sideBottom = true">Bottom</Button>
106
- <Button variant="outline" @click="sideLeft = true">Left</Button>
107
- </div>
108
- <Drawer v-model:visible="sideTop" side="top" title="Top Drawer">
109
- <p>Slides in from the top.</p>
110
- </Drawer>
111
- <Drawer v-model:visible="sideRight" side="right" title="Right Drawer">
112
- <p>Slides in from the right.</p>
113
- </Drawer>
114
- <Drawer v-model:visible="sideBottom" side="bottom" title="Bottom Drawer">
115
- <p>Slides in from the bottom.</p>
116
- </Drawer>
117
- <Drawer v-model:visible="sideLeft" side="left" title="Left Drawer">
118
- <p>Slides in from the left.</p>
119
- </Drawer>
120
- </section>
147
+ export const EventHandling: Story = {
148
+ render: () => ({
149
+ components: { Drawer, Button },
150
+ setup () {
151
+ const visible = ref(false)
152
+ return { visible }
153
+ },
154
+ template: `
155
+ <div>
156
+ <Button @click="visible = true">Open Drawer</Button>
157
+ <Drawer
158
+ v-model:visible="visible"
159
+ title="Event Demo"
160
+ description="Open the Actions panel to see emitted events."
161
+ showCancel
162
+ confirmText="Confirm"
163
+ @open="() => {}"
164
+ @close="() => {}"
165
+ @closed="() => {}"
166
+ @confirm="() => {}"
167
+ @cancel="() => {}"
168
+ >
169
+ <p>Click Confirm, Cancel, or the close button to see events fire.</p>
170
+ </Drawer>
121
171
  </div>
122
172
  `,
123
173
  }),
@@ -7,7 +7,7 @@ import {
7
7
  SheetFooter,
8
8
  SheetHeader,
9
9
  SheetTitle,
10
- } from '@polymarbot/nuxt-layer-shadcn-ui/app/components/shadcn/sheet'
10
+ } from '../../shadcn/sheet'
11
11
  import type { DrawerProps } from './types'
12
12
 
13
13
  const props = withDefaults(defineProps<DrawerProps>(), {
@@ -175,7 +175,6 @@ const contentClass = computed(() =>
175
175
  /* Translucent blur backdrop. SheetOverlay is rendered inside DialogPortal
176
176
  (outside component scope), so use a non-scoped style. */
177
177
  [data-slot='sheet-overlay'] {
178
- z-index: 200;
179
178
  background-color: rgba(252, 252, 252, 0.3);
180
179
  backdrop-filter: blur(2px);
181
180
  -webkit-backdrop-filter: blur(2px);
@@ -184,11 +183,6 @@ const contentClass = computed(() =>
184
183
  background-color: rgba(25, 25, 25, 0.3);
185
184
  }
186
185
 
187
- /* Raise above layout header (z-100) so the drawer covers it. */
188
- [data-slot='sheet-content'] {
189
- z-index: 200;
190
- }
191
-
192
186
  /* Hide SheetContent's hardcoded built-in close button (no data-slot);
193
187
  we render our own SheetClose above with loading-aware disable. */
194
188
  [data-slot='sheet-content'] > button:not([data-slot]) {
@@ -1,4 +1,4 @@
1
- import type { ButtonVariants } from '@polymarbot/nuxt-layer-shadcn-ui/app/components/shadcn/button'
1
+ import type { ButtonVariants } from '../../shadcn/button'
2
2
 
3
3
  export type DrawerSide = 'top' | 'right' | 'bottom' | 'left'
4
4
 
@@ -3,74 +3,132 @@ import type { DropdownItem } from './types'
3
3
  import Button from '../Button/index.vue'
4
4
  import Dropdown from './index.vue'
5
5
 
6
+ const basicMenus: DropdownItem[] = [
7
+ { label: 'Edit', icon: 'pencil' },
8
+ { label: 'Duplicate', icon: 'copy' },
9
+ { type: 'separator' },
10
+ { label: 'Delete', icon: 'trash-2', color: 'danger' },
11
+ ]
12
+
13
+ const accountMenus: DropdownItem[] = [
14
+ { label: 'Profile', icon: 'user' },
15
+ { label: 'Settings', icon: 'settings' },
16
+ { label: 'Admin Panel', icon: 'shield', disabled: true },
17
+ { type: 'separator' },
18
+ { label: 'Logout', icon: 'log-out' },
19
+ ]
20
+
21
+ const linkMenus: DropdownItem[] = [
22
+ { label: 'Documentation', icon: 'book-open', href: 'https://example.com/docs', target: '_blank' },
23
+ { label: 'Support', icon: 'life-buoy', href: 'https://example.com/support', target: '_blank' },
24
+ ]
25
+
26
+ const groupedMenus: DropdownItem[] = [
27
+ { type: 'label', label: 'Organization' },
28
+ { label: 'Switch Org', icon: 'arrow-left-right' },
29
+ { type: 'separator' },
30
+ { type: 'label', label: 'Account' },
31
+ { label: 'Profile', icon: 'user' },
32
+ { label: 'Settings', icon: 'settings' },
33
+ { type: 'separator' },
34
+ { label: 'Logout', icon: 'log-out' },
35
+ ]
36
+
37
+ const customMenus: DropdownItem[] = [
38
+ {
39
+ type: 'custom-label',
40
+ slot: 'profile',
41
+ title: 'Demo User',
42
+ email: 'demo@example.com',
43
+ },
44
+ { type: 'separator' },
45
+ { label: 'Profile', icon: 'user' },
46
+ { label: 'Settings', icon: 'settings' },
47
+ { type: 'separator' },
48
+ {
49
+ type: 'custom-action',
50
+ slot: 'logout',
51
+ command: () => alert('Logged out'),
52
+ },
53
+ ]
54
+
6
55
  const meta = {
7
56
  title: 'UI/Dropdown',
8
57
  component: Dropdown,
58
+ argTypes: {
59
+ 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' ]},
63
+ sideOffset: { control: 'number' },
64
+ },
65
+ args: {
66
+ menus: basicMenus,
67
+ trigger: 'click',
68
+ side: undefined,
69
+ align: undefined,
70
+ sideOffset: undefined,
71
+ },
72
+ render: args => ({
73
+ components: { Dropdown, Button },
74
+ setup: () => ({ args }),
75
+ template: `
76
+ <Dropdown v-bind="args">
77
+ <Button variant="outline">Open Menu</Button>
78
+ </Dropdown>
79
+ `,
80
+ }),
9
81
  } satisfies Meta<typeof Dropdown>
10
82
 
11
83
  export default meta
12
84
  type Story = StoryObj<typeof meta>
13
85
 
14
- const basicMenus: DropdownItem[] = [
15
- { label: 'Edit', icon: 'pencil' },
16
- { label: 'Duplicate', icon: 'copy' },
17
- { type: 'separator' },
18
- { label: 'Delete', icon: 'trash-2', color: 'danger' },
19
- ]
86
+ export const Default: Story = {}
20
87
 
21
- export const Default: Story = {
88
+ export const HoverTrigger: Story = {
22
89
  render: () => ({
23
90
  components: { Dropdown, Button },
24
- setup () {
25
- const menus: DropdownItem[] = [
26
- { label: 'Profile', icon: 'user' },
27
- { label: 'Settings', icon: 'settings' },
28
- { label: 'Admin Panel', icon: 'shield', disabled: true },
29
- { type: 'separator' },
30
- { label: 'Logout', icon: 'log-out' },
31
- ]
32
-
33
- const linkMenus: DropdownItem[] = [
34
- { label: 'Documentation', icon: 'book-open', href: 'https://example.com/docs', target: '_blank' },
35
- { label: 'Support', icon: 'life-buoy', href: 'https://example.com/support', target: '_blank' },
36
- ]
37
-
38
- return { basicMenus, menus, linkMenus }
39
- },
91
+ setup: () => ({ basicMenus }),
40
92
  template: `
41
- <div class="space-y-10">
42
- <!-- Hover Trigger -->
43
- <section>
44
- <h3 class="mb-4 text-lg font-medium">Hover Trigger</h3>
45
- <Dropdown :menus="basicMenus" trigger="hover">
46
- <Button variant="outline">Hover me</Button>
47
- </Dropdown>
48
- </section>
93
+ <Dropdown :menus="basicMenus" trigger="hover">
94
+ <Button variant="outline">Hover me</Button>
95
+ </Dropdown>
96
+ `,
97
+ }),
98
+ }
49
99
 
50
- <!-- Click Trigger -->
51
- <section>
52
- <h3 class="mb-4 text-lg font-medium">Click Trigger</h3>
53
- <Dropdown :menus="basicMenus" trigger="click">
54
- <Button variant="outline">Click me</Button>
55
- </Dropdown>
56
- </section>
100
+ export const ClickTrigger: Story = {
101
+ render: () => ({
102
+ components: { Dropdown, Button },
103
+ setup: () => ({ basicMenus }),
104
+ template: `
105
+ <Dropdown :menus="basicMenus" trigger="click">
106
+ <Button variant="outline">Click me</Button>
107
+ </Dropdown>
108
+ `,
109
+ }),
110
+ }
57
111
 
58
- <!-- With Disabled Items -->
59
- <section>
60
- <h3 class="mb-4 text-lg font-medium">With Disabled Items</h3>
61
- <Dropdown :menus="menus" trigger="click">
62
- <Button variant="outline">Account</Button>
63
- </Dropdown>
64
- </section>
112
+ export const WithDisabledItems: Story = {
113
+ render: () => ({
114
+ components: { Dropdown, Button },
115
+ setup: () => ({ accountMenus }),
116
+ template: `
117
+ <Dropdown :menus="accountMenus" trigger="click">
118
+ <Button variant="outline">Account</Button>
119
+ </Dropdown>
120
+ `,
121
+ }),
122
+ }
65
123
 
66
- <!-- With Links -->
67
- <section>
68
- <h3 class="mb-4 text-lg font-medium">With Links</h3>
69
- <Dropdown :menus="linkMenus" trigger="click">
70
- <Button variant="outline">Resources</Button>
71
- </Dropdown>
72
- </section>
73
- </div>
124
+ export const WithLinks: Story = {
125
+ render: () => ({
126
+ components: { Dropdown, Button },
127
+ setup: () => ({ linkMenus }),
128
+ template: `
129
+ <Dropdown :menus="linkMenus" trigger="click">
130
+ <Button variant="outline">Resources</Button>
131
+ </Dropdown>
74
132
  `,
75
133
  }),
76
134
  }
@@ -78,20 +136,7 @@ export const Default: Story = {
78
136
  export const WithGroups: Story = {
79
137
  render: () => ({
80
138
  components: { Dropdown, Button },
81
- setup () {
82
- const groupedMenus: DropdownItem[] = [
83
- { type: 'label', label: 'Organization' },
84
- { label: 'Switch Org', icon: 'arrow-left-right' },
85
- { type: 'separator' },
86
- { type: 'label', label: 'Account' },
87
- { label: 'Profile', icon: 'user' },
88
- { label: 'Settings', icon: 'settings' },
89
- { type: 'separator' },
90
- { label: 'Logout', icon: 'log-out' },
91
- ]
92
-
93
- return { groupedMenus }
94
- },
139
+ setup: () => ({ groupedMenus }),
95
140
  template: `
96
141
  <Dropdown :menus="groupedMenus" trigger="click">
97
142
  <Button variant="outline">Menu with Groups</Button>
@@ -100,30 +145,10 @@ export const WithGroups: Story = {
100
145
  }),
101
146
  }
102
147
 
103
- export const WithCustomSlots: Story = {
148
+ export const CustomSlots: Story = {
104
149
  render: () => ({
105
150
  components: { Dropdown, Button },
106
- setup () {
107
- const customMenus: DropdownItem[] = [
108
- {
109
- type: 'custom-label',
110
- slot: 'profile',
111
- title: 'Demo User',
112
- email: 'demo@example.com',
113
- },
114
- { type: 'separator' },
115
- { label: 'Profile', icon: 'user' },
116
- { label: 'Settings', icon: 'settings' },
117
- { type: 'separator' },
118
- {
119
- type: 'custom-action',
120
- slot: 'logout',
121
- command: () => alert('Logged out'),
122
- },
123
- ]
124
-
125
- return { customMenus }
126
- },
151
+ setup: () => ({ customMenus }),
127
152
  template: `
128
153
  <Dropdown :menus="customMenus" trigger="click">
129
154
  <Button variant="outline">Custom Slots</Button>
@@ -143,3 +168,23 @@ export const WithCustomSlots: Story = {
143
168
  `,
144
169
  }),
145
170
  }
171
+
172
+ export const PopupSlot: Story = {
173
+ render: () => ({
174
+ components: { Dropdown, Button },
175
+ template: `
176
+ <Dropdown trigger="click">
177
+ <Button variant="outline">Custom Popup</Button>
178
+ <template #popup="{ hide }">
179
+ <div class="flex flex-col gap-2 p-3 min-w-[220px]">
180
+ <div class="text-sm font-semibold">Custom content</div>
181
+ <p class="text-sm text-muted-foreground">
182
+ Use the <code>popup</code> slot to render arbitrary content inside the menu.
183
+ </p>
184
+ <Button size="sm" @click="hide">Close</Button>
185
+ </div>
186
+ </template>
187
+ </Dropdown>
188
+ `,
189
+ }),
190
+ }
@@ -6,7 +6,7 @@ import {
6
6
  DropdownMenuLabel,
7
7
  DropdownMenuSeparator,
8
8
  DropdownMenuTrigger,
9
- } from '@polymarbot/nuxt-layer-shadcn-ui/app/components/shadcn/dropdown-menu'
9
+ } from '../../shadcn/dropdown-menu'
10
10
  import { cva } from 'class-variance-authority'
11
11
  import type {
12
12
  DropdownActionItem,
@@ -18,6 +18,10 @@ const actionColorVariants = cva('', {
18
18
  variants: {
19
19
  color: {
20
20
  default: '',
21
+ primary: `
22
+ text-primary
23
+ focus:bg-primary/10 focus:text-primary
24
+ `,
21
25
  success: `
22
26
  text-success
23
27
  focus:bg-success/10 focus:text-success
@@ -1,6 +1,6 @@
1
1
  import type { Component } from 'vue'
2
2
 
3
- export type DropdownItemColor = 'default' | 'success' | 'info' | 'help' | 'warn' | 'danger'
3
+ export type DropdownItemColor = 'default' | 'primary' | 'success' | 'info' | 'help' | 'warn' | 'danger'
4
4
 
5
5
  export interface DropdownActionItem {
6
6
  /** Defaults to 'action' when omitted. */