@davidbirchall/core 1.0.8 → 1.0.9

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 (95) hide show
  1. package/dist/Button/types.d.ts +4 -0
  2. package/dist/Calendar/types.d.ts +22 -0
  3. package/{src/components/Card/types.ts → dist/Card/types.d.ts} +1 -1
  4. package/dist/Checkbox/types.d.ts +7 -0
  5. package/dist/DataTable/types.d.ts +11 -0
  6. package/dist/Dropdown/types.d.ts +13 -0
  7. package/dist/EmptyState/types.d.ts +8 -0
  8. package/dist/ErrorSummary/types.d.ts +4 -0
  9. package/dist/Heading/types.d.ts +6 -0
  10. package/dist/Input/types.d.ts +11 -0
  11. package/dist/Select/types.d.ts +15 -0
  12. package/dist/StatCard/types.d.ts +12 -0
  13. package/dist/Tag/types.d.ts +4 -0
  14. package/dist/TextArea/types.d.ts +11 -0
  15. package/dist/core.css +1 -0
  16. package/dist/core.js +24 -0
  17. package/dist/core.js.map +1 -0
  18. package/dist/core.umd.cjs +2 -0
  19. package/dist/core.umd.cjs.map +1 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/package.json +27 -0
  22. package/package.json +4 -1
  23. package/.storybook/main.ts +0 -18
  24. package/.storybook/preview.ts +0 -14
  25. package/src/components/Badge/Badge.stories.ts +0 -147
  26. package/src/components/Badge/Badge.test.ts +0 -57
  27. package/src/components/Badge/Badge.vue +0 -79
  28. package/src/components/Button/Button.stories.ts +0 -80
  29. package/src/components/Button/Button.test.ts +0 -145
  30. package/src/components/Button/Button.vue +0 -108
  31. package/src/components/Button/types.ts +0 -4
  32. package/src/components/Calendar/Calendar.stories.ts +0 -261
  33. package/src/components/Calendar/Calendar.test.ts +0 -119
  34. package/src/components/Calendar/Calendar.vue +0 -528
  35. package/src/components/Calendar/types.ts +0 -20
  36. package/src/components/Card/Card.stories.ts +0 -88
  37. package/src/components/Card/Card.test.ts +0 -173
  38. package/src/components/Card/Card.vue +0 -59
  39. package/src/components/Checkbox/Checkbox.stories.ts +0 -126
  40. package/src/components/Checkbox/Checkbox.test.ts +0 -155
  41. package/src/components/Checkbox/Checkbox.vue +0 -121
  42. package/src/components/Checkbox/types.ts +0 -7
  43. package/src/components/DataTable/DataTable.stories.ts +0 -156
  44. package/src/components/DataTable/DataTable.test.ts +0 -185
  45. package/src/components/DataTable/DataTable.vue +0 -177
  46. package/src/components/DataTable/types.ts +0 -12
  47. package/src/components/DatePicker/DatePicker.stories.ts +0 -172
  48. package/src/components/DatePicker/DatePicker.test.ts +0 -87
  49. package/src/components/DatePicker/DatePicker.vue +0 -302
  50. package/src/components/Dropdown/Dropdown.stories.ts +0 -231
  51. package/src/components/Dropdown/Dropdown.vue +0 -314
  52. package/src/components/Dropdown/types.ts +0 -14
  53. package/src/components/EmptyState/EmptyState.stories.ts +0 -189
  54. package/src/components/EmptyState/EmptyState.vue +0 -215
  55. package/src/components/EmptyState/types.ts +0 -8
  56. package/src/components/ErrorSummary/ErrorSummary.vue +0 -78
  57. package/src/components/ErrorSummary/types.ts +0 -4
  58. package/src/components/FormGroup/FormGroup.stories.ts +0 -264
  59. package/src/components/FormGroup/FormGroup.test.ts +0 -63
  60. package/src/components/FormGroup/FormGroup.vue +0 -58
  61. package/src/components/Heading/Heading.stories.ts +0 -121
  62. package/src/components/Heading/Heading.test.ts +0 -184
  63. package/src/components/Heading/Heading.vue +0 -95
  64. package/src/components/Heading/types.ts +0 -6
  65. package/src/components/Input/Input.stories.ts +0 -172
  66. package/src/components/Input/Input.test.ts +0 -213
  67. package/src/components/Input/Input.vue +0 -121
  68. package/src/components/Input/types.ts +0 -11
  69. package/src/components/Modal/Modal.stories.ts +0 -341
  70. package/src/components/Modal/Modal.test.ts +0 -99
  71. package/src/components/Modal/Modal.vue +0 -278
  72. package/src/components/ProgressBar/ProgressBar.stories.ts +0 -313
  73. package/src/components/ProgressBar/ProgressBar.test.ts +0 -98
  74. package/src/components/ProgressBar/ProgressBar.vue +0 -117
  75. package/src/components/Select/Select.stories.ts +0 -177
  76. package/src/components/Select/Select.test.ts +0 -225
  77. package/src/components/Select/Select.vue +0 -147
  78. package/src/components/Select/types.ts +0 -16
  79. package/src/components/StatCard/StatCard.stories.ts +0 -274
  80. package/src/components/StatCard/StatCard.vue +0 -226
  81. package/src/components/StatCard/types.ts +0 -12
  82. package/src/components/Tag/Tag.stories.ts +0 -78
  83. package/src/components/Tag/Tag.test.ts +0 -50
  84. package/src/components/Tag/Tag.vue +0 -71
  85. package/src/components/Tag/types.ts +0 -4
  86. package/src/components/TextArea/TextArea.stories.ts +0 -171
  87. package/src/components/TextArea/TextArea.test.ts +0 -202
  88. package/src/components/TextArea/TextArea.vue +0 -122
  89. package/src/components/TextArea/types.ts +0 -11
  90. package/src/components/index.ts +0 -5
  91. package/src/test/setup.ts +0 -1
  92. package/src/vite-env.d.ts +0 -6
  93. package/tsconfig.json +0 -29
  94. package/vite.config.ts +0 -33
  95. package/vitest.config.ts +0 -28
@@ -1,87 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { mount } from '@vue/test-utils'
3
- import DatePicker from './DatePicker.vue'
4
-
5
- describe('DatePicker', () => {
6
- it('renders correctly', () => {
7
- const wrapper = mount(DatePicker)
8
- expect(wrapper.find('.date-picker').exists()).toBe(true)
9
- expect(wrapper.find('.date-input').exists()).toBe(true)
10
- })
11
-
12
- it('shows placeholder', () => {
13
- const wrapper = mount(DatePicker, {
14
- props: {
15
- placeholder: 'Pick a date'
16
- }
17
- })
18
-
19
- const input = wrapper.find('.date-input')
20
- expect(input.attributes('placeholder')).toBe('Pick a date')
21
- })
22
-
23
- it('displays selected date', () => {
24
- const wrapper = mount(DatePicker, {
25
- props: {
26
- modelValue: '2026-03-15'
27
- }
28
- })
29
-
30
- const input = wrapper.find('.date-input')
31
- const value = (input.element as HTMLInputElement).value
32
- expect(value).toContain('Mar')
33
- expect(value).toContain('15')
34
- expect(value).toContain('2026')
35
- })
36
-
37
- it('opens calendar on click', async () => {
38
- const wrapper = mount(DatePicker, {
39
- attachTo: document.body
40
- })
41
-
42
- await wrapper.find('.date-picker-input').trigger('click')
43
-
44
- // Calendar should open
45
- expect(wrapper.vm.isOpen).toBe(true)
46
- })
47
-
48
- it('respects disabled state', async () => {
49
- const wrapper = mount(DatePicker, {
50
- props: {
51
- disabled: true
52
- }
53
- })
54
-
55
- const input = wrapper.find('.date-input')
56
- expect(input.attributes('disabled')).toBeDefined()
57
-
58
- await wrapper.find('.date-picker-input').trigger('click')
59
- expect(wrapper.vm.isOpen).toBe(false)
60
- })
61
-
62
- it('formats date in long format', () => {
63
- const wrapper = mount(DatePicker, {
64
- props: {
65
- modelValue: '2026-03-15',
66
- format: 'long'
67
- }
68
- })
69
-
70
- const input = wrapper.find('.date-input')
71
- const value = (input.element as HTMLInputElement).value
72
- expect(value).toContain('March')
73
- })
74
-
75
- it('displays date range', () => {
76
- const wrapper = mount(DatePicker, {
77
- props: {
78
- modelValue: { start: '2026-03-15', end: '2026-03-20' },
79
- mode: 'range'
80
- }
81
- })
82
-
83
- const input = wrapper.find('.date-input')
84
- const value = (input.element as HTMLInputElement).value
85
- expect(value).toContain('-')
86
- })
87
- })
@@ -1,302 +0,0 @@
1
- <template>
2
- <div class="date-picker" ref="datePickerRef">
3
- <div class="date-picker-input" @click="toggleCalendar">
4
- <input
5
- type="text"
6
- :id="inputId"
7
- :value="displayValue"
8
- :placeholder="placeholder"
9
- readonly
10
- :disabled="disabled"
11
- :class="['date-input', { 'date-input--error': error }]"
12
- />
13
- <span class="date-icon">📅</span>
14
- </div>
15
- <span v-if="error" class="date-error">{{ error }}</span>
16
-
17
- <Teleport to="body">
18
- <div
19
- v-if="isOpen"
20
- ref="dropdownRef"
21
- class="date-picker-dropdown"
22
- :style="dropdownStyle"
23
- >
24
- <Calendar
25
- v-model="internalValue"
26
- :mode="mode"
27
- :min-date="minDate"
28
- :max-date="maxDate"
29
- :disabled-dates="disabledDates"
30
- :first-day-of-week="firstDayOfWeek"
31
- :initial-date="typeof modelValue === 'string' ? modelValue : undefined"
32
- @update:model-value="handleDateSelect"
33
- />
34
- </div>
35
- </Teleport>
36
-
37
- <div
38
- v-if="isOpen"
39
- class="date-picker-overlay"
40
- @click="closeCalendar"
41
- ></div>
42
- </div>
43
- </template>
44
-
45
- <script setup lang="ts">
46
- import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
47
- import Calendar from '../Calendar/Calendar.vue'
48
- import type { CalendarProps } from '../Calendar/types'
49
-
50
- interface DatePickerProps extends Omit<CalendarProps, 'modelValue'> {
51
- modelValue?: string | string[] | { start: string; end: string }
52
- id?: string
53
- placeholder?: string
54
- disabled?: boolean
55
- format?: 'short' | 'long'
56
- error?: string
57
- }
58
-
59
- const props = withDefaults(defineProps<DatePickerProps>(), {
60
- placeholder: 'Select date',
61
- format: 'short',
62
- mode: 'single',
63
- firstDayOfWeek: 0
64
- })
65
-
66
- const emit = defineEmits<{
67
- 'update:modelValue': [value: string | string[] | { start: string; end: string }]
68
- }>()
69
-
70
- const isOpen = ref(false)
71
- let componentIdCounter = 0
72
- const generatedId = `date-input-${++componentIdCounter}`
73
- const inputId = computed(() => props.id || generatedId)
74
- const datePickerRef = ref<HTMLElement | null>(null)
75
- const dropdownRef = ref<HTMLElement | null>(null)
76
- const dropdownStyle = ref<{ top: string; left: string; width: string }>({
77
- top: '0px',
78
- left: '0px',
79
- width: '320px'
80
- })
81
-
82
- const internalValue = ref(props.modelValue)
83
-
84
- watch(() => props.modelValue, (newValue) => {
85
- internalValue.value = newValue
86
- }, { immediate: true })
87
-
88
- const displayValue = computed(() => {
89
- if (!props.modelValue) return ''
90
-
91
- if (props.mode === 'single' && typeof props.modelValue === 'string') {
92
- return formatDate(props.modelValue)
93
- }
94
-
95
- if (props.mode === 'multiple' && Array.isArray(props.modelValue)) {
96
- return props.modelValue.map(d => formatDate(d)).join(', ')
97
- }
98
-
99
- if (props.mode === 'range' && typeof props.modelValue === 'object' && 'start' in props.modelValue) {
100
- const { start, end } = props.modelValue
101
- if (start && end) {
102
- return `${formatDate(start)} - ${formatDate(end)}`
103
- }
104
- if (start) return formatDate(start)
105
- }
106
-
107
- return ''
108
- })
109
-
110
- const formatDate = (dateString: string): string => {
111
- // Parse date string as local date to avoid timezone issues
112
- const [year, month, day] = dateString.split('-').map(Number)
113
- const date = new Date(year, month - 1, day)
114
-
115
- if (props.format === 'long') {
116
- return date.toLocaleDateString('en-US', {
117
- year: 'numeric',
118
- month: 'long',
119
- day: 'numeric'
120
- })
121
- }
122
-
123
- return date.toLocaleDateString('en-US', {
124
- year: 'numeric',
125
- month: 'short',
126
- day: 'numeric'
127
- })
128
- }
129
-
130
- const toggleCalendar = () => {
131
- if (props.disabled) return
132
-
133
- if (isOpen.value) {
134
- closeCalendar()
135
- } else {
136
- openCalendar()
137
- }
138
- }
139
-
140
- const openCalendar = () => {
141
- isOpen.value = true
142
-
143
- // Calculate position
144
- setTimeout(() => {
145
- if (datePickerRef.value) {
146
- const rect = datePickerRef.value.getBoundingClientRect()
147
- const viewportHeight = window.innerHeight
148
- const spaceBelow = viewportHeight - rect.bottom
149
- const calendarHeight = 400 // Approximate height
150
-
151
- if (spaceBelow < calendarHeight && rect.top > calendarHeight) {
152
- // Show above
153
- dropdownStyle.value = {
154
- top: `${rect.top - calendarHeight + window.scrollY}px`,
155
- left: `${rect.left + window.scrollX}px`,
156
- width: '320px'
157
- }
158
- } else {
159
- // Show below
160
- dropdownStyle.value = {
161
- top: `${rect.bottom + 4 + window.scrollY}px`,
162
- left: `${rect.left + window.scrollX}px`,
163
- width: '320px'
164
- }
165
- }
166
- }
167
- }, 0)
168
- }
169
-
170
- const closeCalendar = () => {
171
- isOpen.value = false
172
- }
173
-
174
- const handleDateSelect = (value: string | string[] | { start: string; end: string }) => {
175
- internalValue.value = value
176
- emit('update:modelValue', value)
177
-
178
- // Auto-close for single mode
179
- if (props.mode === 'single') {
180
- setTimeout(() => {
181
- closeCalendar()
182
- }, 200)
183
- }
184
-
185
- // Auto-close for range mode when both dates selected
186
- if (props.mode === 'range' && typeof value === 'object' && 'start' in value && value.end) {
187
- setTimeout(() => {
188
- closeCalendar()
189
- }, 200)
190
- }
191
- }
192
-
193
- const handleClickOutside = (event: MouseEvent) => {
194
- if (!isOpen.value) return
195
-
196
- const target = event.target as Node
197
-
198
- // Check if click is inside the date picker input or dropdown
199
- if (datePickerRef.value?.contains(target)) return
200
- if (dropdownRef.value?.contains(target)) return
201
-
202
- // Click is outside, close the calendar
203
- closeCalendar()
204
- }
205
-
206
- onMounted(() => {
207
- document.addEventListener('click', handleClickOutside)
208
- })
209
-
210
- onBeforeUnmount(() => {
211
- document.removeEventListener('click', handleClickOutside)
212
- })
213
- </script>
214
-
215
- <style scoped>
216
- .date-picker {
217
- position: relative;
218
- width: 100%;
219
- }
220
-
221
- .date-picker-input {
222
- position: relative;
223
- display: flex;
224
- align-items: center;
225
- cursor: pointer;
226
- }
227
-
228
- .date-input {
229
- width: 100%;
230
- padding: 0.75rem 0.875rem;
231
- padding-right: 2.5rem;
232
- border: 1px solid #d1d5db;
233
- border-radius: 0.375rem;
234
- font-size: 1rem;
235
- line-height: 1.5;
236
- height: 48px;
237
- cursor: pointer;
238
- background: white;
239
- color: #1f2937;
240
- transition: all 0.2s ease-in-out;
241
- box-sizing: border-box;
242
- }
243
-
244
- .date-input:focus {
245
- outline: none;
246
- border-color: #3b82f6;
247
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
248
- }
249
-
250
- .date-input--error {
251
- border-color: #fca5a5;
252
- border-bottom: 2px solid #dc2626;
253
- background: #fef2f2;
254
- }
255
-
256
- .date-input--error:focus {
257
- border-color: #fca5a5;
258
- border-bottom: 2px solid #dc2626;
259
- box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12);
260
- }
261
-
262
- .date-input:disabled {
263
- background: #f3f4f6;
264
- cursor: not-allowed;
265
- color: #9ca3af;
266
- }
267
-
268
- .date-input::placeholder {
269
- color: #9ca3af;
270
- }
271
-
272
- .date-error {
273
- margin-top: 0.35rem;
274
- color: #dc2626;
275
- font-size: 0.8rem;
276
- }
277
-
278
- .date-icon {
279
- position: absolute;
280
- right: 0.75rem;
281
- pointer-events: none;
282
- font-size: 1rem;
283
- }
284
-
285
- .date-picker-overlay {
286
- position: fixed;
287
- top: 0;
288
- left: 0;
289
- width: 100%;
290
- height: 100%;
291
- background: transparent;
292
- z-index: 9998;
293
- }
294
-
295
- .date-picker-dropdown {
296
- position: absolute;
297
- z-index: 9999;
298
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
299
- border-radius: 0.75rem;
300
- background: white;
301
- }
302
- </style>
@@ -1,231 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/vue3'
2
- import Dropdown from './Dropdown.vue'
3
- import type { DropdownItem } from './types'
4
-
5
- const meta = {
6
- title: 'Components/Dropdown',
7
- component: Dropdown,
8
- tags: ['autodocs'],
9
- argTypes: {
10
- placement: {
11
- control: 'select',
12
- options: ['bottom-left', 'bottom-right', 'top-left', 'top-right'],
13
- description: 'Dropdown menu placement'
14
- },
15
- closeOnClick: {
16
- control: 'boolean',
17
- description: 'Close dropdown when item is clicked'
18
- },
19
- disabled: {
20
- control: 'boolean',
21
- description: 'Disabled state'
22
- },
23
- onSelect: { action: 'selected' }
24
- }
25
- } satisfies Meta<typeof Dropdown>
26
-
27
- export default meta
28
- type Story = StoryObj<typeof meta>
29
-
30
- const defaultItems: DropdownItem[] = [
31
- { label: 'Profile', value: 'profile', icon: '👤' },
32
- { label: 'Settings', value: 'settings', icon: '⚙️' },
33
- { label: '', value: 'divider', divider: true },
34
- { label: 'Logout', value: 'logout', icon: '🚪' }
35
- ]
36
-
37
- export const Default: Story = {
38
- args: {
39
- items: defaultItems,
40
- placement: 'bottom-right'
41
- },
42
- render: (args: any) => ({
43
- components: { Dropdown },
44
- setup() {
45
- return { args }
46
- },
47
- template: `
48
- render: (args: any) => ({
49
- <Dropdown v-bind="args" @select="args.onSelect">
50
- <template #trigger>
51
- <button style="padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; cursor: pointer;">
52
- Open Menu
53
- </button>
54
- </template>
55
- </Dropdown>
56
- </div>
57
- `
58
- })
59
- }
60
-
61
- export const WithHeader: Story = {
62
- args: {
63
- items: [
64
- { label: 'Dashboard', value: 'dashboard' },
65
- { label: 'Projects', value: 'projects' },
66
- { label: 'Team', value: 'team' }
67
- ],
68
- placement: 'bottom-left'
69
- },
70
- render: (args: any) => ({
71
- components: { Dropdown },
72
- setup() {
73
- return { args }
74
- },
75
- template: `
76
- <div style="padding: 100px; display: flex; justify-content: center;">
77
- <Dropdown v-bind="args" @select="args.onSelect">
78
- <template #trigger>
79
- <button style="padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; cursor: pointer;">
80
- Navigation
81
- </button>
82
- </template>
83
- <template #header>
84
- <div style="padding: 0.75rem; border-bottom: 1px solid #e5e7eb; font-weight: 600;">
85
- Quick Links
86
- </div>
87
- </template>
88
- </Dropdown>
89
- </div>
90
- `
91
- })
92
- }
93
- export const WithFooter: Story = {
94
- args: {
95
- items: [
96
- { label: 'New Document', value: 'new' },
97
- { label: 'Open', value: 'open' },
98
- { label: 'Save', value: 'save' }
99
- ]
100
- },
101
- render: (args: any) => ({
102
- components: { Dropdown },
103
- setup() {
104
- return { args }
105
- },
106
- template: `
107
- <div style="padding: 100px; display: flex; justify-content: center;">
108
- <Dropdown v-bind="args" @select="args.onSelect">
109
- <template #trigger>
110
- <button style="padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; cursor: pointer;">
111
- File Menu
112
- </button>
113
- </template>
114
- <template #footer>
115
- <div style="padding: 0.5rem; border-top: 1px solid #e5e7eb; text-align: center; font-size: 0.75rem; color: #6b7280;">
116
- v1.0.0
117
- </div>
118
- </template>
119
- </Dropdown>
120
- </div>
121
- `
122
- })
123
- }
124
-
125
- export const UserMenu: Story = {
126
- render: () => ({
127
- components: { Dropdown },
128
- setup() {
129
- const items: DropdownItem[] = [
130
- { label: 'View Profile', value: 'profile' },
131
- { label: 'Account Settings', value: 'settings' },
132
- { label: '', value: 'divider1', divider: true },
133
- { label: 'Help & Support', value: 'help' },
134
- { label: '', value: 'divider2', divider: true },
135
- { label: 'Sign Out', value: 'signout' }
136
- ]
137
- return { items }
138
- },
139
- template: `
140
- <div style="padding: 100px; display: flex; justify-content: center;">
141
- <Dropdown :items="items" placement="bottom-right">
142
- <template #trigger>
143
- <div style="width: 2.5rem; height: 2.5rem; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; display: flex; align-items: center; justify-content: center; font-weight: 600; cursor: pointer;">
144
- JD
145
- </div>
146
- </template>
147
- <template #header>
148
- <div style="padding: 0.75rem; border-bottom: 1px solid #e5e7eb;">
149
- <div style="font-weight: 600; font-size: 0.9375rem;">John Doe</div>
150
- <div style="font-size: 0.875rem; color: #6b7280;">john@example.com</div>
151
- </div>
152
- </template>
153
- </Dropdown>
154
- </div>
155
- `
156
- })
157
- }
158
-
159
- export const Disabled: Story = {
160
- args: {
161
- items: defaultItems,
162
- disabled: true
163
- },
164
- render: (args: any) => ({
165
- components: { Dropdown },
166
- setup() {
167
- return { args }
168
- },
169
- template: `
170
- <div style="padding: 100px; display: flex; justify-content: center;">
171
- <Dropdown v-bind="args">
172
- <template #trigger>
173
- <button style="padding: 0.5rem 1rem; background: #9ca3af; color: white; border: none; border-radius: 0.375rem; cursor: not-allowed;">
174
- Disabled Menu
175
- </button>
176
- </template>
177
- </Dropdown>
178
- </div>
179
- `
180
- })
181
- }
182
-
183
- export const Placements: Story = {
184
- render: () => ({
185
- components: { Dropdown },
186
- setup() {
187
- const items: DropdownItem[] = [
188
- { label: 'Option 1', value: '1' },
189
- { label: 'Option 2', value: '2' },
190
- { label: 'Option 3', value: '3' }
191
- ]
192
- return { items }
193
- },
194
- template: `
195
- <div style="padding: 150px; display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
196
- <div style="display: flex; flex-direction: column; gap: 1rem;">
197
- <Dropdown :items="items" placement="bottom-left">
198
- <template #trigger>
199
- <button style="padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; cursor: pointer; width: 150px;">
200
- Bottom Left
201
- </button>
202
- </template>
203
- </Dropdown>
204
- <Dropdown :items="items" placement="top-left">
205
- <template #trigger>
206
- <button style="padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; cursor: pointer; width: 150px;">
207
- Top Left
208
- </button>
209
- </template>
210
- </Dropdown>
211
- </div>
212
- <div style="display: flex; flex-direction: column; gap: 1rem; align-items: flex-end;">
213
- <Dropdown :items="items" placement="bottom-right">
214
- <template #trigger>
215
- <button style="padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; cursor: pointer; width: 150px;">
216
- Bottom Right
217
- </button>
218
- </template>
219
- </Dropdown>
220
- <Dropdown :items="items" placement="top-right">
221
- <template #trigger>
222
- <button style="padding: 0.5rem 1rem; background: #667eea; color: white; border: none; border-radius: 0.375rem; cursor: pointer; width: 150px;">
223
- Top Right
224
- </button>
225
- </template>
226
- </Dropdown>
227
- </div>
228
- </div>
229
- `
230
- })
231
- }