cisse-vue-ui 0.8.0 → 0.8.1

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 (47) hide show
  1. package/README.md +879 -879
  2. package/dist/{CheckboxGroup.vue_vue_type_script_setup_true_lang-hHJdvUYS.js → CheckboxGroup.vue_vue_type_script_setup_true_lang-BPALlche.js} +3 -3
  3. package/dist/CheckboxGroup.vue_vue_type_script_setup_true_lang-BPALlche.js.map +1 -0
  4. package/dist/{CheckboxGroup.vue_vue_type_script_setup_true_lang-DJbuHoDj.cjs → CheckboxGroup.vue_vue_type_script_setup_true_lang-CYnPpiRd.cjs} +3 -3
  5. package/dist/CheckboxGroup.vue_vue_type_script_setup_true_lang-CYnPpiRd.cjs.map +1 -0
  6. package/dist/{ConfirmDialog.vue_vue_type_script_setup_true_lang-Bine-xfp.cjs → ConfirmDialog.vue_vue_type_script_setup_true_lang-BGUoa5fT.cjs} +58 -9
  7. package/dist/ConfirmDialog.vue_vue_type_script_setup_true_lang-BGUoa5fT.cjs.map +1 -0
  8. package/dist/{ConfirmDialog.vue_vue_type_script_setup_true_lang-DqkA1Zr-.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-DWs2V7xX.js} +58 -9
  9. package/dist/ConfirmDialog.vue_vue_type_script_setup_true_lang-DWs2V7xX.js.map +1 -0
  10. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-56CxoSmj.js.map +1 -1
  11. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-Dd3ySRNB.cjs.map +1 -1
  12. package/dist/FilterTabs.vue_vue_type_script_setup_true_lang-Bj3I5Sn7.cjs.map +1 -1
  13. package/dist/FilterTabs.vue_vue_type_script_setup_true_lang-DzLwUVCW.js.map +1 -1
  14. package/dist/ListSkeleton.vue_vue_type_script_setup_true_lang-BwtEbaiT.js.map +1 -1
  15. package/dist/ListSkeleton.vue_vue_type_script_setup_true_lang-DtwwmfWr.cjs.map +1 -1
  16. package/dist/PageHero.vue_vue_type_script_setup_true_lang-Bi97ypMD.cjs.map +1 -1
  17. package/dist/PageHero.vue_vue_type_script_setup_true_lang-DQQGYAw0.js.map +1 -1
  18. package/dist/cisse-vue-ui.css +57 -57
  19. package/dist/components/feedback/PaginationControls.stories.d.ts +5 -0
  20. package/dist/components/feedback/PaginationControls.vue.d.ts +2 -0
  21. package/dist/components/feedback/index.cjs +1 -1
  22. package/dist/components/feedback/index.js +1 -1
  23. package/dist/components/form/index.cjs +1 -1
  24. package/dist/components/form/index.js +1 -1
  25. package/dist/components/index.cjs +2 -2
  26. package/dist/components/index.js +2 -2
  27. package/dist/{index-rBD1MYh-.js → index-CE90-_mh.js} +3 -3
  28. package/dist/index-CE90-_mh.js.map +1 -0
  29. package/dist/{index-DwqCXgDx.cjs → index-QKwLtDJE.cjs} +3 -3
  30. package/dist/{index-DwqCXgDx.cjs.map → index-QKwLtDJE.cjs.map} +1 -1
  31. package/dist/index.cjs +3 -3
  32. package/dist/index.js +3 -3
  33. package/dist/style.css +1 -1
  34. package/dist/useDropdown-DHFnd259.cjs.map +1 -1
  35. package/dist/useDropdown-iVu14E6s.js.map +1 -1
  36. package/dist/useFocusTrap-AnlJsihM.js.map +1 -1
  37. package/dist/useFocusTrap-kcxO8AeU.cjs.map +1 -1
  38. package/dist/useId-nxrBaIC9.cjs.map +1 -1
  39. package/dist/useId-xeHj7rkg.js.map +1 -1
  40. package/dist/useToast-Bk60GArg.cjs.map +1 -1
  41. package/dist/useToast-ina5g3mj.js.map +1 -1
  42. package/package.json +169 -169
  43. package/dist/CheckboxGroup.vue_vue_type_script_setup_true_lang-DJbuHoDj.cjs.map +0 -1
  44. package/dist/CheckboxGroup.vue_vue_type_script_setup_true_lang-hHJdvUYS.js.map +0 -1
  45. package/dist/ConfirmDialog.vue_vue_type_script_setup_true_lang-Bine-xfp.cjs.map +0 -1
  46. package/dist/ConfirmDialog.vue_vue_type_script_setup_true_lang-DqkA1Zr-.js.map +0 -1
  47. package/dist/index-rBD1MYh-.js.map +0 -1
package/README.md CHANGED
@@ -1,879 +1,879 @@
1
- # cisse-vue-ui
2
-
3
- A Vue 3 component library built with TypeScript and Tailwind CSS v4.
4
-
5
- **[View Storybook Documentation](https://moulayecisse.github.io/cisse-vue-ui/)**
6
-
7
- ## Installation
8
-
9
- ```bash
10
- npm install cisse-vue-ui
11
- # or
12
- bun add cisse-vue-ui
13
- ```
14
-
15
- ### Peer Dependencies
16
-
17
- ```bash
18
- npm install vue@^3.4 tailwindcss@^4 @iconify/vue@^4
19
- ```
20
-
21
- ## Setup
22
-
23
- ### 1. Import Styles
24
-
25
- Add the pre-compiled CSS to your main CSS file:
26
-
27
- ```css
28
- @import 'cisse-vue-ui/style.css';
29
- @import 'tailwindcss';
30
- ```
31
-
32
- ### 2. Configure Primary Color (Optional)
33
-
34
- Override the default primary color in your CSS:
35
-
36
- ```css
37
- @theme {
38
- --color-primary-50: oklch(97% 0.02 142);
39
- --color-primary-100: oklch(94% 0.05 142);
40
- --color-primary-200: oklch(88% 0.10 142);
41
- --color-primary-300: oklch(78% 0.15 142);
42
- --color-primary-400: oklch(65% 0.20 142);
43
- --color-primary-500: oklch(55% 0.22 142);
44
- --color-primary-600: oklch(48% 0.20 142);
45
- --color-primary-700: oklch(40% 0.17 142);
46
- --color-primary-800: oklch(32% 0.14 142);
47
- --color-primary-900: oklch(25% 0.10 142);
48
- --color-primary-950: oklch(18% 0.08 142);
49
- }
50
- ```
51
-
52
- ## Usage
53
-
54
- ### Tree-Shaken Imports (Recommended)
55
-
56
- ```vue
57
- <script setup lang="ts">
58
- import { Button, CardComponent, FormInput } from 'cisse-vue-ui'
59
- </script>
60
- ```
61
-
62
- ### Category Imports
63
-
64
- ```typescript
65
- import { Button, Tabs, TabPanel } from 'cisse-vue-ui/components/core'
66
- import { FormInput, FormSelect, Switch } from 'cisse-vue-ui/components/form'
67
- import { Modal, Alert, LoadingSpinner } from 'cisse-vue-ui/components/feedback'
68
- import { BaseLayout, PageLayout } from 'cisse-vue-ui/components/layout'
69
- ```
70
-
71
- ### Global Registration (Vue Plugin)
72
-
73
- ```typescript
74
- import { createApp } from 'vue'
75
- import { VueTailwindUI } from 'cisse-vue-ui'
76
-
77
- const app = createApp(App)
78
-
79
- // Register all components
80
- app.use(VueTailwindUI)
81
-
82
- // Or with a prefix
83
- app.use(VueTailwindUI, { prefix: 'Ui' }) // <UiButton>, <UiCard>, etc.
84
-
85
- // Or specific components only
86
- app.use(VueTailwindUI, { components: ['Button', 'CardComponent'] })
87
- ```
88
-
89
- ## Components
90
-
91
- ### Core
92
-
93
- | Component | Description |
94
- |-----------|-------------|
95
- | `Button` | Button with variants (primary, secondary, outline, ghost, danger, success), sizes, icons, loading state |
96
- | `CardComponent` | Card container with header, content, and footer slots |
97
- | `TableComponent` | Data table with sorting, selection, actions, and custom column rendering |
98
- | `MobileList` | Mobile-optimized card-based list with selection support |
99
- | `ResponsiveList` | Combines MobileList (mobile) and TableComponent (desktop) with automatic breakpoint switching |
100
- | `Tabs` | Tab navigation with variants (underline, pills, boxed) |
101
- | `TabPanel` | Tab content panel (use with Tabs) |
102
- | `Dropdown` | Dropdown menu with items, icons, and dividers |
103
- | `Avatar` | User avatar with image, initials, or icon fallback |
104
- | `AutocompleteComponent` | Searchable select with keyboard navigation |
105
- | `MenuItem` | Navigation menu item with icon, active state detection, and route support |
106
- | `StatusBadge` | Colored status indicator badge |
107
- | `TableAction` | Icon button for table row actions |
108
- | `Stepper` | Multi-step progress indicator with horizontal/vertical orientation |
109
- | `CollapsibleCard` | Card that can expand/collapse its content |
110
- | `Accordion` | Expandable content sections with single/multiple mode |
111
- | `Breadcrumb` | Navigation breadcrumb trail |
112
- | `Drawer` | Slide-out panel from any edge (left, right, top, bottom) |
113
- | `Popover` | Floating content panel triggered by click or hover |
114
- | `Timeline` | Vertical timeline for events/history display |
115
- | `Tooltip` | Hover tooltip with customizable position |
116
-
117
- ### Form
118
-
119
- | Component | Description |
120
- |-----------|-------------|
121
- | `FormInput` | Text input with validation states and ARIA support |
122
- | `FormSelect` | Select dropdown with search, multi-select, and validation |
123
- | `FormGroup` | Form field wrapper with label, help text, and error states |
124
- | `FormLabel` | Styled form label with required indicator |
125
- | `FormHelp` | Help/error text for form fields |
126
- | `SearchInput` | Search input with icon and clear button |
127
- | `Switch` | Toggle switch with label and description |
128
- | `Checkbox` | Checkbox with label, description, and indeterminate state |
129
- | `Combobox` | Multi-select combobox with search and tags |
130
- | `DatePicker` | Calendar date picker with min/max dates |
131
- | `ColorPicker` | Color selection with swatches and custom input |
132
- | `FileUpload` | Drag-and-drop file upload with preview |
133
- | `Rating` | Star rating input with half-star support |
134
- | `Slider` | Single value slider input |
135
- | `RangeSlider` | Dual-handle range slider |
136
-
137
- ### Feedback
138
-
139
- | Component | Description |
140
- |-----------|-------------|
141
- | `Modal` | Modal dialog with focus trap, ARIA support, and slots |
142
- | `ConfirmDialog` | Confirmation modal with customizable actions |
143
- | `Alert` | Alert banner with variants (info, success, warning, error) |
144
- | `Toast` | Individual toast notification with auto-dismiss |
145
- | `ToastContainer` | Toast notification container with positioning |
146
- | `LoadingSpinner` | Loading indicator with size variants |
147
- | `Progress` | Progress bar with percentage display |
148
- | `Skeleton` | Loading placeholder with animation |
149
- | `CardSkeleton` | Card loading skeleton |
150
- | `ListSkeleton` | List loading skeleton |
151
- | `TableSkeleton` | Table loading skeleton |
152
- | `PaginationControls` | Pagination with page numbers and navigation |
153
- | `NotificationList` | Notification list container |
154
- | `NotificationComponent` | Individual notification item |
155
- | `EmptyState` | Placeholder for empty content with icon and action slot |
156
-
157
- ### Layout
158
-
159
- | Component | Description |
160
- |-----------|-------------|
161
- | `AuthLayout` | Split-panel authentication layout with branding and form sections |
162
- | `BaseLayout` | App shell with sidebar, header, main content area, and route-aware menu |
163
- | `PageLayout` | Page wrapper with breadcrumbs |
164
-
165
- ### Type Display
166
-
167
- | Component | Description |
168
- |-----------|-------------|
169
- | `TextType` | Text value display |
170
- | `NumberType` | Formatted number display |
171
- | `DateType` | Formatted date display |
172
- | `BooleanType` | Boolean value display (check/cross icons) |
173
- | `BadgeType` | Badge value display with colors |
174
-
175
- ## Composables
176
-
177
- ```typescript
178
- import {
179
- useNotifications,
180
- useDarkMode,
181
- useExportCSV,
182
- useDropdown,
183
- useModal,
184
- useToast,
185
- useFocusTrap,
186
- useId
187
- } from 'cisse-vue-ui/composables'
188
- ```
189
-
190
- ### useModal
191
-
192
- Manage modal state with data support:
193
-
194
- ```typescript
195
- import { useModal } from 'cisse-vue-ui/composables'
196
-
197
- // Simple modal
198
- const createModal = useModal()
199
- createModal.open()
200
- createModal.close()
201
-
202
- // Modal with data (e.g., for editing)
203
- const editModal = useModal<User>()
204
- editModal.open(selectedUser)
205
- // Access editModal.data.value in template
206
-
207
- // With callbacks
208
- const deleteModal = useModal<Item>({
209
- onOpen: (data) => console.log('Opening with:', data),
210
- onClose: () => refetchData()
211
- })
212
- ```
213
-
214
- ```vue
215
- <template>
216
- <!-- Use isOpen for v-model binding -->
217
- <Modal v-model="editModal.isOpen.value" title="Edit User">
218
- <FormInput v-model="editModal.data.value.name" label="Name" />
219
- <template #footer>
220
- <Button @click="editModal.close()">Cancel</Button>
221
- <Button variant="primary" @click="save">Save</Button>
222
- </template>
223
- </Modal>
224
- </template>
225
- ```
226
-
227
- ### useDropdown
228
-
229
- Shared dropdown logic for custom dropdown components (used internally by Dropdown, FormSelect, AutocompleteComponent):
230
-
231
- ```typescript
232
- import { useDropdown } from 'cisse-vue-ui/composables'
233
- import { ref } from 'vue'
234
-
235
- const triggerRef = ref<HTMLElement>()
236
- const dropdownRef = ref<HTMLElement>()
237
-
238
- const {
239
- isOpen,
240
- highlightedIndex,
241
- dropdownStyle,
242
- open,
243
- close,
244
- toggle,
245
- handleKeydown,
246
- scrollToHighlighted,
247
- } = useDropdown(triggerRef, dropdownRef, {
248
- teleport: true,
249
- align: 'left',
250
- gap: 8,
251
- onOpen: () => console.log('Opened'),
252
- onClose: () => console.log('Closed'),
253
- })
254
- ```
255
-
256
- ### useNotifications
257
-
258
- ```typescript
259
- const { notifications, addNotification, removeNotification } = useNotifications()
260
-
261
- addNotification({
262
- type: 'success',
263
- title: 'Saved',
264
- message: 'Your changes have been saved.'
265
- })
266
- ```
267
-
268
- ### useDarkMode
269
-
270
- ```typescript
271
- const { isDark, toggle, enable, disable } = useDarkMode({
272
- selector: 'html', // Element to add .dark class
273
- storageKey: 'theme', // localStorage key
274
- defaultDark: false // Default state
275
- })
276
- ```
277
-
278
- ### useExportCSV
279
-
280
- ```typescript
281
- const { exportToCSV } = useExportCSV()
282
-
283
- exportToCSV(data, columns, 'export.csv')
284
- ```
285
-
286
- ### useToast
287
-
288
- Toast notification system with positioning and auto-dismiss:
289
-
290
- ```typescript
291
- import { useToast } from 'cisse-vue-ui/composables'
292
-
293
- const { toasts, addToast, removeToast, clearToasts } = useToast()
294
-
295
- // Add a toast
296
- addToast({
297
- type: 'success',
298
- title: 'Success!',
299
- message: 'Your changes have been saved.',
300
- duration: 5000 // auto-dismiss after 5s
301
- })
302
-
303
- // Different toast types
304
- addToast({ type: 'error', title: 'Error', message: 'Something went wrong' })
305
- addToast({ type: 'warning', title: 'Warning', message: 'Please review' })
306
- addToast({ type: 'info', title: 'Info', message: 'New update available' })
307
- ```
308
-
309
- ```vue
310
- <template>
311
- <!-- Add ToastContainer to your app root -->
312
- <ToastContainer position="top-right" />
313
- </template>
314
- ```
315
-
316
- ### useFocusTrap
317
-
318
- Trap focus within a container (used internally by Modal):
319
-
320
- ```typescript
321
- import { useFocusTrap } from 'cisse-vue-ui/composables'
322
- import { ref } from 'vue'
323
-
324
- const isActive = ref(true)
325
- const { containerRef } = useFocusTrap({
326
- active: isActive,
327
- focusFirst: true, // Focus first focusable element on activate
328
- restoreFocus: true // Restore focus on deactivate
329
- })
330
- ```
331
-
332
- ### useId
333
-
334
- Generate unique IDs for accessibility (ARIA relationships):
335
-
336
- ```typescript
337
- import { useId } from 'cisse-vue-ui/composables'
338
-
339
- const { id, related } = useId({ prefix: 'modal' })
340
- // id.value = 'cisse-modal-1'
341
- // related('title') = 'cisse-modal-1-title'
342
- // related('description') = 'cisse-modal-1-description'
343
- ```
344
-
345
- ```vue
346
- <template>
347
- <div :id="id" role="dialog" :aria-labelledby="related('title')">
348
- <h2 :id="related('title')">Dialog Title</h2>
349
- </div>
350
- </template>
351
- ```
352
-
353
- ## Types
354
-
355
- ```typescript
356
- import type { Property, Notification, Breadcrumb } from 'cisse-vue-ui/types'
357
-
358
- // Table column definition
359
- const columns: Property[] = [
360
- { key: 'name', label: 'Name', sortable: true },
361
- { key: 'email', label: 'Email' },
362
- { key: 'status', label: 'Status', type: 'badge' }
363
- ]
364
-
365
- // Breadcrumb navigation
366
- const breadcrumbs: Breadcrumb[] = [
367
- { label: 'Home', to: '/' },
368
- { label: 'Users', to: '/users' },
369
- { label: 'Edit' }
370
- ]
371
- ```
372
-
373
- ## Component Examples
374
-
375
- ### Button
376
-
377
- ```vue
378
- <Button variant="primary" size="md" :loading="isLoading">
379
- Save Changes
380
- </Button>
381
-
382
- <Button variant="outline" icon="lucide:plus">
383
- Add Item
384
- </Button>
385
-
386
- <Button variant="danger" icon="lucide:trash">
387
- Delete
388
- </Button>
389
- ```
390
-
391
- ### Tabs
392
-
393
- ```vue
394
- <script setup>
395
- import { ref } from 'vue'
396
- import { Tabs, TabPanel } from 'cisse-vue-ui'
397
-
398
- const activeTab = ref('profile')
399
- const tabs = [
400
- { key: 'profile', label: 'Profile' },
401
- { key: 'settings', label: 'Settings' },
402
- { key: 'notifications', label: 'Notifications' }
403
- ]
404
- </script>
405
-
406
- <template>
407
- <Tabs v-model="activeTab" :tabs="tabs" variant="underline">
408
- <TabPanel value="profile">Profile content</TabPanel>
409
- <TabPanel value="settings">Settings content</TabPanel>
410
- <TabPanel value="notifications">Notifications content</TabPanel>
411
- </Tabs>
412
- </template>
413
- ```
414
-
415
- ### Switch
416
-
417
- ```vue
418
- <Switch
419
- v-model="emailNotifications"
420
- label="Email notifications"
421
- description="Receive email updates about your account"
422
- />
423
- ```
424
-
425
- ### Alert
426
-
427
- ```vue
428
- <Alert variant="success" title="Success!" dismissible>
429
- Your changes have been saved successfully.
430
- </Alert>
431
-
432
- <Alert variant="error" title="Error">
433
- Something went wrong. Please try again.
434
- </Alert>
435
- ```
436
-
437
- ### Dropdown
438
-
439
- ```vue
440
- <script setup>
441
- import { Dropdown } from 'cisse-vue-ui'
442
-
443
- const items = [
444
- { key: 'edit', label: 'Edit', icon: 'lucide:edit' },
445
- { key: 'duplicate', label: 'Duplicate', icon: 'lucide:copy' },
446
- { key: 'divider', divider: true },
447
- { key: 'delete', label: 'Delete', icon: 'lucide:trash', danger: true }
448
- ]
449
-
450
- const handleSelect = (item) => {
451
- console.log('Selected:', item.key)
452
- }
453
- </script>
454
-
455
- <template>
456
- <Dropdown :items="items" @select="handleSelect">
457
- <template #trigger-label>Actions</template>
458
- </Dropdown>
459
- </template>
460
- ```
461
-
462
- ### Stepper
463
-
464
- ```vue
465
- <script setup>
466
- import { ref } from 'vue'
467
- import { Stepper } from 'cisse-vue-ui'
468
-
469
- const currentStep = ref('step2')
470
- const steps = [
471
- { key: 'step1', title: 'Account', description: 'Create account', icon: 'lucide:user' },
472
- { key: 'step2', title: 'Profile', description: 'Set up profile', icon: 'lucide:settings' },
473
- { key: 'step3', title: 'Complete', description: 'Ready to go!', icon: 'lucide:check' }
474
- ]
475
- </script>
476
-
477
- <template>
478
- <Stepper v-model="currentStep" :steps="steps" />
479
- </template>
480
- ```
481
-
482
- ### EmptyState
483
-
484
- ```vue
485
- <EmptyState
486
- title="No results found"
487
- message="Try adjusting your search or filters"
488
- icon="lucide:search-x"
489
- >
490
- <template #action>
491
- <Button variant="primary" size="sm">Clear filters</Button>
492
- </template>
493
- </EmptyState>
494
- ```
495
-
496
- ### Checkbox
497
-
498
- ```vue
499
- <Checkbox
500
- v-model="accepted"
501
- label="Accept terms"
502
- description="I agree to the terms and conditions"
503
- />
504
- ```
505
-
506
- ### TableComponent
507
-
508
- ```vue
509
- <script setup>
510
- import { ref } from 'vue'
511
- import { TableComponent } from 'cisse-vue-ui'
512
-
513
- const properties = [
514
- { name: 'name', label: 'Name', main: true },
515
- { name: 'email', label: 'Email' },
516
- { name: 'role', label: 'Role', type: 'badge' }
517
- ]
518
-
519
- const items = [
520
- { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
521
- { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' }
522
- ]
523
-
524
- // Selection support
525
- const selectedItems = ref(new Set())
526
- const toggleSelect = (id) => {
527
- if (selectedItems.value.has(id)) {
528
- selectedItems.value.delete(id)
529
- } else {
530
- selectedItems.value.add(id)
531
- }
532
- }
533
- </script>
534
-
535
- <template>
536
- <TableComponent
537
- :properties="properties"
538
- :items="items"
539
- selectable
540
- :selected-items="selectedItems"
541
- @select="toggleSelect"
542
- @select-all="toggleSelectAll"
543
- >
544
- <template #action="{ item }">
545
- <TableAction icon="lucide:edit" @click="edit(item)" />
546
- <TableAction icon="lucide:trash" variant="danger" @click="delete(item)" />
547
- </template>
548
- </TableComponent>
549
- </template>
550
- ```
551
-
552
- ### ResponsiveList
553
-
554
- A component that automatically switches between a mobile card layout and a desktop table layout based on screen size.
555
-
556
- ```vue
557
- <script setup>
558
- import { ref } from 'vue'
559
- import { ResponsiveList } from 'cisse-vue-ui'
560
-
561
- const columns = [
562
- { key: 'name', label: 'Name' },
563
- { key: 'email', label: 'Email' },
564
- { key: 'status', label: 'Status' }
565
- ]
566
-
567
- const items = [
568
- { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
569
- { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' }
570
- ]
571
-
572
- const selectedItems = ref(new Set())
573
-
574
- const toggleSelect = (id) => {
575
- if (selectedItems.value.has(id)) {
576
- selectedItems.value.delete(id)
577
- } else {
578
- selectedItems.value.add(id)
579
- }
580
- }
581
-
582
- const toggleSelectAll = () => {
583
- if (selectedItems.value.size === items.length) {
584
- selectedItems.value.clear()
585
- } else {
586
- items.forEach(item => selectedItems.value.add(String(item.id)))
587
- }
588
- }
589
- </script>
590
-
591
- <template>
592
- <ResponsiveList
593
- :items="items"
594
- :columns="columns"
595
- key-field="id"
596
- selectable
597
- :selected-items="selectedItems"
598
- breakpoint="lg"
599
- @select="toggleSelect"
600
- @select-all="toggleSelectAll"
601
- >
602
- <!-- Mobile view: avatar -->
603
- <template #avatar="{ item }">
604
- <div class="w-10 h-10 rounded-full bg-primary-500 flex items-center justify-center text-white">
605
- {{ item.name[0] }}
606
- </div>
607
- </template>
608
-
609
- <!-- Mobile view: content -->
610
- <template #mobileContent="{ item }">
611
- <h3 class="font-semibold">{{ item.name }}</h3>
612
- <p class="text-sm text-gray-500">{{ item.email }}</p>
613
- </template>
614
-
615
- <!-- Mobile view: actions -->
616
- <template #mobileActions="{ item }">
617
- <button @click="viewItem(item)">View</button>
618
- </template>
619
-
620
- <!-- Desktop table: custom cell rendering -->
621
- <template #cell-name="{ item }">
622
- <span class="font-medium">{{ item.name }}</span>
623
- </template>
624
-
625
- <template #cell-status="{ item }">
626
- <span :class="item.status === 'active' ? 'text-green-600' : 'text-red-600'">
627
- {{ item.status }}
628
- </span>
629
- </template>
630
-
631
- <!-- Desktop table: actions column -->
632
- <template #actions="{ item }">
633
- <Button size="sm" variant="ghost" @click="edit(item)">Edit</Button>
634
- </template>
635
-
636
- <!-- Empty state -->
637
- <template #empty>
638
- <EmptyState title="No items" message="No items to display" />
639
- </template>
640
- </ResponsiveList>
641
- </template>
642
- ```
643
-
644
- #### ResponsiveList Props
645
-
646
- | Prop | Type | Default | Description |
647
- |------|------|---------|-------------|
648
- | `items` | `Array` | required | Array of items to display |
649
- | `columns` | `Array` | required | Column definitions with `key` or `name`, `label`, and optional `type` |
650
- | `keyField` | `string` | `'id'` | Field to use as unique key for items |
651
- | `selectable` | `boolean` | `false` | Enable selection mode |
652
- | `selectedItems` | `Set<string>` | - | Set of selected item keys |
653
- | `selectableFilter` | `Function` | - | Filter function to determine if an item is selectable |
654
- | `breakpoint` | `string` | `'lg'` | Breakpoint for switching views: `'sm'`, `'md'`, `'lg'`, `'xl'`, `'2xl'` |
655
-
656
- ### MobileList
657
-
658
- A mobile-optimized card-based list component with selection support.
659
-
660
- ```vue
661
- <script setup>
662
- import { MobileList } from 'cisse-vue-ui'
663
- </script>
664
-
665
- <template>
666
- <MobileList
667
- :items="items"
668
- key-field="id"
669
- selectable
670
- :selected-items="selectedItems"
671
- @select="toggleSelect"
672
- @select-all="toggleSelectAll"
673
- >
674
- <template #avatar="{ item }">
675
- <div class="w-12 h-12 rounded-full bg-blue-500" />
676
- </template>
677
-
678
- <template #content="{ item }">
679
- <h3>{{ item.name }}</h3>
680
- <p>{{ item.description }}</p>
681
- </template>
682
-
683
- <template #actions="{ item }">
684
- <button>View</button>
685
- </template>
686
- </MobileList>
687
- </template>
688
- ```
689
-
690
- ### MenuItem
691
-
692
- ```vue
693
- <script setup>
694
- import { useRoute } from 'vue-router'
695
- import { MenuItem } from 'cisse-vue-ui'
696
-
697
- const route = useRoute()
698
-
699
- const menuItem = {
700
- label: 'Dashboard',
701
- link: '/dashboard',
702
- icon: 'lucide:layout-dashboard'
703
- }
704
- </script>
705
-
706
- <template>
707
- <!-- Auto-detect active state from current route -->
708
- <MenuItem :menu-item="menuItem" :current-path="route.path" />
709
-
710
- <!-- Or manually control active state -->
711
- <MenuItem :menu-item="menuItem" :active="true" />
712
- </template>
713
- ```
714
-
715
- ### AuthLayout
716
-
717
- Split-panel layout for authentication pages (login, register, password reset).
718
-
719
- ```vue
720
- <script setup>
721
- import { ref } from 'vue'
722
- import { AuthLayout, type AuthFeature } from 'cisse-vue-ui'
723
-
724
- const email = ref('')
725
- const password = ref('')
726
-
727
- const features: AuthFeature[] = [
728
- { icon: 'lucide:shield', text: 'Secure authentication' },
729
- { icon: 'lucide:zap', text: 'Fast login' },
730
- { icon: 'lucide:users', text: 'Team collaboration' },
731
- ]
732
-
733
- function handleSubmit() {
734
- // Handle login
735
- }
736
- </script>
737
-
738
- <template>
739
- <AuthLayout
740
- app-name="My App"
741
- app-icon="lucide:box"
742
- headline="Welcome to"
743
- sub-headline="My Application"
744
- description="Sign in to access your account."
745
- :features="features"
746
- form-title="Sign In"
747
- form-subtitle="Enter your credentials"
748
- home-link="/"
749
- >
750
- <form @submit.prevent="handleSubmit" class="space-y-4">
751
- <input v-model="email" type="email" placeholder="Email" class="..." />
752
- <input v-model="password" type="password" placeholder="Password" class="..." />
753
- <button type="submit">Sign In</button>
754
- </form>
755
-
756
- <template #form-footer>
757
- <p class="text-center mt-6">
758
- Don't have an account? <a href="/register">Sign up</a>
759
- </p>
760
- </template>
761
- </AuthLayout>
762
- </template>
763
- ```
764
-
765
- #### AuthLayout Props
766
-
767
- | Prop | Type | Default | Description |
768
- |------|------|---------|-------------|
769
- | `appName` | `string` | `''` | Application name displayed in logo |
770
- | `appIcon` | `string` | `'lucide:box'` | Iconify icon for app logo |
771
- | `headline` | `string` | `''` | First line of headline |
772
- | `subHeadline` | `string` | `''` | Second line with decorative underline |
773
- | `description` | `string` | `''` | Description paragraph |
774
- | `features` | `AuthFeature[]` | `[]` | List of features with icon and text |
775
- | `gradientFrom` | `string` | `'from-primary-700'` | Tailwind gradient start class |
776
- | `gradientVia` | `string` | `''` | Tailwind gradient middle class |
777
- | `gradientTo` | `string` | `'to-primary-800'` | Tailwind gradient end class |
778
- | `showDecorations` | `boolean` | `true` | Show floating decorative shapes |
779
- | `showPattern` | `boolean` | `true` | Show dot pattern overlay |
780
- | `underlineColor` | `string` | `'rgba(165, 180, 252, 0.5)'` | Sub-headline underline color |
781
- | `formTitle` | `string` | `''` | Form panel title |
782
- | `formSubtitle` | `string` | `''` | Form panel subtitle |
783
- | `homeLink` | `string` | `'/'` | Mobile logo link URL |
784
- | `brandingAnimation` | `string` | `''` | CSS class for branding animation |
785
- | `formAnimation` | `string` | `''` | CSS class for form panel animation |
786
-
787
- #### AuthLayout Slots
788
-
789
- | Slot | Description |
790
- |------|-------------|
791
- | `default` | Form content (inside the white card) |
792
- | `branding-panel` | Complete override of the branding panel |
793
- | `branding-logo` | Custom logo in branding panel |
794
- | `branding-headline` | Custom headline content |
795
- | `branding-features` | Custom features list |
796
- | `branding-content` | Additional content after features |
797
- | `mobile-logo` | Custom mobile logo |
798
- | `form-header` | Content above the form card |
799
- | `form-footer` | Content below the form card |
800
-
801
- ### BaseLayout
802
-
803
- ```vue
804
- <script setup>
805
- import { useRoute } from 'vue-router'
806
- import { BaseLayout } from 'cisse-vue-ui'
807
-
808
- const route = useRoute()
809
-
810
- const menuItems = [
811
- { label: 'Dashboard', link: '/', icon: 'lucide:home' },
812
- { label: 'Users', link: '/users', icon: 'lucide:users' },
813
- { label: 'Settings', link: '/settings', icon: 'lucide:settings' }
814
- ]
815
- </script>
816
-
817
- <template>
818
- <BaseLayout
819
- :menu-items="menuItems"
820
- :current-path="route.path"
821
- :show-dark-toggle="true"
822
- menu-position="top"
823
- >
824
- <template #logo>
825
- <img src="/logo.svg" alt="Logo" class="h-8" />
826
- </template>
827
-
828
- <RouterView />
829
- </BaseLayout>
830
- </template>
831
- ```
832
-
833
- #### BaseLayout Props
834
-
835
- | Prop | Type | Default | Description |
836
- |------|------|---------|-------------|
837
- | `menuItems` | `MenuItemProps[]` | `[]` | Menu items for the sidebar |
838
- | `appName` | `string` | `'App'` | App/brand name displayed in sidebar |
839
- | `appIcon` | `string` | `'lucide:box'` | App icon (Iconify icon name) |
840
- | `sidebarOpen` | `boolean` | `true` | Whether sidebar is open (v-model:sidebarOpen) |
841
- | `dark` | `boolean` | `false` | Whether dark mode is enabled (v-model:dark) |
842
- | `showDarkToggle` | `boolean` | `true` | Show dark mode toggle in header |
843
- | `sidebarClass` | `string` | `'bg-[#172b4c]...'` | CSS classes for sidebar background |
844
- | `currentPath` | `string` | - | Current route path for menu active state |
845
- | `userName` | `string` | - | User display name |
846
- | `userAvatar` | `string` | - | User avatar (initials or image URL) |
847
- | `userMenuItems` | `UserMenuItem[]` | `[]` | User menu dropdown items |
848
- | `menuPosition` | `'top' \| 'center' \| 'bottom'` | `'top'` | Menu vertical position in sidebar |
849
-
850
- #### BaseLayout Slots
851
-
852
- | Slot | Description |
853
- |------|-------------|
854
- | `default` | Main content area (or renders RouterView if available) |
855
- | `logo` | Custom logo in sidebar header |
856
- | `menu` | Custom menu content (receives currentPath) |
857
- | `sidebar-footer` | Content at bottom of sidebar |
858
- | `header-center` | Center content in header |
859
- | `header-actions` | Action buttons in header (before dark toggle) |
860
-
861
- ## Dark Mode
862
-
863
- Components support dark mode via the `.dark` class on a parent element:
864
-
865
- ```html
866
- <html class="dark">
867
- <!-- Components will use dark theme -->
868
- </html>
869
- ```
870
-
871
- Use the `useDarkMode` composable or implement your own toggle:
872
-
873
- ```typescript
874
- const { isDark, toggle } = useDarkMode()
875
- ```
876
-
877
- ## License
878
-
879
- MIT
1
+ # cisse-vue-ui
2
+
3
+ A Vue 3 component library built with TypeScript and Tailwind CSS v4.
4
+
5
+ **[View Storybook Documentation](https://moulayecisse.github.io/cisse-vue-ui/)**
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install cisse-vue-ui
11
+ # or
12
+ bun add cisse-vue-ui
13
+ ```
14
+
15
+ ### Peer Dependencies
16
+
17
+ ```bash
18
+ npm install vue@^3.4 tailwindcss@^4 @iconify/vue@^4
19
+ ```
20
+
21
+ ## Setup
22
+
23
+ ### 1. Import Styles
24
+
25
+ Add the pre-compiled CSS to your main CSS file:
26
+
27
+ ```css
28
+ @import 'cisse-vue-ui/style.css';
29
+ @import 'tailwindcss';
30
+ ```
31
+
32
+ ### 2. Configure Primary Color (Optional)
33
+
34
+ Override the default primary color in your CSS:
35
+
36
+ ```css
37
+ @theme {
38
+ --color-primary-50: oklch(97% 0.02 142);
39
+ --color-primary-100: oklch(94% 0.05 142);
40
+ --color-primary-200: oklch(88% 0.10 142);
41
+ --color-primary-300: oklch(78% 0.15 142);
42
+ --color-primary-400: oklch(65% 0.20 142);
43
+ --color-primary-500: oklch(55% 0.22 142);
44
+ --color-primary-600: oklch(48% 0.20 142);
45
+ --color-primary-700: oklch(40% 0.17 142);
46
+ --color-primary-800: oklch(32% 0.14 142);
47
+ --color-primary-900: oklch(25% 0.10 142);
48
+ --color-primary-950: oklch(18% 0.08 142);
49
+ }
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ### Tree-Shaken Imports (Recommended)
55
+
56
+ ```vue
57
+ <script setup lang="ts">
58
+ import { Button, CardComponent, FormInput } from 'cisse-vue-ui'
59
+ </script>
60
+ ```
61
+
62
+ ### Category Imports
63
+
64
+ ```typescript
65
+ import { Button, Tabs, TabPanel } from 'cisse-vue-ui/components/core'
66
+ import { FormInput, FormSelect, Switch } from 'cisse-vue-ui/components/form'
67
+ import { Modal, Alert, LoadingSpinner } from 'cisse-vue-ui/components/feedback'
68
+ import { BaseLayout, PageLayout } from 'cisse-vue-ui/components/layout'
69
+ ```
70
+
71
+ ### Global Registration (Vue Plugin)
72
+
73
+ ```typescript
74
+ import { createApp } from 'vue'
75
+ import { VueTailwindUI } from 'cisse-vue-ui'
76
+
77
+ const app = createApp(App)
78
+
79
+ // Register all components
80
+ app.use(VueTailwindUI)
81
+
82
+ // Or with a prefix
83
+ app.use(VueTailwindUI, { prefix: 'Ui' }) // <UiButton>, <UiCard>, etc.
84
+
85
+ // Or specific components only
86
+ app.use(VueTailwindUI, { components: ['Button', 'CardComponent'] })
87
+ ```
88
+
89
+ ## Components
90
+
91
+ ### Core
92
+
93
+ | Component | Description |
94
+ |-----------|-------------|
95
+ | `Button` | Button with variants (primary, secondary, outline, ghost, danger, success), sizes, icons, loading state |
96
+ | `CardComponent` | Card container with header, content, and footer slots |
97
+ | `TableComponent` | Data table with sorting, selection, actions, and custom column rendering |
98
+ | `MobileList` | Mobile-optimized card-based list with selection support |
99
+ | `ResponsiveList` | Combines MobileList (mobile) and TableComponent (desktop) with automatic breakpoint switching |
100
+ | `Tabs` | Tab navigation with variants (underline, pills, boxed) |
101
+ | `TabPanel` | Tab content panel (use with Tabs) |
102
+ | `Dropdown` | Dropdown menu with items, icons, and dividers |
103
+ | `Avatar` | User avatar with image, initials, or icon fallback |
104
+ | `AutocompleteComponent` | Searchable select with keyboard navigation |
105
+ | `MenuItem` | Navigation menu item with icon, active state detection, and route support |
106
+ | `StatusBadge` | Colored status indicator badge |
107
+ | `TableAction` | Icon button for table row actions |
108
+ | `Stepper` | Multi-step progress indicator with horizontal/vertical orientation |
109
+ | `CollapsibleCard` | Card that can expand/collapse its content |
110
+ | `Accordion` | Expandable content sections with single/multiple mode |
111
+ | `Breadcrumb` | Navigation breadcrumb trail |
112
+ | `Drawer` | Slide-out panel from any edge (left, right, top, bottom) |
113
+ | `Popover` | Floating content panel triggered by click or hover |
114
+ | `Timeline` | Vertical timeline for events/history display |
115
+ | `Tooltip` | Hover tooltip with customizable position |
116
+
117
+ ### Form
118
+
119
+ | Component | Description |
120
+ |-----------|-------------|
121
+ | `FormInput` | Text input with validation states and ARIA support |
122
+ | `FormSelect` | Select dropdown with search, multi-select, and validation |
123
+ | `FormGroup` | Form field wrapper with label, help text, and error states |
124
+ | `FormLabel` | Styled form label with required indicator |
125
+ | `FormHelp` | Help/error text for form fields |
126
+ | `SearchInput` | Search input with icon and clear button |
127
+ | `Switch` | Toggle switch with label and description |
128
+ | `Checkbox` | Checkbox with label, description, and indeterminate state |
129
+ | `Combobox` | Multi-select combobox with search and tags |
130
+ | `DatePicker` | Calendar date picker with min/max dates |
131
+ | `ColorPicker` | Color selection with swatches and custom input |
132
+ | `FileUpload` | Drag-and-drop file upload with preview |
133
+ | `Rating` | Star rating input with half-star support |
134
+ | `Slider` | Single value slider input |
135
+ | `RangeSlider` | Dual-handle range slider |
136
+
137
+ ### Feedback
138
+
139
+ | Component | Description |
140
+ |-----------|-------------|
141
+ | `Modal` | Modal dialog with focus trap, ARIA support, and slots |
142
+ | `ConfirmDialog` | Confirmation modal with customizable actions |
143
+ | `Alert` | Alert banner with variants (info, success, warning, error) |
144
+ | `Toast` | Individual toast notification with auto-dismiss |
145
+ | `ToastContainer` | Toast notification container with positioning |
146
+ | `LoadingSpinner` | Loading indicator with size variants |
147
+ | `Progress` | Progress bar with percentage display |
148
+ | `Skeleton` | Loading placeholder with animation |
149
+ | `CardSkeleton` | Card loading skeleton |
150
+ | `ListSkeleton` | List loading skeleton |
151
+ | `TableSkeleton` | Table loading skeleton |
152
+ | `PaginationControls` | Pagination with page numbers and navigation |
153
+ | `NotificationList` | Notification list container |
154
+ | `NotificationComponent` | Individual notification item |
155
+ | `EmptyState` | Placeholder for empty content with icon and action slot |
156
+
157
+ ### Layout
158
+
159
+ | Component | Description |
160
+ |-----------|-------------|
161
+ | `AuthLayout` | Split-panel authentication layout with branding and form sections |
162
+ | `BaseLayout` | App shell with sidebar, header, main content area, and route-aware menu |
163
+ | `PageLayout` | Page wrapper with breadcrumbs |
164
+
165
+ ### Type Display
166
+
167
+ | Component | Description |
168
+ |-----------|-------------|
169
+ | `TextType` | Text value display |
170
+ | `NumberType` | Formatted number display |
171
+ | `DateType` | Formatted date display |
172
+ | `BooleanType` | Boolean value display (check/cross icons) |
173
+ | `BadgeType` | Badge value display with colors |
174
+
175
+ ## Composables
176
+
177
+ ```typescript
178
+ import {
179
+ useNotifications,
180
+ useDarkMode,
181
+ useExportCSV,
182
+ useDropdown,
183
+ useModal,
184
+ useToast,
185
+ useFocusTrap,
186
+ useId
187
+ } from 'cisse-vue-ui/composables'
188
+ ```
189
+
190
+ ### useModal
191
+
192
+ Manage modal state with data support:
193
+
194
+ ```typescript
195
+ import { useModal } from 'cisse-vue-ui/composables'
196
+
197
+ // Simple modal
198
+ const createModal = useModal()
199
+ createModal.open()
200
+ createModal.close()
201
+
202
+ // Modal with data (e.g., for editing)
203
+ const editModal = useModal<User>()
204
+ editModal.open(selectedUser)
205
+ // Access editModal.data.value in template
206
+
207
+ // With callbacks
208
+ const deleteModal = useModal<Item>({
209
+ onOpen: (data) => console.log('Opening with:', data),
210
+ onClose: () => refetchData()
211
+ })
212
+ ```
213
+
214
+ ```vue
215
+ <template>
216
+ <!-- Use isOpen for v-model binding -->
217
+ <Modal v-model="editModal.isOpen.value" title="Edit User">
218
+ <FormInput v-model="editModal.data.value.name" label="Name" />
219
+ <template #footer>
220
+ <Button @click="editModal.close()">Cancel</Button>
221
+ <Button variant="primary" @click="save">Save</Button>
222
+ </template>
223
+ </Modal>
224
+ </template>
225
+ ```
226
+
227
+ ### useDropdown
228
+
229
+ Shared dropdown logic for custom dropdown components (used internally by Dropdown, FormSelect, AutocompleteComponent):
230
+
231
+ ```typescript
232
+ import { useDropdown } from 'cisse-vue-ui/composables'
233
+ import { ref } from 'vue'
234
+
235
+ const triggerRef = ref<HTMLElement>()
236
+ const dropdownRef = ref<HTMLElement>()
237
+
238
+ const {
239
+ isOpen,
240
+ highlightedIndex,
241
+ dropdownStyle,
242
+ open,
243
+ close,
244
+ toggle,
245
+ handleKeydown,
246
+ scrollToHighlighted,
247
+ } = useDropdown(triggerRef, dropdownRef, {
248
+ teleport: true,
249
+ align: 'left',
250
+ gap: 8,
251
+ onOpen: () => console.log('Opened'),
252
+ onClose: () => console.log('Closed'),
253
+ })
254
+ ```
255
+
256
+ ### useNotifications
257
+
258
+ ```typescript
259
+ const { notifications, addNotification, removeNotification } = useNotifications()
260
+
261
+ addNotification({
262
+ type: 'success',
263
+ title: 'Saved',
264
+ message: 'Your changes have been saved.'
265
+ })
266
+ ```
267
+
268
+ ### useDarkMode
269
+
270
+ ```typescript
271
+ const { isDark, toggle, enable, disable } = useDarkMode({
272
+ selector: 'html', // Element to add .dark class
273
+ storageKey: 'theme', // localStorage key
274
+ defaultDark: false // Default state
275
+ })
276
+ ```
277
+
278
+ ### useExportCSV
279
+
280
+ ```typescript
281
+ const { exportToCSV } = useExportCSV()
282
+
283
+ exportToCSV(data, columns, 'export.csv')
284
+ ```
285
+
286
+ ### useToast
287
+
288
+ Toast notification system with positioning and auto-dismiss:
289
+
290
+ ```typescript
291
+ import { useToast } from 'cisse-vue-ui/composables'
292
+
293
+ const { toasts, addToast, removeToast, clearToasts } = useToast()
294
+
295
+ // Add a toast
296
+ addToast({
297
+ type: 'success',
298
+ title: 'Success!',
299
+ message: 'Your changes have been saved.',
300
+ duration: 5000 // auto-dismiss after 5s
301
+ })
302
+
303
+ // Different toast types
304
+ addToast({ type: 'error', title: 'Error', message: 'Something went wrong' })
305
+ addToast({ type: 'warning', title: 'Warning', message: 'Please review' })
306
+ addToast({ type: 'info', title: 'Info', message: 'New update available' })
307
+ ```
308
+
309
+ ```vue
310
+ <template>
311
+ <!-- Add ToastContainer to your app root -->
312
+ <ToastContainer position="top-right" />
313
+ </template>
314
+ ```
315
+
316
+ ### useFocusTrap
317
+
318
+ Trap focus within a container (used internally by Modal):
319
+
320
+ ```typescript
321
+ import { useFocusTrap } from 'cisse-vue-ui/composables'
322
+ import { ref } from 'vue'
323
+
324
+ const isActive = ref(true)
325
+ const { containerRef } = useFocusTrap({
326
+ active: isActive,
327
+ focusFirst: true, // Focus first focusable element on activate
328
+ restoreFocus: true // Restore focus on deactivate
329
+ })
330
+ ```
331
+
332
+ ### useId
333
+
334
+ Generate unique IDs for accessibility (ARIA relationships):
335
+
336
+ ```typescript
337
+ import { useId } from 'cisse-vue-ui/composables'
338
+
339
+ const { id, related } = useId({ prefix: 'modal' })
340
+ // id.value = 'cisse-modal-1'
341
+ // related('title') = 'cisse-modal-1-title'
342
+ // related('description') = 'cisse-modal-1-description'
343
+ ```
344
+
345
+ ```vue
346
+ <template>
347
+ <div :id="id" role="dialog" :aria-labelledby="related('title')">
348
+ <h2 :id="related('title')">Dialog Title</h2>
349
+ </div>
350
+ </template>
351
+ ```
352
+
353
+ ## Types
354
+
355
+ ```typescript
356
+ import type { Property, Notification, Breadcrumb } from 'cisse-vue-ui/types'
357
+
358
+ // Table column definition
359
+ const columns: Property[] = [
360
+ { key: 'name', label: 'Name', sortable: true },
361
+ { key: 'email', label: 'Email' },
362
+ { key: 'status', label: 'Status', type: 'badge' }
363
+ ]
364
+
365
+ // Breadcrumb navigation
366
+ const breadcrumbs: Breadcrumb[] = [
367
+ { label: 'Home', to: '/' },
368
+ { label: 'Users', to: '/users' },
369
+ { label: 'Edit' }
370
+ ]
371
+ ```
372
+
373
+ ## Component Examples
374
+
375
+ ### Button
376
+
377
+ ```vue
378
+ <Button variant="primary" size="md" :loading="isLoading">
379
+ Save Changes
380
+ </Button>
381
+
382
+ <Button variant="outline" icon="lucide:plus">
383
+ Add Item
384
+ </Button>
385
+
386
+ <Button variant="danger" icon="lucide:trash">
387
+ Delete
388
+ </Button>
389
+ ```
390
+
391
+ ### Tabs
392
+
393
+ ```vue
394
+ <script setup>
395
+ import { ref } from 'vue'
396
+ import { Tabs, TabPanel } from 'cisse-vue-ui'
397
+
398
+ const activeTab = ref('profile')
399
+ const tabs = [
400
+ { key: 'profile', label: 'Profile' },
401
+ { key: 'settings', label: 'Settings' },
402
+ { key: 'notifications', label: 'Notifications' }
403
+ ]
404
+ </script>
405
+
406
+ <template>
407
+ <Tabs v-model="activeTab" :tabs="tabs" variant="underline">
408
+ <TabPanel value="profile">Profile content</TabPanel>
409
+ <TabPanel value="settings">Settings content</TabPanel>
410
+ <TabPanel value="notifications">Notifications content</TabPanel>
411
+ </Tabs>
412
+ </template>
413
+ ```
414
+
415
+ ### Switch
416
+
417
+ ```vue
418
+ <Switch
419
+ v-model="emailNotifications"
420
+ label="Email notifications"
421
+ description="Receive email updates about your account"
422
+ />
423
+ ```
424
+
425
+ ### Alert
426
+
427
+ ```vue
428
+ <Alert variant="success" title="Success!" dismissible>
429
+ Your changes have been saved successfully.
430
+ </Alert>
431
+
432
+ <Alert variant="error" title="Error">
433
+ Something went wrong. Please try again.
434
+ </Alert>
435
+ ```
436
+
437
+ ### Dropdown
438
+
439
+ ```vue
440
+ <script setup>
441
+ import { Dropdown } from 'cisse-vue-ui'
442
+
443
+ const items = [
444
+ { key: 'edit', label: 'Edit', icon: 'lucide:edit' },
445
+ { key: 'duplicate', label: 'Duplicate', icon: 'lucide:copy' },
446
+ { key: 'divider', divider: true },
447
+ { key: 'delete', label: 'Delete', icon: 'lucide:trash', danger: true }
448
+ ]
449
+
450
+ const handleSelect = (item) => {
451
+ console.log('Selected:', item.key)
452
+ }
453
+ </script>
454
+
455
+ <template>
456
+ <Dropdown :items="items" @select="handleSelect">
457
+ <template #trigger-label>Actions</template>
458
+ </Dropdown>
459
+ </template>
460
+ ```
461
+
462
+ ### Stepper
463
+
464
+ ```vue
465
+ <script setup>
466
+ import { ref } from 'vue'
467
+ import { Stepper } from 'cisse-vue-ui'
468
+
469
+ const currentStep = ref('step2')
470
+ const steps = [
471
+ { key: 'step1', title: 'Account', description: 'Create account', icon: 'lucide:user' },
472
+ { key: 'step2', title: 'Profile', description: 'Set up profile', icon: 'lucide:settings' },
473
+ { key: 'step3', title: 'Complete', description: 'Ready to go!', icon: 'lucide:check' }
474
+ ]
475
+ </script>
476
+
477
+ <template>
478
+ <Stepper v-model="currentStep" :steps="steps" />
479
+ </template>
480
+ ```
481
+
482
+ ### EmptyState
483
+
484
+ ```vue
485
+ <EmptyState
486
+ title="No results found"
487
+ message="Try adjusting your search or filters"
488
+ icon="lucide:search-x"
489
+ >
490
+ <template #action>
491
+ <Button variant="primary" size="sm">Clear filters</Button>
492
+ </template>
493
+ </EmptyState>
494
+ ```
495
+
496
+ ### Checkbox
497
+
498
+ ```vue
499
+ <Checkbox
500
+ v-model="accepted"
501
+ label="Accept terms"
502
+ description="I agree to the terms and conditions"
503
+ />
504
+ ```
505
+
506
+ ### TableComponent
507
+
508
+ ```vue
509
+ <script setup>
510
+ import { ref } from 'vue'
511
+ import { TableComponent } from 'cisse-vue-ui'
512
+
513
+ const properties = [
514
+ { name: 'name', label: 'Name', main: true },
515
+ { name: 'email', label: 'Email' },
516
+ { name: 'role', label: 'Role', type: 'badge' }
517
+ ]
518
+
519
+ const items = [
520
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
521
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' }
522
+ ]
523
+
524
+ // Selection support
525
+ const selectedItems = ref(new Set())
526
+ const toggleSelect = (id) => {
527
+ if (selectedItems.value.has(id)) {
528
+ selectedItems.value.delete(id)
529
+ } else {
530
+ selectedItems.value.add(id)
531
+ }
532
+ }
533
+ </script>
534
+
535
+ <template>
536
+ <TableComponent
537
+ :properties="properties"
538
+ :items="items"
539
+ selectable
540
+ :selected-items="selectedItems"
541
+ @select="toggleSelect"
542
+ @select-all="toggleSelectAll"
543
+ >
544
+ <template #action="{ item }">
545
+ <TableAction icon="lucide:edit" @click="edit(item)" />
546
+ <TableAction icon="lucide:trash" variant="danger" @click="delete(item)" />
547
+ </template>
548
+ </TableComponent>
549
+ </template>
550
+ ```
551
+
552
+ ### ResponsiveList
553
+
554
+ A component that automatically switches between a mobile card layout and a desktop table layout based on screen size.
555
+
556
+ ```vue
557
+ <script setup>
558
+ import { ref } from 'vue'
559
+ import { ResponsiveList } from 'cisse-vue-ui'
560
+
561
+ const columns = [
562
+ { key: 'name', label: 'Name' },
563
+ { key: 'email', label: 'Email' },
564
+ { key: 'status', label: 'Status' }
565
+ ]
566
+
567
+ const items = [
568
+ { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
569
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' }
570
+ ]
571
+
572
+ const selectedItems = ref(new Set())
573
+
574
+ const toggleSelect = (id) => {
575
+ if (selectedItems.value.has(id)) {
576
+ selectedItems.value.delete(id)
577
+ } else {
578
+ selectedItems.value.add(id)
579
+ }
580
+ }
581
+
582
+ const toggleSelectAll = () => {
583
+ if (selectedItems.value.size === items.length) {
584
+ selectedItems.value.clear()
585
+ } else {
586
+ items.forEach(item => selectedItems.value.add(String(item.id)))
587
+ }
588
+ }
589
+ </script>
590
+
591
+ <template>
592
+ <ResponsiveList
593
+ :items="items"
594
+ :columns="columns"
595
+ key-field="id"
596
+ selectable
597
+ :selected-items="selectedItems"
598
+ breakpoint="lg"
599
+ @select="toggleSelect"
600
+ @select-all="toggleSelectAll"
601
+ >
602
+ <!-- Mobile view: avatar -->
603
+ <template #avatar="{ item }">
604
+ <div class="w-10 h-10 rounded-full bg-primary-500 flex items-center justify-center text-white">
605
+ {{ item.name[0] }}
606
+ </div>
607
+ </template>
608
+
609
+ <!-- Mobile view: content -->
610
+ <template #mobileContent="{ item }">
611
+ <h3 class="font-semibold">{{ item.name }}</h3>
612
+ <p class="text-sm text-gray-500">{{ item.email }}</p>
613
+ </template>
614
+
615
+ <!-- Mobile view: actions -->
616
+ <template #mobileActions="{ item }">
617
+ <button @click="viewItem(item)">View</button>
618
+ </template>
619
+
620
+ <!-- Desktop table: custom cell rendering -->
621
+ <template #cell-name="{ item }">
622
+ <span class="font-medium">{{ item.name }}</span>
623
+ </template>
624
+
625
+ <template #cell-status="{ item }">
626
+ <span :class="item.status === 'active' ? 'text-green-600' : 'text-red-600'">
627
+ {{ item.status }}
628
+ </span>
629
+ </template>
630
+
631
+ <!-- Desktop table: actions column -->
632
+ <template #actions="{ item }">
633
+ <Button size="sm" variant="ghost" @click="edit(item)">Edit</Button>
634
+ </template>
635
+
636
+ <!-- Empty state -->
637
+ <template #empty>
638
+ <EmptyState title="No items" message="No items to display" />
639
+ </template>
640
+ </ResponsiveList>
641
+ </template>
642
+ ```
643
+
644
+ #### ResponsiveList Props
645
+
646
+ | Prop | Type | Default | Description |
647
+ |------|------|---------|-------------|
648
+ | `items` | `Array` | required | Array of items to display |
649
+ | `columns` | `Array` | required | Column definitions with `key` or `name`, `label`, and optional `type` |
650
+ | `keyField` | `string` | `'id'` | Field to use as unique key for items |
651
+ | `selectable` | `boolean` | `false` | Enable selection mode |
652
+ | `selectedItems` | `Set<string>` | - | Set of selected item keys |
653
+ | `selectableFilter` | `Function` | - | Filter function to determine if an item is selectable |
654
+ | `breakpoint` | `string` | `'lg'` | Breakpoint for switching views: `'sm'`, `'md'`, `'lg'`, `'xl'`, `'2xl'` |
655
+
656
+ ### MobileList
657
+
658
+ A mobile-optimized card-based list component with selection support.
659
+
660
+ ```vue
661
+ <script setup>
662
+ import { MobileList } from 'cisse-vue-ui'
663
+ </script>
664
+
665
+ <template>
666
+ <MobileList
667
+ :items="items"
668
+ key-field="id"
669
+ selectable
670
+ :selected-items="selectedItems"
671
+ @select="toggleSelect"
672
+ @select-all="toggleSelectAll"
673
+ >
674
+ <template #avatar="{ item }">
675
+ <div class="w-12 h-12 rounded-full bg-blue-500" />
676
+ </template>
677
+
678
+ <template #content="{ item }">
679
+ <h3>{{ item.name }}</h3>
680
+ <p>{{ item.description }}</p>
681
+ </template>
682
+
683
+ <template #actions="{ item }">
684
+ <button>View</button>
685
+ </template>
686
+ </MobileList>
687
+ </template>
688
+ ```
689
+
690
+ ### MenuItem
691
+
692
+ ```vue
693
+ <script setup>
694
+ import { useRoute } from 'vue-router'
695
+ import { MenuItem } from 'cisse-vue-ui'
696
+
697
+ const route = useRoute()
698
+
699
+ const menuItem = {
700
+ label: 'Dashboard',
701
+ link: '/dashboard',
702
+ icon: 'lucide:layout-dashboard'
703
+ }
704
+ </script>
705
+
706
+ <template>
707
+ <!-- Auto-detect active state from current route -->
708
+ <MenuItem :menu-item="menuItem" :current-path="route.path" />
709
+
710
+ <!-- Or manually control active state -->
711
+ <MenuItem :menu-item="menuItem" :active="true" />
712
+ </template>
713
+ ```
714
+
715
+ ### AuthLayout
716
+
717
+ Split-panel layout for authentication pages (login, register, password reset).
718
+
719
+ ```vue
720
+ <script setup>
721
+ import { ref } from 'vue'
722
+ import { AuthLayout, type AuthFeature } from 'cisse-vue-ui'
723
+
724
+ const email = ref('')
725
+ const password = ref('')
726
+
727
+ const features: AuthFeature[] = [
728
+ { icon: 'lucide:shield', text: 'Secure authentication' },
729
+ { icon: 'lucide:zap', text: 'Fast login' },
730
+ { icon: 'lucide:users', text: 'Team collaboration' },
731
+ ]
732
+
733
+ function handleSubmit() {
734
+ // Handle login
735
+ }
736
+ </script>
737
+
738
+ <template>
739
+ <AuthLayout
740
+ app-name="My App"
741
+ app-icon="lucide:box"
742
+ headline="Welcome to"
743
+ sub-headline="My Application"
744
+ description="Sign in to access your account."
745
+ :features="features"
746
+ form-title="Sign In"
747
+ form-subtitle="Enter your credentials"
748
+ home-link="/"
749
+ >
750
+ <form @submit.prevent="handleSubmit" class="space-y-4">
751
+ <input v-model="email" type="email" placeholder="Email" class="..." />
752
+ <input v-model="password" type="password" placeholder="Password" class="..." />
753
+ <button type="submit">Sign In</button>
754
+ </form>
755
+
756
+ <template #form-footer>
757
+ <p class="text-center mt-6">
758
+ Don't have an account? <a href="/register">Sign up</a>
759
+ </p>
760
+ </template>
761
+ </AuthLayout>
762
+ </template>
763
+ ```
764
+
765
+ #### AuthLayout Props
766
+
767
+ | Prop | Type | Default | Description |
768
+ |------|------|---------|-------------|
769
+ | `appName` | `string` | `''` | Application name displayed in logo |
770
+ | `appIcon` | `string` | `'lucide:box'` | Iconify icon for app logo |
771
+ | `headline` | `string` | `''` | First line of headline |
772
+ | `subHeadline` | `string` | `''` | Second line with decorative underline |
773
+ | `description` | `string` | `''` | Description paragraph |
774
+ | `features` | `AuthFeature[]` | `[]` | List of features with icon and text |
775
+ | `gradientFrom` | `string` | `'from-primary-700'` | Tailwind gradient start class |
776
+ | `gradientVia` | `string` | `''` | Tailwind gradient middle class |
777
+ | `gradientTo` | `string` | `'to-primary-800'` | Tailwind gradient end class |
778
+ | `showDecorations` | `boolean` | `true` | Show floating decorative shapes |
779
+ | `showPattern` | `boolean` | `true` | Show dot pattern overlay |
780
+ | `underlineColor` | `string` | `'rgba(165, 180, 252, 0.5)'` | Sub-headline underline color |
781
+ | `formTitle` | `string` | `''` | Form panel title |
782
+ | `formSubtitle` | `string` | `''` | Form panel subtitle |
783
+ | `homeLink` | `string` | `'/'` | Mobile logo link URL |
784
+ | `brandingAnimation` | `string` | `''` | CSS class for branding animation |
785
+ | `formAnimation` | `string` | `''` | CSS class for form panel animation |
786
+
787
+ #### AuthLayout Slots
788
+
789
+ | Slot | Description |
790
+ |------|-------------|
791
+ | `default` | Form content (inside the white card) |
792
+ | `branding-panel` | Complete override of the branding panel |
793
+ | `branding-logo` | Custom logo in branding panel |
794
+ | `branding-headline` | Custom headline content |
795
+ | `branding-features` | Custom features list |
796
+ | `branding-content` | Additional content after features |
797
+ | `mobile-logo` | Custom mobile logo |
798
+ | `form-header` | Content above the form card |
799
+ | `form-footer` | Content below the form card |
800
+
801
+ ### BaseLayout
802
+
803
+ ```vue
804
+ <script setup>
805
+ import { useRoute } from 'vue-router'
806
+ import { BaseLayout } from 'cisse-vue-ui'
807
+
808
+ const route = useRoute()
809
+
810
+ const menuItems = [
811
+ { label: 'Dashboard', link: '/', icon: 'lucide:home' },
812
+ { label: 'Users', link: '/users', icon: 'lucide:users' },
813
+ { label: 'Settings', link: '/settings', icon: 'lucide:settings' }
814
+ ]
815
+ </script>
816
+
817
+ <template>
818
+ <BaseLayout
819
+ :menu-items="menuItems"
820
+ :current-path="route.path"
821
+ :show-dark-toggle="true"
822
+ menu-position="top"
823
+ >
824
+ <template #logo>
825
+ <img src="/logo.svg" alt="Logo" class="h-8" />
826
+ </template>
827
+
828
+ <RouterView />
829
+ </BaseLayout>
830
+ </template>
831
+ ```
832
+
833
+ #### BaseLayout Props
834
+
835
+ | Prop | Type | Default | Description |
836
+ |------|------|---------|-------------|
837
+ | `menuItems` | `MenuItemProps[]` | `[]` | Menu items for the sidebar |
838
+ | `appName` | `string` | `'App'` | App/brand name displayed in sidebar |
839
+ | `appIcon` | `string` | `'lucide:box'` | App icon (Iconify icon name) |
840
+ | `sidebarOpen` | `boolean` | `true` | Whether sidebar is open (v-model:sidebarOpen) |
841
+ | `dark` | `boolean` | `false` | Whether dark mode is enabled (v-model:dark) |
842
+ | `showDarkToggle` | `boolean` | `true` | Show dark mode toggle in header |
843
+ | `sidebarClass` | `string` | `'bg-[#172b4c]...'` | CSS classes for sidebar background |
844
+ | `currentPath` | `string` | - | Current route path for menu active state |
845
+ | `userName` | `string` | - | User display name |
846
+ | `userAvatar` | `string` | - | User avatar (initials or image URL) |
847
+ | `userMenuItems` | `UserMenuItem[]` | `[]` | User menu dropdown items |
848
+ | `menuPosition` | `'top' \| 'center' \| 'bottom'` | `'top'` | Menu vertical position in sidebar |
849
+
850
+ #### BaseLayout Slots
851
+
852
+ | Slot | Description |
853
+ |------|-------------|
854
+ | `default` | Main content area (or renders RouterView if available) |
855
+ | `logo` | Custom logo in sidebar header |
856
+ | `menu` | Custom menu content (receives currentPath) |
857
+ | `sidebar-footer` | Content at bottom of sidebar |
858
+ | `header-center` | Center content in header |
859
+ | `header-actions` | Action buttons in header (before dark toggle) |
860
+
861
+ ## Dark Mode
862
+
863
+ Components support dark mode via the `.dark` class on a parent element:
864
+
865
+ ```html
866
+ <html class="dark">
867
+ <!-- Components will use dark theme -->
868
+ </html>
869
+ ```
870
+
871
+ Use the `useDarkMode` composable or implement your own toggle:
872
+
873
+ ```typescript
874
+ const { isDark, toggle } = useDarkMode()
875
+ ```
876
+
877
+ ## License
878
+
879
+ MIT