@davidbirchall/core 1.0.6 → 1.0.8
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.
- package/.storybook/main.ts +18 -0
- package/.storybook/preview.ts +14 -0
- package/package.json +1 -4
- package/src/components/Badge/Badge.stories.ts +147 -0
- package/src/components/Badge/Badge.test.ts +57 -0
- package/src/components/Badge/Badge.vue +79 -0
- package/src/components/Button/Button.stories.ts +80 -0
- package/src/components/Button/Button.test.ts +145 -0
- package/src/components/Button/Button.vue +108 -0
- package/src/components/Button/types.ts +4 -0
- package/src/components/Calendar/Calendar.stories.ts +261 -0
- package/src/components/Calendar/Calendar.test.ts +119 -0
- package/src/components/Calendar/Calendar.vue +528 -0
- package/src/components/Calendar/types.ts +20 -0
- package/src/components/Card/Card.stories.ts +88 -0
- package/src/components/Card/Card.test.ts +173 -0
- package/src/components/Card/Card.vue +59 -0
- package/{dist/Card/types.d.ts → src/components/Card/types.ts} +1 -1
- package/src/components/Checkbox/Checkbox.stories.ts +126 -0
- package/src/components/Checkbox/Checkbox.test.ts +155 -0
- package/src/components/Checkbox/Checkbox.vue +121 -0
- package/src/components/Checkbox/types.ts +7 -0
- package/src/components/DataTable/DataTable.stories.ts +156 -0
- package/src/components/DataTable/DataTable.test.ts +185 -0
- package/src/components/DataTable/DataTable.vue +177 -0
- package/src/components/DataTable/types.ts +12 -0
- package/src/components/DatePicker/DatePicker.stories.ts +172 -0
- package/src/components/DatePicker/DatePicker.test.ts +87 -0
- package/src/components/DatePicker/DatePicker.vue +302 -0
- package/src/components/Dropdown/Dropdown.stories.ts +231 -0
- package/src/components/Dropdown/Dropdown.vue +314 -0
- package/src/components/Dropdown/types.ts +14 -0
- package/src/components/EmptyState/EmptyState.stories.ts +189 -0
- package/src/components/EmptyState/EmptyState.vue +215 -0
- package/src/components/EmptyState/types.ts +8 -0
- package/src/components/ErrorSummary/ErrorSummary.vue +78 -0
- package/src/components/ErrorSummary/types.ts +4 -0
- package/src/components/FormGroup/FormGroup.stories.ts +264 -0
- package/src/components/FormGroup/FormGroup.test.ts +63 -0
- package/src/components/FormGroup/FormGroup.vue +58 -0
- package/src/components/Heading/Heading.stories.ts +121 -0
- package/src/components/Heading/Heading.test.ts +184 -0
- package/src/components/Heading/Heading.vue +95 -0
- package/src/components/Heading/types.ts +6 -0
- package/src/components/Input/Input.stories.ts +172 -0
- package/src/components/Input/Input.test.ts +213 -0
- package/src/components/Input/Input.vue +121 -0
- package/src/components/Input/types.ts +11 -0
- package/src/components/Modal/Modal.stories.ts +341 -0
- package/src/components/Modal/Modal.test.ts +99 -0
- package/src/components/Modal/Modal.vue +278 -0
- package/src/components/ProgressBar/ProgressBar.stories.ts +313 -0
- package/src/components/ProgressBar/ProgressBar.test.ts +98 -0
- package/src/components/ProgressBar/ProgressBar.vue +117 -0
- package/src/components/Select/Select.stories.ts +177 -0
- package/src/components/Select/Select.test.ts +225 -0
- package/src/components/Select/Select.vue +147 -0
- package/src/components/Select/types.ts +16 -0
- package/src/components/StatCard/StatCard.stories.ts +274 -0
- package/src/components/StatCard/StatCard.vue +226 -0
- package/src/components/StatCard/types.ts +12 -0
- package/src/components/Tag/Tag.stories.ts +78 -0
- package/src/components/Tag/Tag.test.ts +50 -0
- package/src/components/Tag/Tag.vue +71 -0
- package/src/components/Tag/types.ts +4 -0
- package/src/components/TextArea/TextArea.stories.ts +171 -0
- package/src/components/TextArea/TextArea.test.ts +202 -0
- package/src/components/TextArea/TextArea.vue +122 -0
- package/src/components/TextArea/types.ts +11 -0
- package/src/components/index.ts +5 -0
- package/src/test/setup.ts +1 -0
- package/src/vite-env.d.ts +6 -0
- package/tsconfig.json +29 -0
- package/vite.config.ts +33 -0
- package/vitest.config.ts +28 -0
- package/dist/Button/types.d.ts +0 -4
- package/dist/Calendar/types.d.ts +0 -22
- package/dist/Checkbox/types.d.ts +0 -7
- package/dist/DataTable/types.d.ts +0 -11
- package/dist/Dropdown/types.d.ts +0 -13
- package/dist/EmptyState/types.d.ts +0 -8
- package/dist/ErrorSummary/types.d.ts +0 -4
- package/dist/Heading/types.d.ts +0 -6
- package/dist/Input/types.d.ts +0 -11
- package/dist/Select/types.d.ts +0 -15
- package/dist/StatCard/types.d.ts +0 -12
- package/dist/Tag/types.d.ts +0 -4
- package/dist/TextArea/types.d.ts +0 -11
- package/dist/core.css +0 -1
- package/dist/core.js +0 -24
- package/dist/core.js.map +0 -1
- package/dist/core.umd.cjs +0 -2
- package/dist/core.umd.cjs.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/package.json +0 -27
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
ref="dropdownRef"
|
|
4
|
+
class="dropdown"
|
|
5
|
+
:class="{ 'dropdown--disabled': disabled }"
|
|
6
|
+
>
|
|
7
|
+
<div @click="toggleDropdown" class="dropdown__trigger">
|
|
8
|
+
<slot name="trigger">
|
|
9
|
+
<button type="button" class="dropdown__button">
|
|
10
|
+
Menu
|
|
11
|
+
</button>
|
|
12
|
+
</slot>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<Teleport to="body">
|
|
16
|
+
<Transition name="dropdown-fade">
|
|
17
|
+
<div
|
|
18
|
+
v-if="isOpen"
|
|
19
|
+
ref="dropdownMenuRef"
|
|
20
|
+
class="dropdown__menu"
|
|
21
|
+
:class="`dropdown__menu--${placement}`"
|
|
22
|
+
:style="menuStyles"
|
|
23
|
+
@click="handleMenuClick"
|
|
24
|
+
>
|
|
25
|
+
<div v-if="$slots.header" class="dropdown__header">
|
|
26
|
+
<slot name="header" />
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div class="dropdown__content">
|
|
30
|
+
<slot>
|
|
31
|
+
<template v-for="(item, index) in items" :key="index">
|
|
32
|
+
<div v-if="item.divider" class="dropdown__divider" />
|
|
33
|
+
<button
|
|
34
|
+
v-else
|
|
35
|
+
type="button"
|
|
36
|
+
class="dropdown__item"
|
|
37
|
+
:class="{ 'dropdown__item--disabled': item.disabled }"
|
|
38
|
+
:disabled="item.disabled"
|
|
39
|
+
@click="() => handleItemClick(item)"
|
|
40
|
+
>
|
|
41
|
+
<span v-if="item.icon" class="dropdown__item-icon">{{ item.icon }}</span>
|
|
42
|
+
<span class="dropdown__item-label">{{ item.label }}</span>
|
|
43
|
+
</button>
|
|
44
|
+
</template>
|
|
45
|
+
</slot>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div v-if="$slots.footer" class="dropdown__footer">
|
|
49
|
+
<slot name="footer" />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</Transition>
|
|
53
|
+
</Teleport>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script setup lang="ts">
|
|
58
|
+
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
59
|
+
import type { DropdownProps, DropdownItem } from './types'
|
|
60
|
+
|
|
61
|
+
const props = withDefaults(defineProps<DropdownProps>(), {
|
|
62
|
+
items: () => [],
|
|
63
|
+
placement: 'bottom-left',
|
|
64
|
+
closeOnClick: true,
|
|
65
|
+
disabled: false
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const emit = defineEmits<{
|
|
69
|
+
select: [item: DropdownItem]
|
|
70
|
+
open: []
|
|
71
|
+
close: []
|
|
72
|
+
}>()
|
|
73
|
+
|
|
74
|
+
const dropdownRef = ref<HTMLElement | null>(null)
|
|
75
|
+
const dropdownMenuRef = ref<HTMLElement | null>(null)
|
|
76
|
+
const isOpen = ref(false)
|
|
77
|
+
const menuStyles = ref<Record<string, string>>({})
|
|
78
|
+
|
|
79
|
+
const toggleDropdown = () => {
|
|
80
|
+
if (props.disabled) return
|
|
81
|
+
|
|
82
|
+
if (isOpen.value) {
|
|
83
|
+
closeDropdown()
|
|
84
|
+
} else {
|
|
85
|
+
openDropdown()
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const openDropdown = () => {
|
|
90
|
+
isOpen.value = true
|
|
91
|
+
emit('open')
|
|
92
|
+
|
|
93
|
+
// Calculate position on next tick
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
updateMenuPosition()
|
|
96
|
+
}, 0)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const closeDropdown = () => {
|
|
100
|
+
isOpen.value = false
|
|
101
|
+
emit('close')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const updateMenuPosition = () => {
|
|
105
|
+
if (!dropdownRef.value || !dropdownMenuRef.value) return
|
|
106
|
+
|
|
107
|
+
const triggerRect = dropdownRef.value.getBoundingClientRect()
|
|
108
|
+
const menuRect = dropdownMenuRef.value.getBoundingClientRect()
|
|
109
|
+
|
|
110
|
+
let top = 0
|
|
111
|
+
let left = 0
|
|
112
|
+
|
|
113
|
+
switch (props.placement) {
|
|
114
|
+
case 'bottom-left':
|
|
115
|
+
top = triggerRect.bottom + 8
|
|
116
|
+
left = triggerRect.left
|
|
117
|
+
break
|
|
118
|
+
case 'bottom-right':
|
|
119
|
+
top = triggerRect.bottom + 8
|
|
120
|
+
left = triggerRect.right - menuRect.width
|
|
121
|
+
break
|
|
122
|
+
case 'top-left':
|
|
123
|
+
top = triggerRect.top - menuRect.height - 8
|
|
124
|
+
left = triggerRect.left
|
|
125
|
+
break
|
|
126
|
+
case 'top-right':
|
|
127
|
+
top = triggerRect.top - menuRect.height - 8
|
|
128
|
+
left = triggerRect.right - menuRect.width
|
|
129
|
+
break
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
menuStyles.value = {
|
|
133
|
+
top: `${top}px`,
|
|
134
|
+
left: `${left}px`
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const handleItemClick = (item: DropdownItem) => {
|
|
139
|
+
if (item.disabled) return
|
|
140
|
+
|
|
141
|
+
emit('select', item)
|
|
142
|
+
|
|
143
|
+
if (props.closeOnClick) {
|
|
144
|
+
closeDropdown()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const handleMenuClick = (event: MouseEvent) => {
|
|
149
|
+
// Prevent closing when clicking inside menu (unless on an item)
|
|
150
|
+
event.stopPropagation()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
154
|
+
if (!dropdownRef.value || !dropdownMenuRef.value) return
|
|
155
|
+
|
|
156
|
+
const target = event.target as Node
|
|
157
|
+
if (!dropdownRef.value.contains(target) && !dropdownMenuRef.value.contains(target)) {
|
|
158
|
+
closeDropdown()
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
onMounted(() => {
|
|
163
|
+
document.addEventListener('click', handleClickOutside)
|
|
164
|
+
window.addEventListener('scroll', updateMenuPosition, true)
|
|
165
|
+
window.addEventListener('resize', updateMenuPosition)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
onUnmounted(() => {
|
|
169
|
+
document.removeEventListener('click', handleClickOutside)
|
|
170
|
+
window.removeEventListener('scroll', updateMenuPosition, true)
|
|
171
|
+
window.removeEventListener('resize', updateMenuPosition)
|
|
172
|
+
})
|
|
173
|
+
</script>
|
|
174
|
+
|
|
175
|
+
<style scoped>
|
|
176
|
+
.dropdown {
|
|
177
|
+
position: relative;
|
|
178
|
+
display: inline-block;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.dropdown--disabled {
|
|
182
|
+
opacity: 0.5;
|
|
183
|
+
cursor: not-allowed;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.dropdown__trigger {
|
|
187
|
+
cursor: pointer;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.dropdown__button {
|
|
191
|
+
padding: 0.5rem 1rem;
|
|
192
|
+
background: white;
|
|
193
|
+
border: 1px solid #d1d5db;
|
|
194
|
+
border-radius: 6px;
|
|
195
|
+
font-size: 0.875rem;
|
|
196
|
+
font-weight: 500;
|
|
197
|
+
color: #374151;
|
|
198
|
+
cursor: pointer;
|
|
199
|
+
transition: all 0.2s;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.dropdown__button:hover {
|
|
203
|
+
background: #f9fafb;
|
|
204
|
+
border-color: #9ca3af;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.dropdown__menu {
|
|
208
|
+
position: fixed;
|
|
209
|
+
z-index: 1000;
|
|
210
|
+
min-width: 200px;
|
|
211
|
+
background: white;
|
|
212
|
+
border: 1px solid #e5e7eb;
|
|
213
|
+
border-radius: 8px;
|
|
214
|
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
215
|
+
overflow: hidden;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.dropdown__header,
|
|
219
|
+
.dropdown__footer {
|
|
220
|
+
padding: 0.75rem 1rem;
|
|
221
|
+
border-bottom: 1px solid #e5e7eb;
|
|
222
|
+
background: #f9fafb;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.dropdown__footer {
|
|
226
|
+
border-top: 1px solid #e5e7eb;
|
|
227
|
+
border-bottom: none;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.dropdown__content {
|
|
231
|
+
padding: 0.5rem 0;
|
|
232
|
+
max-height: 300px;
|
|
233
|
+
overflow-y: auto;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.dropdown__item {
|
|
237
|
+
width: 100%;
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
gap: 0.75rem;
|
|
241
|
+
padding: 0.625rem 1rem;
|
|
242
|
+
border: none;
|
|
243
|
+
background: transparent;
|
|
244
|
+
text-align: left;
|
|
245
|
+
font-size: 0.875rem;
|
|
246
|
+
color: #374151;
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
transition: background-color 0.15s;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.dropdown__item:hover {
|
|
252
|
+
background: #f3f4f6;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.dropdown__item:active {
|
|
256
|
+
background: #e5e7eb;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.dropdown__item--disabled {
|
|
260
|
+
color: #9ca3af;
|
|
261
|
+
cursor: not-allowed;
|
|
262
|
+
opacity: 0.5;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.dropdown__item--disabled:hover {
|
|
266
|
+
background: transparent;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.dropdown__item-icon {
|
|
270
|
+
font-size: 1.125rem;
|
|
271
|
+
flex-shrink: 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.dropdown__item-label {
|
|
275
|
+
flex: 1;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.dropdown__divider {
|
|
279
|
+
height: 1px;
|
|
280
|
+
margin: 0.5rem 0;
|
|
281
|
+
background: #e5e7eb;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* Transitions */
|
|
285
|
+
.dropdown-fade-enter-active,
|
|
286
|
+
.dropdown-fade-leave-active {
|
|
287
|
+
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.dropdown-fade-enter-from,
|
|
291
|
+
.dropdown-fade-leave-to {
|
|
292
|
+
opacity: 0;
|
|
293
|
+
transform: translateY(-8px);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Scrollbar styling */
|
|
297
|
+
.dropdown__content::-webkit-scrollbar {
|
|
298
|
+
width: 6px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.dropdown__content::-webkit-scrollbar-track {
|
|
302
|
+
background: #f1f1f1;
|
|
303
|
+
border-radius: 3px;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.dropdown__content::-webkit-scrollbar-thumb {
|
|
307
|
+
background: #d1d5db;
|
|
308
|
+
border-radius: 3px;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.dropdown__content::-webkit-scrollbar-thumb:hover {
|
|
312
|
+
background: #9ca3af;
|
|
313
|
+
}
|
|
314
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface DropdownItem {
|
|
2
|
+
label: string
|
|
3
|
+
value: string
|
|
4
|
+
icon?: string
|
|
5
|
+
disabled?: boolean
|
|
6
|
+
divider?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DropdownProps {
|
|
10
|
+
items?: DropdownItem[]
|
|
11
|
+
placement?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'
|
|
12
|
+
closeOnClick?: boolean
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import EmptyState from './EmptyState.vue'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/EmptyState',
|
|
6
|
+
component: EmptyState,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
title: {
|
|
10
|
+
control: 'text',
|
|
11
|
+
description: 'Empty state title'
|
|
12
|
+
},
|
|
13
|
+
description: {
|
|
14
|
+
control: 'text',
|
|
15
|
+
description: 'Empty state description'
|
|
16
|
+
},
|
|
17
|
+
icon: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
description: 'Icon or emoji to display'
|
|
20
|
+
},
|
|
21
|
+
actionText: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
description: 'Action button text'
|
|
24
|
+
},
|
|
25
|
+
actionVariant: {
|
|
26
|
+
control: 'select',
|
|
27
|
+
options: ['primary', 'secondary'],
|
|
28
|
+
description: 'Action button variant'
|
|
29
|
+
},
|
|
30
|
+
size: {
|
|
31
|
+
control: 'select',
|
|
32
|
+
options: ['small', 'medium', 'large'],
|
|
33
|
+
description: 'Empty state size'
|
|
34
|
+
},
|
|
35
|
+
onAction: { action: 'action-clicked' }
|
|
36
|
+
},
|
|
37
|
+
args: {
|
|
38
|
+
size: 'medium',
|
|
39
|
+
actionVariant: 'primary'
|
|
40
|
+
}
|
|
41
|
+
} satisfies Meta<typeof EmptyState>
|
|
42
|
+
|
|
43
|
+
export default meta
|
|
44
|
+
type Story = StoryObj<typeof meta>
|
|
45
|
+
|
|
46
|
+
export const Default: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
title: 'No items found',
|
|
49
|
+
description: 'Get started by creating a new item',
|
|
50
|
+
icon: '📭'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const WithAction: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
title: 'No projects yet',
|
|
57
|
+
description: 'Create your first project to get started',
|
|
58
|
+
icon: '📁',
|
|
59
|
+
actionText: 'Create Project'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const NoData: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
title: 'No data available',
|
|
66
|
+
description: 'There is no data to display at this time',
|
|
67
|
+
icon: '📊'
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const NoResults: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
title: 'No results found',
|
|
74
|
+
description: 'Try adjusting your search or filter to find what you\'re looking for',
|
|
75
|
+
icon: '🔍',
|
|
76
|
+
actionText: 'Clear Filters',
|
|
77
|
+
actionVariant: 'secondary'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const EmptyInbox: Story = {
|
|
82
|
+
args: {
|
|
83
|
+
title: 'Inbox Zero!',
|
|
84
|
+
description: 'You\'ve read all your messages. Great job!',
|
|
85
|
+
icon: '✅'
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const Small: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
title: 'No items',
|
|
92
|
+
description: 'Add your first item',
|
|
93
|
+
icon: '➕',
|
|
94
|
+
actionText: 'Add Item',
|
|
95
|
+
size: 'small'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const Large: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
title: 'Welcome to your dashboard',
|
|
102
|
+
description: 'Get started by creating your first project. You can add team members, set goals, and track progress all in one place.',
|
|
103
|
+
icon: '🎉',
|
|
104
|
+
actionText: 'Get Started',
|
|
105
|
+
size: 'large'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const CustomIcon: Story = {
|
|
110
|
+
args: {
|
|
111
|
+
title: 'No notifications',
|
|
112
|
+
description: 'You\'re all caught up!',
|
|
113
|
+
icon: '🔔'
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const ErrorState: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
title: 'Something went wrong',
|
|
120
|
+
description: 'We couldn\'t load your data. Please try again.',
|
|
121
|
+
icon: '⚠️',
|
|
122
|
+
actionText: 'Retry',
|
|
123
|
+
actionVariant: 'primary'
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export const WithCustomSlots: Story = {
|
|
128
|
+
render: (args: any) => ({
|
|
129
|
+
components: { EmptyState },
|
|
130
|
+
setup() {
|
|
131
|
+
return { args }
|
|
132
|
+
},
|
|
133
|
+
template: `
|
|
134
|
+
<EmptyState v-bind="args">
|
|
135
|
+
<template #icon>
|
|
136
|
+
<div style="font-size: 4rem;">🚀</div>
|
|
137
|
+
</template>
|
|
138
|
+
<template #title>
|
|
139
|
+
<h2 style="color: #667eea;">Custom Title Slot</h2>
|
|
140
|
+
</template>
|
|
141
|
+
<template #description>
|
|
142
|
+
<p style="color: #6b7280; font-style: italic;">
|
|
143
|
+
You can customize any part of the empty state with slots
|
|
144
|
+
</p>
|
|
145
|
+
</template>
|
|
146
|
+
</EmptyState>
|
|
147
|
+
`
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const AllSizes: Story = {
|
|
152
|
+
render: () => ({
|
|
153
|
+
components: { EmptyState },
|
|
154
|
+
template: `
|
|
155
|
+
<div style="display: flex; flex-direction: column; gap: 3rem;">
|
|
156
|
+
<div>
|
|
157
|
+
<h3 style="margin-bottom: 1rem;">Small</h3>
|
|
158
|
+
<EmptyState
|
|
159
|
+
size="small"
|
|
160
|
+
title="Small empty state"
|
|
161
|
+
description="Compact size for inline usage"
|
|
162
|
+
icon="📦"
|
|
163
|
+
actionText="Action"
|
|
164
|
+
/>
|
|
165
|
+
</div>
|
|
166
|
+
<div>
|
|
167
|
+
<h3 style="margin-bottom: 1rem;">Medium</h3>
|
|
168
|
+
<EmptyState
|
|
169
|
+
size="medium"
|
|
170
|
+
title="Medium empty state"
|
|
171
|
+
description="Default size for most use cases"
|
|
172
|
+
icon="📦"
|
|
173
|
+
actionText="Action"
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
<div>
|
|
177
|
+
<h3 style="margin-bottom: 1rem;">Large</h3>
|
|
178
|
+
<EmptyState
|
|
179
|
+
size="large"
|
|
180
|
+
title="Large empty state"
|
|
181
|
+
description="Larger size for prominent empty states with more detailed descriptions"
|
|
182
|
+
icon="📦"
|
|
183
|
+
actionText="Action"
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
`
|
|
188
|
+
})
|
|
189
|
+
}
|