@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.
Files changed (95) hide show
  1. package/.storybook/main.ts +18 -0
  2. package/.storybook/preview.ts +14 -0
  3. package/package.json +1 -4
  4. package/src/components/Badge/Badge.stories.ts +147 -0
  5. package/src/components/Badge/Badge.test.ts +57 -0
  6. package/src/components/Badge/Badge.vue +79 -0
  7. package/src/components/Button/Button.stories.ts +80 -0
  8. package/src/components/Button/Button.test.ts +145 -0
  9. package/src/components/Button/Button.vue +108 -0
  10. package/src/components/Button/types.ts +4 -0
  11. package/src/components/Calendar/Calendar.stories.ts +261 -0
  12. package/src/components/Calendar/Calendar.test.ts +119 -0
  13. package/src/components/Calendar/Calendar.vue +528 -0
  14. package/src/components/Calendar/types.ts +20 -0
  15. package/src/components/Card/Card.stories.ts +88 -0
  16. package/src/components/Card/Card.test.ts +173 -0
  17. package/src/components/Card/Card.vue +59 -0
  18. package/{dist/Card/types.d.ts → src/components/Card/types.ts} +1 -1
  19. package/src/components/Checkbox/Checkbox.stories.ts +126 -0
  20. package/src/components/Checkbox/Checkbox.test.ts +155 -0
  21. package/src/components/Checkbox/Checkbox.vue +121 -0
  22. package/src/components/Checkbox/types.ts +7 -0
  23. package/src/components/DataTable/DataTable.stories.ts +156 -0
  24. package/src/components/DataTable/DataTable.test.ts +185 -0
  25. package/src/components/DataTable/DataTable.vue +177 -0
  26. package/src/components/DataTable/types.ts +12 -0
  27. package/src/components/DatePicker/DatePicker.stories.ts +172 -0
  28. package/src/components/DatePicker/DatePicker.test.ts +87 -0
  29. package/src/components/DatePicker/DatePicker.vue +302 -0
  30. package/src/components/Dropdown/Dropdown.stories.ts +231 -0
  31. package/src/components/Dropdown/Dropdown.vue +314 -0
  32. package/src/components/Dropdown/types.ts +14 -0
  33. package/src/components/EmptyState/EmptyState.stories.ts +189 -0
  34. package/src/components/EmptyState/EmptyState.vue +215 -0
  35. package/src/components/EmptyState/types.ts +8 -0
  36. package/src/components/ErrorSummary/ErrorSummary.vue +78 -0
  37. package/src/components/ErrorSummary/types.ts +4 -0
  38. package/src/components/FormGroup/FormGroup.stories.ts +264 -0
  39. package/src/components/FormGroup/FormGroup.test.ts +63 -0
  40. package/src/components/FormGroup/FormGroup.vue +58 -0
  41. package/src/components/Heading/Heading.stories.ts +121 -0
  42. package/src/components/Heading/Heading.test.ts +184 -0
  43. package/src/components/Heading/Heading.vue +95 -0
  44. package/src/components/Heading/types.ts +6 -0
  45. package/src/components/Input/Input.stories.ts +172 -0
  46. package/src/components/Input/Input.test.ts +213 -0
  47. package/src/components/Input/Input.vue +121 -0
  48. package/src/components/Input/types.ts +11 -0
  49. package/src/components/Modal/Modal.stories.ts +341 -0
  50. package/src/components/Modal/Modal.test.ts +99 -0
  51. package/src/components/Modal/Modal.vue +278 -0
  52. package/src/components/ProgressBar/ProgressBar.stories.ts +313 -0
  53. package/src/components/ProgressBar/ProgressBar.test.ts +98 -0
  54. package/src/components/ProgressBar/ProgressBar.vue +117 -0
  55. package/src/components/Select/Select.stories.ts +177 -0
  56. package/src/components/Select/Select.test.ts +225 -0
  57. package/src/components/Select/Select.vue +147 -0
  58. package/src/components/Select/types.ts +16 -0
  59. package/src/components/StatCard/StatCard.stories.ts +274 -0
  60. package/src/components/StatCard/StatCard.vue +226 -0
  61. package/src/components/StatCard/types.ts +12 -0
  62. package/src/components/Tag/Tag.stories.ts +78 -0
  63. package/src/components/Tag/Tag.test.ts +50 -0
  64. package/src/components/Tag/Tag.vue +71 -0
  65. package/src/components/Tag/types.ts +4 -0
  66. package/src/components/TextArea/TextArea.stories.ts +171 -0
  67. package/src/components/TextArea/TextArea.test.ts +202 -0
  68. package/src/components/TextArea/TextArea.vue +122 -0
  69. package/src/components/TextArea/types.ts +11 -0
  70. package/src/components/index.ts +5 -0
  71. package/src/test/setup.ts +1 -0
  72. package/src/vite-env.d.ts +6 -0
  73. package/tsconfig.json +29 -0
  74. package/vite.config.ts +33 -0
  75. package/vitest.config.ts +28 -0
  76. package/dist/Button/types.d.ts +0 -4
  77. package/dist/Calendar/types.d.ts +0 -22
  78. package/dist/Checkbox/types.d.ts +0 -7
  79. package/dist/DataTable/types.d.ts +0 -11
  80. package/dist/Dropdown/types.d.ts +0 -13
  81. package/dist/EmptyState/types.d.ts +0 -8
  82. package/dist/ErrorSummary/types.d.ts +0 -4
  83. package/dist/Heading/types.d.ts +0 -6
  84. package/dist/Input/types.d.ts +0 -11
  85. package/dist/Select/types.d.ts +0 -15
  86. package/dist/StatCard/types.d.ts +0 -12
  87. package/dist/Tag/types.d.ts +0 -4
  88. package/dist/TextArea/types.d.ts +0 -11
  89. package/dist/core.css +0 -1
  90. package/dist/core.js +0 -24
  91. package/dist/core.js.map +0 -1
  92. package/dist/core.umd.cjs +0 -2
  93. package/dist/core.umd.cjs.map +0 -1
  94. package/dist/index.d.ts +0 -2
  95. package/dist/package.json +0 -27
@@ -0,0 +1,528 @@
1
+ <template>
2
+ <div class="calendar">
3
+ <div class="calendar-header">
4
+ <button @click.stop="previousMonth" class="calendar-nav" type="button">
5
+
6
+ </button>
7
+ <div class="calendar-title">
8
+ <button @click.stop="showMonthPicker" class="calendar-month-btn" type="button">
9
+ {{ monthName }}
10
+ </button>
11
+ <button @click.stop="showYearPicker" class="calendar-year-btn" type="button">
12
+ {{ currentYear }}
13
+ </button>
14
+ </div>
15
+ <button @click.stop="nextMonth" class="calendar-nav" type="button">
16
+
17
+ </button>
18
+ </div>
19
+
20
+ <div v-if="!pickerMode" class="calendar-body">
21
+ <div class="calendar-weekdays">
22
+ <div
23
+ v-for="day in weekdays"
24
+ :key="day"
25
+ class="calendar-weekday"
26
+ >
27
+ {{ day }}
28
+ </div>
29
+ </div>
30
+
31
+ <div class="calendar-days">
32
+ <button
33
+ v-for="day in calendarDays"
34
+ :key="day.dateString"
35
+ @click="selectDate(day)"
36
+ type="button"
37
+ :disabled="day.isDisabled"
38
+ :class="[
39
+ 'calendar-day',
40
+ {
41
+ 'calendar-day--other-month': !day.isCurrentMonth,
42
+ 'calendar-day--today': day.isToday,
43
+ 'calendar-day--selected': day.isSelected,
44
+ 'calendar-day--disabled': day.isDisabled,
45
+ 'calendar-day--in-range': day.isInRange
46
+ }
47
+ ]"
48
+ >
49
+ {{ day.date.getDate() }}
50
+ </button>
51
+ </div>
52
+ </div>
53
+
54
+ <div v-else-if="pickerMode === 'month'" class="calendar-picker">
55
+ <div class="calendar-picker-grid">
56
+ <button
57
+ v-for="(month, index) in monthNames"
58
+ :key="month"
59
+ @click.stop="selectMonth(index)"
60
+ type="button"
61
+ :class="[
62
+ 'calendar-picker-item',
63
+ { 'calendar-picker-item--selected': index === currentMonth }
64
+ ]"
65
+ >
66
+ {{ month }}
67
+ </button>
68
+ </div>
69
+ </div>
70
+
71
+ <div v-else-if="pickerMode === 'year'" class="calendar-picker">
72
+ <div class="calendar-picker-nav">
73
+ <button @click.stop="previousYearPage" class="calendar-nav" type="button">
74
+
75
+ </button>
76
+ <span class="calendar-picker-range">{{ yearRangeLabel }}</span>
77
+ <button @click.stop="nextYearPage" class="calendar-nav" type="button">
78
+
79
+ </button>
80
+ </div>
81
+ <div class="calendar-picker-grid">
82
+ <button
83
+ v-for="year in yearRange"
84
+ :key="year"
85
+ @click.stop="selectYear(year)"
86
+ type="button"
87
+ :class="[
88
+ 'calendar-picker-item',
89
+ { 'calendar-picker-item--selected': year === currentYear }
90
+ ]"
91
+ >
92
+ {{ year }}
93
+ </button>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </template>
98
+
99
+ <script setup lang="ts">
100
+ import { ref, computed } from 'vue'
101
+ import type { CalendarProps, CalendarDay } from './types'
102
+
103
+ const props = withDefaults(defineProps<CalendarProps>(), {
104
+ mode: 'single',
105
+ locale: 'en-US',
106
+ firstDayOfWeek: 0
107
+ })
108
+
109
+ const emit = defineEmits<{
110
+ 'update:modelValue': [value: string | string[] | { start: string; end: string }]
111
+ }>()
112
+
113
+ const getInitialDate = (): Date => {
114
+ if (props.initialDate) {
115
+ const [year, month, day] = props.initialDate.split('-').map(Number)
116
+ return new Date(year, month - 1, day)
117
+ }
118
+
119
+ if (props.modelValue) {
120
+ if (typeof props.modelValue === 'string') {
121
+ const [year, month, day] = props.modelValue.split('-').map(Number)
122
+ return new Date(year, month - 1, day)
123
+ }
124
+ if (typeof props.modelValue === 'object' && 'start' in props.modelValue && props.modelValue.start) {
125
+ const [year, month, day] = props.modelValue.start.split('-').map(Number)
126
+ return new Date(year, month - 1, day)
127
+ }
128
+ if (Array.isArray(props.modelValue) && props.modelValue.length > 0) {
129
+ const [year, month, day] = props.modelValue[0].split('-').map(Number)
130
+ return new Date(year, month - 1, day)
131
+ }
132
+ }
133
+
134
+ return new Date()
135
+ }
136
+
137
+ const initialDate = getInitialDate()
138
+ const currentMonth = ref(initialDate.getMonth())
139
+ const currentYear = ref(initialDate.getFullYear())
140
+ const pickerMode = ref<'month' | 'year' | null>(null)
141
+ const rangeStart = ref<string | null>(null)
142
+ const yearPageStart = ref(Math.floor(initialDate.getFullYear() / 12) * 12)
143
+
144
+ const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
145
+ const fullMonthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
146
+ const weekdayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
147
+
148
+ const monthName = computed(() => fullMonthNames[currentMonth.value])
149
+
150
+ const weekdays = computed(() => {
151
+ const days = [...weekdayNames]
152
+ if (props.firstDayOfWeek > 0) {
153
+ return [...days.slice(props.firstDayOfWeek), ...days.slice(0, props.firstDayOfWeek)]
154
+ }
155
+ return days
156
+ })
157
+
158
+ const yearRange = computed(() => {
159
+ return Array.from({ length: 12 }, (_, i) => yearPageStart.value + i)
160
+ })
161
+
162
+ const yearRangeLabel = computed(() => {
163
+ const start = yearPageStart.value
164
+ const end = start + 11
165
+ return `${start} - ${end}`
166
+ })
167
+
168
+ const calendarDays = computed<CalendarDay[]>(() => {
169
+ const days: CalendarDay[] = []
170
+ const firstDay = new Date(currentYear.value, currentMonth.value, 1)
171
+ const lastDay = new Date(currentYear.value, currentMonth.value + 1, 0)
172
+
173
+ let startDay = firstDay.getDay() - props.firstDayOfWeek
174
+ if (startDay < 0) startDay += 7
175
+
176
+ const prevMonthDays = new Date(currentYear.value, currentMonth.value, 0).getDate()
177
+
178
+ // Previous month days
179
+ for (let i = startDay - 1; i >= 0; i--) {
180
+ const date = new Date(currentYear.value, currentMonth.value - 1, prevMonthDays - i)
181
+ days.push(createCalendarDay(date, false))
182
+ }
183
+
184
+ // Current month days
185
+ for (let i = 1; i <= lastDay.getDate(); i++) {
186
+ const date = new Date(currentYear.value, currentMonth.value, i)
187
+ days.push(createCalendarDay(date, true))
188
+ }
189
+
190
+ // Next month days
191
+ const remainingDays = 42 - days.length
192
+ for (let i = 1; i <= remainingDays; i++) {
193
+ const date = new Date(currentYear.value, currentMonth.value + 1, i)
194
+ days.push(createCalendarDay(date, false))
195
+ }
196
+
197
+ return days
198
+ })
199
+
200
+ const createCalendarDay = (date: Date, isCurrentMonth: boolean): CalendarDay => {
201
+ const dateString = formatDate(date)
202
+ const today = formatDate(new Date())
203
+
204
+ return {
205
+ date,
206
+ dateString,
207
+ isCurrentMonth,
208
+ isToday: dateString === today,
209
+ isSelected: isDateSelected(dateString),
210
+ isDisabled: isDateDisabled(dateString),
211
+ isInRange: isDateInRange(dateString)
212
+ }
213
+ }
214
+
215
+ const formatDate = (date: Date): string => {
216
+ const year = date.getFullYear()
217
+ const month = String(date.getMonth() + 1).padStart(2, '0')
218
+ const day = String(date.getDate()).padStart(2, '0')
219
+ return `${year}-${month}-${day}`
220
+ }
221
+
222
+ const isDateSelected = (dateString: string): boolean => {
223
+ if (!props.modelValue) return false
224
+
225
+ if (props.mode === 'single') {
226
+ return props.modelValue === dateString
227
+ }
228
+
229
+ if (props.mode === 'multiple' && Array.isArray(props.modelValue)) {
230
+ return props.modelValue.includes(dateString)
231
+ }
232
+
233
+ if (props.mode === 'range' && typeof props.modelValue === 'object' && 'start' in props.modelValue) {
234
+ return dateString === props.modelValue.start || dateString === props.modelValue.end
235
+ }
236
+
237
+ return false
238
+ }
239
+
240
+ const isDateDisabled = (dateString: string): boolean => {
241
+ if (props.minDate && dateString < props.minDate) return true
242
+ if (props.maxDate && dateString > props.maxDate) return true
243
+ if (props.disabledDates?.includes(dateString)) return true
244
+ return false
245
+ }
246
+
247
+ const isDateInRange = (dateString: string): boolean => {
248
+ if (props.mode !== 'range') return false
249
+ if (typeof props.modelValue !== 'object' || !('start' in props.modelValue)) return false
250
+
251
+ const { start, end } = props.modelValue
252
+ if (!start || !end) return false
253
+
254
+ return dateString > start && dateString < end
255
+ }
256
+
257
+ const selectDate = (day: CalendarDay) => {
258
+ if (day.isDisabled) return
259
+
260
+ if (props.mode === 'single') {
261
+ emit('update:modelValue', day.dateString)
262
+ } else if (props.mode === 'multiple') {
263
+ const current = Array.isArray(props.modelValue) ? [...props.modelValue] : []
264
+ const index = current.indexOf(day.dateString)
265
+
266
+ if (index > -1) {
267
+ current.splice(index, 1)
268
+ } else {
269
+ current.push(day.dateString)
270
+ }
271
+
272
+ emit('update:modelValue', current)
273
+ } else if (props.mode === 'range') {
274
+ if (!rangeStart.value) {
275
+ rangeStart.value = day.dateString
276
+ emit('update:modelValue', { start: day.dateString, end: day.dateString })
277
+ } else {
278
+ const start = rangeStart.value < day.dateString ? rangeStart.value : day.dateString
279
+ const end = rangeStart.value < day.dateString ? day.dateString : rangeStart.value
280
+ emit('update:modelValue', { start, end })
281
+ rangeStart.value = null
282
+ }
283
+ }
284
+ }
285
+
286
+ const previousMonth = () => {
287
+ if (currentMonth.value === 0) {
288
+ currentMonth.value = 11
289
+ currentYear.value--
290
+ } else {
291
+ currentMonth.value--
292
+ }
293
+ }
294
+
295
+ const nextMonth = () => {
296
+ if (currentMonth.value === 11) {
297
+ currentMonth.value = 0
298
+ currentYear.value++
299
+ } else {
300
+ currentMonth.value++
301
+ }
302
+ }
303
+
304
+ const showMonthPicker = () => {
305
+ pickerMode.value = pickerMode.value === 'month' ? null : 'month'
306
+ }
307
+
308
+ const showYearPicker = () => {
309
+ if (pickerMode.value !== 'year') {
310
+ // Center the year page around the current year when opening
311
+ yearPageStart.value = Math.floor(currentYear.value / 12) * 12
312
+ }
313
+ pickerMode.value = pickerMode.value === 'year' ? null : 'year'
314
+ }
315
+
316
+ const selectMonth = (month: number) => {
317
+ currentMonth.value = month
318
+ pickerMode.value = null
319
+ }
320
+
321
+ const selectYear = (year: number) => {
322
+ currentYear.value = year
323
+ pickerMode.value = null
324
+ }
325
+
326
+ const previousYearPage = () => {
327
+ yearPageStart.value -= 12
328
+ }
329
+
330
+ const nextYearPage = () => {
331
+ yearPageStart.value += 12
332
+ }
333
+ </script>
334
+
335
+ <style scoped>
336
+ .calendar {
337
+ background: white;
338
+ border: 1px solid #e5e7eb;
339
+ border-radius: 0.75rem;
340
+ padding: 1rem;
341
+ width: 100%;
342
+ max-width: 320px;
343
+ user-select: none;
344
+ }
345
+
346
+ .calendar-header {
347
+ display: flex;
348
+ justify-content: space-between;
349
+ align-items: center;
350
+ margin-bottom: 1rem;
351
+ padding-bottom: 0.75rem;
352
+ border-bottom: 1px solid #f3f4f6;
353
+ }
354
+
355
+ .calendar-title {
356
+ display: flex;
357
+ gap: 0.5rem;
358
+ align-items: center;
359
+ }
360
+
361
+ .calendar-month-btn,
362
+ .calendar-year-btn {
363
+ background: none;
364
+ border: none;
365
+ font-size: 1rem;
366
+ font-weight: 600;
367
+ color: #1f2937;
368
+ cursor: pointer;
369
+ padding: 0.25rem 0.5rem;
370
+ border-radius: 0.375rem;
371
+ transition: all 0.2s;
372
+ }
373
+
374
+ .calendar-month-btn:hover,
375
+ .calendar-year-btn:hover {
376
+ background: #f3f4f6;
377
+ }
378
+
379
+ .calendar-nav {
380
+ background: none;
381
+ border: none;
382
+ font-size: 1.5rem;
383
+ color: #6b7280;
384
+ cursor: pointer;
385
+ width: 2rem;
386
+ height: 2rem;
387
+ display: flex;
388
+ align-items: center;
389
+ justify-content: center;
390
+ border-radius: 0.375rem;
391
+ transition: all 0.2s;
392
+ }
393
+
394
+ .calendar-nav:hover {
395
+ background: #f3f4f6;
396
+ color: #1f2937;
397
+ }
398
+
399
+ .calendar-body {
400
+ display: flex;
401
+ flex-direction: column;
402
+ }
403
+
404
+ .calendar-weekdays {
405
+ display: grid;
406
+ grid-template-columns: repeat(7, 1fr);
407
+ gap: 0.25rem;
408
+ margin-bottom: 0.5rem;
409
+ }
410
+
411
+ .calendar-weekday {
412
+ text-align: center;
413
+ font-size: 0.75rem;
414
+ font-weight: 600;
415
+ color: #6b7280;
416
+ padding: 0.5rem 0;
417
+ }
418
+
419
+ .calendar-days {
420
+ display: grid;
421
+ grid-template-columns: repeat(7, 1fr);
422
+ gap: 0.25rem;
423
+ }
424
+
425
+ .calendar-day {
426
+ aspect-ratio: 1;
427
+ border: none;
428
+ background: none;
429
+ font-size: 0.875rem;
430
+ color: #1f2937;
431
+ cursor: pointer;
432
+ border-radius: 0.375rem;
433
+ transition: all 0.2s;
434
+ display: flex;
435
+ align-items: center;
436
+ justify-content: center;
437
+ }
438
+
439
+ .calendar-day:hover:not(:disabled) {
440
+ background: #f3f4f6;
441
+ }
442
+
443
+ .calendar-day--other-month {
444
+ color: #d1d5db;
445
+ }
446
+
447
+ .calendar-day--today {
448
+ font-weight: 700;
449
+ color: #667eea;
450
+ }
451
+
452
+ .calendar-day--selected {
453
+ background: #667eea;
454
+ color: white;
455
+ font-weight: 600;
456
+ }
457
+
458
+ .calendar-day--selected:hover {
459
+ background: #5568d3;
460
+ }
461
+
462
+ .calendar-day--in-range {
463
+ background: #eef2ff;
464
+ color: #667eea;
465
+ }
466
+
467
+ .calendar-day--disabled {
468
+ color: #d1d5db;
469
+ cursor: not-allowed;
470
+ }
471
+
472
+ .calendar-day--disabled:hover {
473
+ background: none;
474
+ }
475
+
476
+ .calendar-picker {
477
+ display: flex;
478
+ flex-direction: column;
479
+ gap: 0.5rem;
480
+ }
481
+
482
+ .calendar-picker-nav {
483
+ display: flex;
484
+ justify-content: space-between;
485
+ align-items: center;
486
+ padding: 0.5rem 0;
487
+ margin-bottom: 0.5rem;
488
+ border-bottom: 1px solid #f3f4f6;
489
+ }
490
+
491
+ .calendar-picker-range {
492
+ font-size: 0.875rem;
493
+ font-weight: 600;
494
+ color: #1f2937;
495
+ }
496
+
497
+ .calendar-picker-grid {
498
+ display: grid;
499
+ grid-template-columns: repeat(3, 1fr);
500
+ gap: 0.5rem;
501
+ }
502
+
503
+ .calendar-picker-item {
504
+ background: none;
505
+ border: 1px solid #e5e7eb;
506
+ padding: 0.75rem;
507
+ border-radius: 0.375rem;
508
+ font-size: 0.875rem;
509
+ color: #1f2937;
510
+ cursor: pointer;
511
+ transition: all 0.2s;
512
+ }
513
+
514
+ .calendar-picker-item:hover {
515
+ background: #f3f4f6;
516
+ border-color: #667eea;
517
+ }
518
+
519
+ .calendar-picker-item--selected {
520
+ background: #667eea;
521
+ color: white;
522
+ border-color: #667eea;
523
+ }
524
+
525
+ .calendar-picker-item--selected:hover {
526
+ background: #5568d3;
527
+ }
528
+ </style>
@@ -0,0 +1,20 @@
1
+ export interface CalendarProps {
2
+ modelValue?: string | string[] | { start: string; end: string }
3
+ mode?: 'single' | 'multiple' | 'range'
4
+ minDate?: string
5
+ maxDate?: string
6
+ disabledDates?: string[]
7
+ locale?: string
8
+ firstDayOfWeek?: 0 | 1 | 2 | 3 | 4 | 5 | 6
9
+ initialDate?: string
10
+ }
11
+
12
+ export interface CalendarDay {
13
+ date: Date
14
+ dateString: string
15
+ isCurrentMonth: boolean
16
+ isToday: boolean
17
+ isSelected: boolean
18
+ isDisabled: boolean
19
+ isInRange: boolean
20
+ }
@@ -0,0 +1,88 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import Card from './Card.vue'
3
+
4
+ const meta = {
5
+ title: 'Components/Card',
6
+ component: Card,
7
+ tags: ['autodocs'],
8
+ argTypes: {
9
+ hoverable: {
10
+ control: 'boolean',
11
+ description: 'Enable hover effect'
12
+ }
13
+ },
14
+ args: {
15
+ hoverable: false
16
+ }
17
+ } satisfies Meta<typeof Card>
18
+
19
+ export default meta
20
+ type Story = StoryObj<typeof meta>
21
+
22
+ export const Default: Story = {
23
+ render: (args: any) => ({
24
+ components: { Card },
25
+ setup() {
26
+ return { args }
27
+ },
28
+ template: `
29
+ <Card v-bind="args">
30
+ <p>This is the card body content. You can put any content here.</p>
31
+ </Card>
32
+ `
33
+ })
34
+ }
35
+
36
+ export const WithHeader: Story = {
37
+ render: (args: any) => ({
38
+ components: { Card },
39
+ setup() {
40
+ return { args }
41
+ },
42
+ template: `
43
+ <Card v-bind="args">
44
+ <template #header>Card Header</template>
45
+ <p>This is the card body content with a header.</p>
46
+ </Card>
47
+ `
48
+ })
49
+ }
50
+
51
+ export const WithHeaderAndFooter: Story = {
52
+ render: (args: any) => ({
53
+ components: { Card },
54
+ setup() {
55
+ return { args }
56
+ },
57
+ template: `
58
+ <Card v-bind="args">
59
+ <template #header>Card Title</template>
60
+ <p>This card has both a header and a footer section.</p>
61
+ <template #footer>
62
+ <div style="display: flex; gap: 0.5rem;">
63
+ <button>Cancel</button>
64
+ <button>Save</button>
65
+ </div>
66
+ </template>
67
+ </Card>
68
+ `
69
+ })
70
+ }
71
+
72
+ export const Hoverable: Story = {
73
+ args: {
74
+ hoverable: true
75
+ },
76
+ render: (args: any) => ({
77
+ components: { Card },
78
+ setup() {
79
+ return { args }
80
+ },
81
+ template: `
82
+ <Card v-bind="args">
83
+ <template #header>Hoverable Card</template>
84
+ <p>Hover over this card to see the effect!</p>
85
+ </Card>
86
+ `
87
+ })
88
+ }