cisse-vue-ui 0.3.2 → 0.5.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 (134) hide show
  1. package/README.md +666 -666
  2. package/dist/BadgeType.vue_vue_type_script_setup_true_lang-CJb63H1I.cjs.map +1 -1
  3. package/dist/BadgeType.vue_vue_type_script_setup_true_lang-CnB5eNEM.js.map +1 -1
  4. package/dist/Checkbox.vue_vue_type_script_setup_true_lang-B-nLCCNY.js.map +1 -1
  5. package/dist/Checkbox.vue_vue_type_script_setup_true_lang-DIoHDji4.cjs.map +1 -1
  6. package/dist/{Dropdown.vue_vue_type_script_setup_true_lang-B9DsCY8M.js → Dropdown.vue_vue_type_script_setup_true_lang-C3pr8BwC.js} +2 -2
  7. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-C3pr8BwC.js.map +1 -0
  8. package/dist/{Dropdown.vue_vue_type_script_setup_true_lang-nMP2OxXp.cjs → Dropdown.vue_vue_type_script_setup_true_lang-DKxcVBKu.cjs} +2 -2
  9. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-DKxcVBKu.cjs.map +1 -0
  10. package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-Bnw5L-xO.cjs → PageLayout.vue_vue_type_script_setup_true_lang-1rNUMab6.cjs} +2 -2
  11. package/dist/PageLayout.vue_vue_type_script_setup_true_lang-1rNUMab6.cjs.map +1 -0
  12. package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-D8uD3-Fe.js → PageLayout.vue_vue_type_script_setup_true_lang-DLfqj6EP.js} +2 -2
  13. package/dist/PageLayout.vue_vue_type_script_setup_true_lang-DLfqj6EP.js.map +1 -0
  14. package/dist/{DatePicker.vue_vue_type_script_setup_true_lang-CxddOiV4.cjs → RangeSlider.vue_vue_type_script_setup_true_lang-BRNkkx89.cjs} +715 -100
  15. package/dist/RangeSlider.vue_vue_type_script_setup_true_lang-BRNkkx89.cjs.map +1 -0
  16. package/dist/{DatePicker.vue_vue_type_script_setup_true_lang-DKDcnO28.js → RangeSlider.vue_vue_type_script_setup_true_lang-DD9UxnCU.js} +716 -101
  17. package/dist/RangeSlider.vue_vue_type_script_setup_true_lang-DD9UxnCU.js.map +1 -0
  18. package/dist/{Skeleton.vue_vue_type_script_setup_true_lang-DRC4EADS.js → Skeleton.vue_vue_type_script_setup_true_lang-BcWF7mwz.js} +2 -2
  19. package/dist/Skeleton.vue_vue_type_script_setup_true_lang-BcWF7mwz.js.map +1 -0
  20. package/dist/{Skeleton.vue_vue_type_script_setup_true_lang-D2S5g9s5.cjs → Skeleton.vue_vue_type_script_setup_true_lang-D-2qAhyG.cjs} +2 -2
  21. package/dist/Skeleton.vue_vue_type_script_setup_true_lang-D-2qAhyG.cjs.map +1 -0
  22. package/dist/{Popover.vue_vue_type_script_setup_true_lang-Q7gRZsT9.js → Timeline.vue_vue_type_script_setup_true_lang-BWIfzQOG.js} +591 -135
  23. package/dist/Timeline.vue_vue_type_script_setup_true_lang-BWIfzQOG.js.map +1 -0
  24. package/dist/{Popover.vue_vue_type_script_setup_true_lang-dvlDTcf1.cjs → Timeline.vue_vue_type_script_setup_true_lang-C2Mq4XOO.cjs} +578 -122
  25. package/dist/Timeline.vue_vue_type_script_setup_true_lang-C2Mq4XOO.cjs.map +1 -0
  26. package/dist/cisse-vue-ui.css +51 -51
  27. package/dist/components/core/Accordion.stories.d.ts +11 -0
  28. package/dist/components/core/Accordion.test.d.ts +1 -0
  29. package/dist/components/core/Accordion.vue.d.ts +27 -0
  30. package/dist/components/core/AccordionItem.vue.d.ts +27 -0
  31. package/dist/components/core/AutocompleteComponent.test.d.ts +1 -0
  32. package/dist/components/core/Breadcrumb.stories.d.ts +11 -0
  33. package/dist/components/core/Breadcrumb.test.d.ts +1 -0
  34. package/dist/components/core/Breadcrumb.vue.d.ts +26 -0
  35. package/dist/components/core/CardComponent.test.d.ts +1 -0
  36. package/dist/components/core/CollapsibleCard.test.d.ts +1 -0
  37. package/dist/components/core/Drawer.stories.d.ts +12 -0
  38. package/dist/components/core/Drawer.test.d.ts +1 -0
  39. package/dist/components/core/Drawer.vue.d.ts +50 -0
  40. package/dist/components/core/Dropdown.test.d.ts +1 -0
  41. package/dist/components/core/MenuItem.test.d.ts +1 -0
  42. package/dist/components/core/MobileList.test.d.ts +1 -0
  43. package/dist/components/core/Popover.test.d.ts +1 -0
  44. package/dist/components/core/ResponsiveList.test.d.ts +1 -0
  45. package/dist/components/core/StatusBadge.test.d.ts +1 -0
  46. package/dist/components/core/Stepper.test.d.ts +1 -0
  47. package/dist/components/core/TabPanel.test.d.ts +1 -0
  48. package/dist/components/core/TableAction.test.d.ts +1 -0
  49. package/dist/components/core/TableComponent.test.d.ts +1 -0
  50. package/dist/components/core/Tabs.test.d.ts +1 -0
  51. package/dist/components/core/Timeline.stories.d.ts +11 -0
  52. package/dist/components/core/Timeline.test.d.ts +1 -0
  53. package/dist/components/core/Timeline.vue.d.ts +34 -0
  54. package/dist/components/core/index.cjs +21 -16
  55. package/dist/components/core/index.cjs.map +1 -1
  56. package/dist/components/core/index.d.ts +9 -0
  57. package/dist/components/core/index.js +7 -2
  58. package/dist/components/feedback/EmptyState.test.d.ts +1 -0
  59. package/dist/components/feedback/LoadingSpinner.test.d.ts +1 -0
  60. package/dist/components/feedback/NotificationComponent.test.d.ts +1 -0
  61. package/dist/components/feedback/NotificationList.test.d.ts +1 -0
  62. package/dist/components/feedback/PaginationControls.test.d.ts +1 -0
  63. package/dist/components/feedback/Toast.test.d.ts +1 -0
  64. package/dist/components/feedback/ToastContainer.test.d.ts +1 -0
  65. package/dist/components/feedback/index.cjs +1 -1
  66. package/dist/components/feedback/index.js +1 -1
  67. package/dist/components/form/Checkbox.test.d.ts +1 -0
  68. package/dist/components/form/ColorPicker.stories.d.ts +12 -0
  69. package/dist/components/form/ColorPicker.test.d.ts +1 -0
  70. package/dist/components/form/ColorPicker.vue.d.ts +23 -0
  71. package/dist/components/form/DatePicker.test.d.ts +1 -0
  72. package/dist/components/form/FileUpload.stories.d.ts +12 -0
  73. package/dist/components/form/FileUpload.test.d.ts +1 -0
  74. package/dist/components/form/FileUpload.vue.d.ts +40 -0
  75. package/dist/components/form/FormGroup.test.d.ts +1 -0
  76. package/dist/components/form/FormHelp.test.d.ts +1 -0
  77. package/dist/components/form/FormInput.test.d.ts +1 -0
  78. package/dist/components/form/FormLabel.test.d.ts +1 -0
  79. package/dist/components/form/FormSelect.test.d.ts +1 -0
  80. package/dist/components/form/RangeSlider.stories.d.ts +13 -0
  81. package/dist/components/form/RangeSlider.test.d.ts +1 -0
  82. package/dist/components/form/RangeSlider.vue.d.ts +35 -0
  83. package/dist/components/form/Rating.stories.d.ts +16 -0
  84. package/dist/components/form/Rating.test.d.ts +1 -0
  85. package/dist/components/form/Rating.vue.d.ts +43 -0
  86. package/dist/components/form/SearchInput.test.d.ts +1 -0
  87. package/dist/components/form/Slider.test.d.ts +1 -0
  88. package/dist/components/form/Switch.test.d.ts +1 -0
  89. package/dist/components/form/index.cjs +14 -10
  90. package/dist/components/form/index.cjs.map +1 -1
  91. package/dist/components/form/index.d.ts +6 -0
  92. package/dist/components/form/index.js +5 -1
  93. package/dist/components/index.cjs +37 -28
  94. package/dist/components/index.cjs.map +1 -1
  95. package/dist/components/index.js +16 -7
  96. package/dist/components/layout/BaseLayout.test.d.ts +1 -0
  97. package/dist/components/layout/PageLayout.test.d.ts +1 -0
  98. package/dist/components/layout/PageLayout.vue.d.ts +3 -3
  99. package/dist/components/layout/index.cjs +1 -1
  100. package/dist/components/layout/index.d.ts +1 -1
  101. package/dist/components/layout/index.js +1 -1
  102. package/dist/components/type/BadgeType.test.d.ts +1 -0
  103. package/dist/components/type/BooleanType.test.d.ts +1 -0
  104. package/dist/components/type/DateType.test.d.ts +1 -0
  105. package/dist/components/type/NumberType.test.d.ts +1 -0
  106. package/dist/components/type/TextType.test.d.ts +1 -0
  107. package/dist/index-B4NFaDHr.cjs +67 -0
  108. package/dist/index-B4NFaDHr.cjs.map +1 -0
  109. package/dist/index-C2DRkEjb.js +68 -0
  110. package/dist/index-C2DRkEjb.js.map +1 -0
  111. package/dist/index.cjs +38 -29
  112. package/dist/index.cjs.map +1 -1
  113. package/dist/index.js +17 -8
  114. package/dist/style.css +2 -2
  115. package/dist/types/components.d.ts +1 -1
  116. package/dist/useDropdown-DHFnd259.cjs.map +1 -1
  117. package/dist/useDropdown-iVu14E6s.js.map +1 -1
  118. package/dist/useToast-DT9hFfpM.js.map +1 -1
  119. package/dist/useToast-nJXpFz_M.cjs.map +1 -1
  120. package/package.json +7 -2
  121. package/dist/DatePicker.vue_vue_type_script_setup_true_lang-CxddOiV4.cjs.map +0 -1
  122. package/dist/DatePicker.vue_vue_type_script_setup_true_lang-DKDcnO28.js.map +0 -1
  123. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-B9DsCY8M.js.map +0 -1
  124. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-nMP2OxXp.cjs.map +0 -1
  125. package/dist/PageLayout.vue_vue_type_script_setup_true_lang-Bnw5L-xO.cjs.map +0 -1
  126. package/dist/PageLayout.vue_vue_type_script_setup_true_lang-D8uD3-Fe.js.map +0 -1
  127. package/dist/Popover.vue_vue_type_script_setup_true_lang-Q7gRZsT9.js.map +0 -1
  128. package/dist/Popover.vue_vue_type_script_setup_true_lang-dvlDTcf1.cjs.map +0 -1
  129. package/dist/Skeleton.vue_vue_type_script_setup_true_lang-D2S5g9s5.cjs.map +0 -1
  130. package/dist/Skeleton.vue_vue_type_script_setup_true_lang-DRC4EADS.js.map +0 -1
  131. package/dist/index-DNndxsOE.cjs +0 -58
  132. package/dist/index-DNndxsOE.cjs.map +0 -1
  133. package/dist/index-_NEu9rf2.js +0 -59
  134. package/dist/index-_NEu9rf2.js.map +0 -1
package/README.md CHANGED
@@ -1,666 +1,666 @@
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
-
111
- ### Form
112
-
113
- | Component | Description |
114
- |-----------|-------------|
115
- | `FormInput` | Text input with label, validation, and icons |
116
- | `FormSelect` | Select dropdown with label and validation |
117
- | `FormGroup` | Form field wrapper with label and help text |
118
- | `FormLabel` | Styled form label |
119
- | `FormHelp` | Help text for form fields |
120
- | `SearchInput` | Search input with icon and clear button |
121
- | `Switch` | Toggle switch with label and description |
122
- | `Checkbox` | Checkbox with label and description |
123
-
124
- ### Feedback
125
-
126
- | Component | Description |
127
- |-----------|-------------|
128
- | `Modal` | Modal dialog with header, body, footer slots |
129
- | `Alert` | Alert banner with variants (info, success, warning, error) |
130
- | `LoadingSpinner` | Loading indicator with size variants |
131
- | `PaginationControls` | Pagination with page numbers and navigation |
132
- | `NotificationList` | Toast notification container |
133
- | `NotificationComponent` | Individual toast notification |
134
- | `EmptyState` | Placeholder for empty content with icon and action slot |
135
-
136
- ### Layout
137
-
138
- | Component | Description |
139
- |-----------|-------------|
140
- | `BaseLayout` | App shell with sidebar, header, main content area, and route-aware menu |
141
- | `PageLayout` | Page wrapper with breadcrumbs |
142
-
143
- ### Type Display
144
-
145
- | Component | Description |
146
- |-----------|-------------|
147
- | `TextType` | Text value display |
148
- | `NumberType` | Formatted number display |
149
- | `DateType` | Formatted date display |
150
- | `BooleanType` | Boolean value display (check/cross icons) |
151
- | `BadgeType` | Badge value display with colors |
152
-
153
- ## Composables
154
-
155
- ```typescript
156
- import { useNotifications, useDarkMode, useExportCSV, useDropdown, useModal } from 'cisse-vue-ui/composables'
157
- ```
158
-
159
- ### useModal
160
-
161
- Manage modal state with data support:
162
-
163
- ```typescript
164
- import { useModal } from 'cisse-vue-ui/composables'
165
-
166
- // Simple modal
167
- const createModal = useModal()
168
- createModal.open()
169
- createModal.close()
170
-
171
- // Modal with data (e.g., for editing)
172
- const editModal = useModal<User>()
173
- editModal.open(selectedUser)
174
- // Access editModal.data.value in template
175
-
176
- // With callbacks
177
- const deleteModal = useModal<Item>({
178
- onOpen: (data) => console.log('Opening with:', data),
179
- onClose: () => refetchData()
180
- })
181
- ```
182
-
183
- ```vue
184
- <template>
185
- <!-- Use isOpen for v-model binding -->
186
- <Modal v-model="editModal.isOpen.value" title="Edit User">
187
- <FormInput v-model="editModal.data.value.name" label="Name" />
188
- <template #footer>
189
- <Button @click="editModal.close()">Cancel</Button>
190
- <Button variant="primary" @click="save">Save</Button>
191
- </template>
192
- </Modal>
193
- </template>
194
- ```
195
-
196
- ### useDropdown
197
-
198
- Shared dropdown logic for custom dropdown components (used internally by Dropdown, FormSelect, AutocompleteComponent):
199
-
200
- ```typescript
201
- import { useDropdown } from 'cisse-vue-ui/composables'
202
- import { ref } from 'vue'
203
-
204
- const triggerRef = ref<HTMLElement>()
205
- const dropdownRef = ref<HTMLElement>()
206
-
207
- const {
208
- isOpen,
209
- highlightedIndex,
210
- dropdownStyle,
211
- open,
212
- close,
213
- toggle,
214
- handleKeydown,
215
- scrollToHighlighted,
216
- } = useDropdown(triggerRef, dropdownRef, {
217
- teleport: true,
218
- align: 'left',
219
- gap: 8,
220
- onOpen: () => console.log('Opened'),
221
- onClose: () => console.log('Closed'),
222
- })
223
- ```
224
-
225
- ### useNotifications
226
-
227
- ```typescript
228
- const { notifications, addNotification, removeNotification } = useNotifications()
229
-
230
- addNotification({
231
- type: 'success',
232
- title: 'Saved',
233
- message: 'Your changes have been saved.'
234
- })
235
- ```
236
-
237
- ### useDarkMode
238
-
239
- ```typescript
240
- const { isDark, toggle, enable, disable } = useDarkMode({
241
- selector: 'html', // Element to add .dark class
242
- storageKey: 'theme', // localStorage key
243
- defaultDark: false // Default state
244
- })
245
- ```
246
-
247
- ### useExportCSV
248
-
249
- ```typescript
250
- const { exportToCSV } = useExportCSV()
251
-
252
- exportToCSV(data, columns, 'export.csv')
253
- ```
254
-
255
- ## Types
256
-
257
- ```typescript
258
- import type { Property, Notification, Breadcrumb } from 'cisse-vue-ui/types'
259
-
260
- // Table column definition
261
- const columns: Property[] = [
262
- { key: 'name', label: 'Name', sortable: true },
263
- { key: 'email', label: 'Email' },
264
- { key: 'status', label: 'Status', type: 'badge' }
265
- ]
266
-
267
- // Breadcrumb navigation
268
- const breadcrumbs: Breadcrumb[] = [
269
- { label: 'Home', to: '/' },
270
- { label: 'Users', to: '/users' },
271
- { label: 'Edit' }
272
- ]
273
- ```
274
-
275
- ## Component Examples
276
-
277
- ### Button
278
-
279
- ```vue
280
- <Button variant="primary" size="md" :loading="isLoading">
281
- Save Changes
282
- </Button>
283
-
284
- <Button variant="outline" icon="lucide:plus">
285
- Add Item
286
- </Button>
287
-
288
- <Button variant="danger" icon="lucide:trash">
289
- Delete
290
- </Button>
291
- ```
292
-
293
- ### Tabs
294
-
295
- ```vue
296
- <script setup>
297
- import { ref } from 'vue'
298
- import { Tabs, TabPanel } from 'cisse-vue-ui'
299
-
300
- const activeTab = ref('profile')
301
- const tabs = [
302
- { key: 'profile', label: 'Profile' },
303
- { key: 'settings', label: 'Settings' },
304
- { key: 'notifications', label: 'Notifications' }
305
- ]
306
- </script>
307
-
308
- <template>
309
- <Tabs v-model="activeTab" :tabs="tabs" variant="underline">
310
- <TabPanel value="profile">Profile content</TabPanel>
311
- <TabPanel value="settings">Settings content</TabPanel>
312
- <TabPanel value="notifications">Notifications content</TabPanel>
313
- </Tabs>
314
- </template>
315
- ```
316
-
317
- ### Switch
318
-
319
- ```vue
320
- <Switch
321
- v-model="emailNotifications"
322
- label="Email notifications"
323
- description="Receive email updates about your account"
324
- />
325
- ```
326
-
327
- ### Alert
328
-
329
- ```vue
330
- <Alert variant="success" title="Success!" dismissible>
331
- Your changes have been saved successfully.
332
- </Alert>
333
-
334
- <Alert variant="error" title="Error">
335
- Something went wrong. Please try again.
336
- </Alert>
337
- ```
338
-
339
- ### Dropdown
340
-
341
- ```vue
342
- <script setup>
343
- import { Dropdown } from 'cisse-vue-ui'
344
-
345
- const items = [
346
- { key: 'edit', label: 'Edit', icon: 'lucide:edit' },
347
- { key: 'duplicate', label: 'Duplicate', icon: 'lucide:copy' },
348
- { key: 'divider', divider: true },
349
- { key: 'delete', label: 'Delete', icon: 'lucide:trash', danger: true }
350
- ]
351
-
352
- const handleSelect = (item) => {
353
- console.log('Selected:', item.key)
354
- }
355
- </script>
356
-
357
- <template>
358
- <Dropdown :items="items" @select="handleSelect">
359
- <template #trigger-label>Actions</template>
360
- </Dropdown>
361
- </template>
362
- ```
363
-
364
- ### Stepper
365
-
366
- ```vue
367
- <script setup>
368
- import { ref } from 'vue'
369
- import { Stepper } from 'cisse-vue-ui'
370
-
371
- const currentStep = ref('step2')
372
- const steps = [
373
- { key: 'step1', title: 'Account', description: 'Create account', icon: 'lucide:user' },
374
- { key: 'step2', title: 'Profile', description: 'Set up profile', icon: 'lucide:settings' },
375
- { key: 'step3', title: 'Complete', description: 'Ready to go!', icon: 'lucide:check' }
376
- ]
377
- </script>
378
-
379
- <template>
380
- <Stepper v-model="currentStep" :steps="steps" />
381
- </template>
382
- ```
383
-
384
- ### EmptyState
385
-
386
- ```vue
387
- <EmptyState
388
- title="No results found"
389
- message="Try adjusting your search or filters"
390
- icon="lucide:search-x"
391
- >
392
- <template #action>
393
- <Button variant="primary" size="sm">Clear filters</Button>
394
- </template>
395
- </EmptyState>
396
- ```
397
-
398
- ### Checkbox
399
-
400
- ```vue
401
- <Checkbox
402
- v-model="accepted"
403
- label="Accept terms"
404
- description="I agree to the terms and conditions"
405
- />
406
- ```
407
-
408
- ### TableComponent
409
-
410
- ```vue
411
- <script setup>
412
- import { ref } from 'vue'
413
- import { TableComponent } from 'cisse-vue-ui'
414
-
415
- const properties = [
416
- { name: 'name', label: 'Name', main: true },
417
- { name: 'email', label: 'Email' },
418
- { name: 'role', label: 'Role', type: 'badge' }
419
- ]
420
-
421
- const items = [
422
- { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
423
- { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' }
424
- ]
425
-
426
- // Selection support
427
- const selectedItems = ref(new Set())
428
- const toggleSelect = (id) => {
429
- if (selectedItems.value.has(id)) {
430
- selectedItems.value.delete(id)
431
- } else {
432
- selectedItems.value.add(id)
433
- }
434
- }
435
- </script>
436
-
437
- <template>
438
- <TableComponent
439
- :properties="properties"
440
- :items="items"
441
- selectable
442
- :selected-items="selectedItems"
443
- @select="toggleSelect"
444
- @select-all="toggleSelectAll"
445
- >
446
- <template #action="{ item }">
447
- <TableAction icon="lucide:edit" @click="edit(item)" />
448
- <TableAction icon="lucide:trash" variant="danger" @click="delete(item)" />
449
- </template>
450
- </TableComponent>
451
- </template>
452
- ```
453
-
454
- ### ResponsiveList
455
-
456
- A component that automatically switches between a mobile card layout and a desktop table layout based on screen size.
457
-
458
- ```vue
459
- <script setup>
460
- import { ref } from 'vue'
461
- import { ResponsiveList } from 'cisse-vue-ui'
462
-
463
- const columns = [
464
- { key: 'name', label: 'Name' },
465
- { key: 'email', label: 'Email' },
466
- { key: 'status', label: 'Status' }
467
- ]
468
-
469
- const items = [
470
- { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
471
- { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' }
472
- ]
473
-
474
- const selectedItems = ref(new Set())
475
-
476
- const toggleSelect = (id) => {
477
- if (selectedItems.value.has(id)) {
478
- selectedItems.value.delete(id)
479
- } else {
480
- selectedItems.value.add(id)
481
- }
482
- }
483
-
484
- const toggleSelectAll = () => {
485
- if (selectedItems.value.size === items.length) {
486
- selectedItems.value.clear()
487
- } else {
488
- items.forEach(item => selectedItems.value.add(String(item.id)))
489
- }
490
- }
491
- </script>
492
-
493
- <template>
494
- <ResponsiveList
495
- :items="items"
496
- :columns="columns"
497
- key-field="id"
498
- selectable
499
- :selected-items="selectedItems"
500
- breakpoint="lg"
501
- @select="toggleSelect"
502
- @select-all="toggleSelectAll"
503
- >
504
- <!-- Mobile view: avatar -->
505
- <template #avatar="{ item }">
506
- <div class="w-10 h-10 rounded-full bg-primary-500 flex items-center justify-center text-white">
507
- {{ item.name[0] }}
508
- </div>
509
- </template>
510
-
511
- <!-- Mobile view: content -->
512
- <template #mobileContent="{ item }">
513
- <h3 class="font-semibold">{{ item.name }}</h3>
514
- <p class="text-sm text-gray-500">{{ item.email }}</p>
515
- </template>
516
-
517
- <!-- Mobile view: actions -->
518
- <template #mobileActions="{ item }">
519
- <button @click="viewItem(item)">View</button>
520
- </template>
521
-
522
- <!-- Desktop table: custom cell rendering -->
523
- <template #cell-name="{ item }">
524
- <span class="font-medium">{{ item.name }}</span>
525
- </template>
526
-
527
- <template #cell-status="{ item }">
528
- <span :class="item.status === 'active' ? 'text-green-600' : 'text-red-600'">
529
- {{ item.status }}
530
- </span>
531
- </template>
532
-
533
- <!-- Desktop table: actions column -->
534
- <template #actions="{ item }">
535
- <Button size="sm" variant="ghost" @click="edit(item)">Edit</Button>
536
- </template>
537
-
538
- <!-- Empty state -->
539
- <template #empty>
540
- <EmptyState title="No items" message="No items to display" />
541
- </template>
542
- </ResponsiveList>
543
- </template>
544
- ```
545
-
546
- #### ResponsiveList Props
547
-
548
- | Prop | Type | Default | Description |
549
- |------|------|---------|-------------|
550
- | `items` | `Array` | required | Array of items to display |
551
- | `columns` | `Array` | required | Column definitions with `key` or `name`, `label`, and optional `type` |
552
- | `keyField` | `string` | `'id'` | Field to use as unique key for items |
553
- | `selectable` | `boolean` | `false` | Enable selection mode |
554
- | `selectedItems` | `Set<string>` | - | Set of selected item keys |
555
- | `selectableFilter` | `Function` | - | Filter function to determine if an item is selectable |
556
- | `breakpoint` | `string` | `'lg'` | Breakpoint for switching views: `'sm'`, `'md'`, `'lg'`, `'xl'`, `'2xl'` |
557
-
558
- ### MobileList
559
-
560
- A mobile-optimized card-based list component with selection support.
561
-
562
- ```vue
563
- <script setup>
564
- import { MobileList } from 'cisse-vue-ui'
565
- </script>
566
-
567
- <template>
568
- <MobileList
569
- :items="items"
570
- key-field="id"
571
- selectable
572
- :selected-items="selectedItems"
573
- @select="toggleSelect"
574
- @select-all="toggleSelectAll"
575
- >
576
- <template #avatar="{ item }">
577
- <div class="w-12 h-12 rounded-full bg-blue-500" />
578
- </template>
579
-
580
- <template #content="{ item }">
581
- <h3>{{ item.name }}</h3>
582
- <p>{{ item.description }}</p>
583
- </template>
584
-
585
- <template #actions="{ item }">
586
- <button>View</button>
587
- </template>
588
- </MobileList>
589
- </template>
590
- ```
591
-
592
- ### MenuItem
593
-
594
- ```vue
595
- <script setup>
596
- import { useRoute } from 'vue-router'
597
- import { MenuItem } from 'cisse-vue-ui'
598
-
599
- const route = useRoute()
600
-
601
- const menuItem = {
602
- label: 'Dashboard',
603
- link: '/dashboard',
604
- icon: 'lucide:layout-dashboard'
605
- }
606
- </script>
607
-
608
- <template>
609
- <!-- Auto-detect active state from current route -->
610
- <MenuItem :menu-item="menuItem" :current-path="route.path" />
611
-
612
- <!-- Or manually control active state -->
613
- <MenuItem :menu-item="menuItem" :active="true" />
614
- </template>
615
- ```
616
-
617
- ### BaseLayout
618
-
619
- ```vue
620
- <script setup>
621
- import { useRoute } from 'vue-router'
622
- import { BaseLayout } from 'cisse-vue-ui'
623
-
624
- const route = useRoute()
625
-
626
- const menuItems = [
627
- { label: 'Dashboard', link: '/', icon: 'lucide:home' },
628
- { label: 'Users', link: '/users', icon: 'lucide:users' },
629
- { label: 'Settings', link: '/settings', icon: 'lucide:settings' }
630
- ]
631
- </script>
632
-
633
- <template>
634
- <BaseLayout
635
- :menu-items="menuItems"
636
- :current-path="route.path"
637
- :show-dark-toggle="true"
638
- >
639
- <template #logo>
640
- <img src="/logo.svg" alt="Logo" class="h-8" />
641
- </template>
642
-
643
- <RouterView />
644
- </BaseLayout>
645
- </template>
646
- ```
647
-
648
- ## Dark Mode
649
-
650
- Components support dark mode via the `.dark` class on a parent element:
651
-
652
- ```html
653
- <html class="dark">
654
- <!-- Components will use dark theme -->
655
- </html>
656
- ```
657
-
658
- Use the `useDarkMode` composable or implement your own toggle:
659
-
660
- ```typescript
661
- const { isDark, toggle } = useDarkMode()
662
- ```
663
-
664
- ## License
665
-
666
- 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
+
111
+ ### Form
112
+
113
+ | Component | Description |
114
+ |-----------|-------------|
115
+ | `FormInput` | Text input with label, validation, and icons |
116
+ | `FormSelect` | Select dropdown with label and validation |
117
+ | `FormGroup` | Form field wrapper with label and help text |
118
+ | `FormLabel` | Styled form label |
119
+ | `FormHelp` | Help text for form fields |
120
+ | `SearchInput` | Search input with icon and clear button |
121
+ | `Switch` | Toggle switch with label and description |
122
+ | `Checkbox` | Checkbox with label and description |
123
+
124
+ ### Feedback
125
+
126
+ | Component | Description |
127
+ |-----------|-------------|
128
+ | `Modal` | Modal dialog with header, body, footer slots |
129
+ | `Alert` | Alert banner with variants (info, success, warning, error) |
130
+ | `LoadingSpinner` | Loading indicator with size variants |
131
+ | `PaginationControls` | Pagination with page numbers and navigation |
132
+ | `NotificationList` | Toast notification container |
133
+ | `NotificationComponent` | Individual toast notification |
134
+ | `EmptyState` | Placeholder for empty content with icon and action slot |
135
+
136
+ ### Layout
137
+
138
+ | Component | Description |
139
+ |-----------|-------------|
140
+ | `BaseLayout` | App shell with sidebar, header, main content area, and route-aware menu |
141
+ | `PageLayout` | Page wrapper with breadcrumbs |
142
+
143
+ ### Type Display
144
+
145
+ | Component | Description |
146
+ |-----------|-------------|
147
+ | `TextType` | Text value display |
148
+ | `NumberType` | Formatted number display |
149
+ | `DateType` | Formatted date display |
150
+ | `BooleanType` | Boolean value display (check/cross icons) |
151
+ | `BadgeType` | Badge value display with colors |
152
+
153
+ ## Composables
154
+
155
+ ```typescript
156
+ import { useNotifications, useDarkMode, useExportCSV, useDropdown, useModal } from 'cisse-vue-ui/composables'
157
+ ```
158
+
159
+ ### useModal
160
+
161
+ Manage modal state with data support:
162
+
163
+ ```typescript
164
+ import { useModal } from 'cisse-vue-ui/composables'
165
+
166
+ // Simple modal
167
+ const createModal = useModal()
168
+ createModal.open()
169
+ createModal.close()
170
+
171
+ // Modal with data (e.g., for editing)
172
+ const editModal = useModal<User>()
173
+ editModal.open(selectedUser)
174
+ // Access editModal.data.value in template
175
+
176
+ // With callbacks
177
+ const deleteModal = useModal<Item>({
178
+ onOpen: (data) => console.log('Opening with:', data),
179
+ onClose: () => refetchData()
180
+ })
181
+ ```
182
+
183
+ ```vue
184
+ <template>
185
+ <!-- Use isOpen for v-model binding -->
186
+ <Modal v-model="editModal.isOpen.value" title="Edit User">
187
+ <FormInput v-model="editModal.data.value.name" label="Name" />
188
+ <template #footer>
189
+ <Button @click="editModal.close()">Cancel</Button>
190
+ <Button variant="primary" @click="save">Save</Button>
191
+ </template>
192
+ </Modal>
193
+ </template>
194
+ ```
195
+
196
+ ### useDropdown
197
+
198
+ Shared dropdown logic for custom dropdown components (used internally by Dropdown, FormSelect, AutocompleteComponent):
199
+
200
+ ```typescript
201
+ import { useDropdown } from 'cisse-vue-ui/composables'
202
+ import { ref } from 'vue'
203
+
204
+ const triggerRef = ref<HTMLElement>()
205
+ const dropdownRef = ref<HTMLElement>()
206
+
207
+ const {
208
+ isOpen,
209
+ highlightedIndex,
210
+ dropdownStyle,
211
+ open,
212
+ close,
213
+ toggle,
214
+ handleKeydown,
215
+ scrollToHighlighted,
216
+ } = useDropdown(triggerRef, dropdownRef, {
217
+ teleport: true,
218
+ align: 'left',
219
+ gap: 8,
220
+ onOpen: () => console.log('Opened'),
221
+ onClose: () => console.log('Closed'),
222
+ })
223
+ ```
224
+
225
+ ### useNotifications
226
+
227
+ ```typescript
228
+ const { notifications, addNotification, removeNotification } = useNotifications()
229
+
230
+ addNotification({
231
+ type: 'success',
232
+ title: 'Saved',
233
+ message: 'Your changes have been saved.'
234
+ })
235
+ ```
236
+
237
+ ### useDarkMode
238
+
239
+ ```typescript
240
+ const { isDark, toggle, enable, disable } = useDarkMode({
241
+ selector: 'html', // Element to add .dark class
242
+ storageKey: 'theme', // localStorage key
243
+ defaultDark: false // Default state
244
+ })
245
+ ```
246
+
247
+ ### useExportCSV
248
+
249
+ ```typescript
250
+ const { exportToCSV } = useExportCSV()
251
+
252
+ exportToCSV(data, columns, 'export.csv')
253
+ ```
254
+
255
+ ## Types
256
+
257
+ ```typescript
258
+ import type { Property, Notification, Breadcrumb } from 'cisse-vue-ui/types'
259
+
260
+ // Table column definition
261
+ const columns: Property[] = [
262
+ { key: 'name', label: 'Name', sortable: true },
263
+ { key: 'email', label: 'Email' },
264
+ { key: 'status', label: 'Status', type: 'badge' }
265
+ ]
266
+
267
+ // Breadcrumb navigation
268
+ const breadcrumbs: Breadcrumb[] = [
269
+ { label: 'Home', to: '/' },
270
+ { label: 'Users', to: '/users' },
271
+ { label: 'Edit' }
272
+ ]
273
+ ```
274
+
275
+ ## Component Examples
276
+
277
+ ### Button
278
+
279
+ ```vue
280
+ <Button variant="primary" size="md" :loading="isLoading">
281
+ Save Changes
282
+ </Button>
283
+
284
+ <Button variant="outline" icon="lucide:plus">
285
+ Add Item
286
+ </Button>
287
+
288
+ <Button variant="danger" icon="lucide:trash">
289
+ Delete
290
+ </Button>
291
+ ```
292
+
293
+ ### Tabs
294
+
295
+ ```vue
296
+ <script setup>
297
+ import { ref } from 'vue'
298
+ import { Tabs, TabPanel } from 'cisse-vue-ui'
299
+
300
+ const activeTab = ref('profile')
301
+ const tabs = [
302
+ { key: 'profile', label: 'Profile' },
303
+ { key: 'settings', label: 'Settings' },
304
+ { key: 'notifications', label: 'Notifications' }
305
+ ]
306
+ </script>
307
+
308
+ <template>
309
+ <Tabs v-model="activeTab" :tabs="tabs" variant="underline">
310
+ <TabPanel value="profile">Profile content</TabPanel>
311
+ <TabPanel value="settings">Settings content</TabPanel>
312
+ <TabPanel value="notifications">Notifications content</TabPanel>
313
+ </Tabs>
314
+ </template>
315
+ ```
316
+
317
+ ### Switch
318
+
319
+ ```vue
320
+ <Switch
321
+ v-model="emailNotifications"
322
+ label="Email notifications"
323
+ description="Receive email updates about your account"
324
+ />
325
+ ```
326
+
327
+ ### Alert
328
+
329
+ ```vue
330
+ <Alert variant="success" title="Success!" dismissible>
331
+ Your changes have been saved successfully.
332
+ </Alert>
333
+
334
+ <Alert variant="error" title="Error">
335
+ Something went wrong. Please try again.
336
+ </Alert>
337
+ ```
338
+
339
+ ### Dropdown
340
+
341
+ ```vue
342
+ <script setup>
343
+ import { Dropdown } from 'cisse-vue-ui'
344
+
345
+ const items = [
346
+ { key: 'edit', label: 'Edit', icon: 'lucide:edit' },
347
+ { key: 'duplicate', label: 'Duplicate', icon: 'lucide:copy' },
348
+ { key: 'divider', divider: true },
349
+ { key: 'delete', label: 'Delete', icon: 'lucide:trash', danger: true }
350
+ ]
351
+
352
+ const handleSelect = (item) => {
353
+ console.log('Selected:', item.key)
354
+ }
355
+ </script>
356
+
357
+ <template>
358
+ <Dropdown :items="items" @select="handleSelect">
359
+ <template #trigger-label>Actions</template>
360
+ </Dropdown>
361
+ </template>
362
+ ```
363
+
364
+ ### Stepper
365
+
366
+ ```vue
367
+ <script setup>
368
+ import { ref } from 'vue'
369
+ import { Stepper } from 'cisse-vue-ui'
370
+
371
+ const currentStep = ref('step2')
372
+ const steps = [
373
+ { key: 'step1', title: 'Account', description: 'Create account', icon: 'lucide:user' },
374
+ { key: 'step2', title: 'Profile', description: 'Set up profile', icon: 'lucide:settings' },
375
+ { key: 'step3', title: 'Complete', description: 'Ready to go!', icon: 'lucide:check' }
376
+ ]
377
+ </script>
378
+
379
+ <template>
380
+ <Stepper v-model="currentStep" :steps="steps" />
381
+ </template>
382
+ ```
383
+
384
+ ### EmptyState
385
+
386
+ ```vue
387
+ <EmptyState
388
+ title="No results found"
389
+ message="Try adjusting your search or filters"
390
+ icon="lucide:search-x"
391
+ >
392
+ <template #action>
393
+ <Button variant="primary" size="sm">Clear filters</Button>
394
+ </template>
395
+ </EmptyState>
396
+ ```
397
+
398
+ ### Checkbox
399
+
400
+ ```vue
401
+ <Checkbox
402
+ v-model="accepted"
403
+ label="Accept terms"
404
+ description="I agree to the terms and conditions"
405
+ />
406
+ ```
407
+
408
+ ### TableComponent
409
+
410
+ ```vue
411
+ <script setup>
412
+ import { ref } from 'vue'
413
+ import { TableComponent } from 'cisse-vue-ui'
414
+
415
+ const properties = [
416
+ { name: 'name', label: 'Name', main: true },
417
+ { name: 'email', label: 'Email' },
418
+ { name: 'role', label: 'Role', type: 'badge' }
419
+ ]
420
+
421
+ const items = [
422
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
423
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' }
424
+ ]
425
+
426
+ // Selection support
427
+ const selectedItems = ref(new Set())
428
+ const toggleSelect = (id) => {
429
+ if (selectedItems.value.has(id)) {
430
+ selectedItems.value.delete(id)
431
+ } else {
432
+ selectedItems.value.add(id)
433
+ }
434
+ }
435
+ </script>
436
+
437
+ <template>
438
+ <TableComponent
439
+ :properties="properties"
440
+ :items="items"
441
+ selectable
442
+ :selected-items="selectedItems"
443
+ @select="toggleSelect"
444
+ @select-all="toggleSelectAll"
445
+ >
446
+ <template #action="{ item }">
447
+ <TableAction icon="lucide:edit" @click="edit(item)" />
448
+ <TableAction icon="lucide:trash" variant="danger" @click="delete(item)" />
449
+ </template>
450
+ </TableComponent>
451
+ </template>
452
+ ```
453
+
454
+ ### ResponsiveList
455
+
456
+ A component that automatically switches between a mobile card layout and a desktop table layout based on screen size.
457
+
458
+ ```vue
459
+ <script setup>
460
+ import { ref } from 'vue'
461
+ import { ResponsiveList } from 'cisse-vue-ui'
462
+
463
+ const columns = [
464
+ { key: 'name', label: 'Name' },
465
+ { key: 'email', label: 'Email' },
466
+ { key: 'status', label: 'Status' }
467
+ ]
468
+
469
+ const items = [
470
+ { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
471
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' }
472
+ ]
473
+
474
+ const selectedItems = ref(new Set())
475
+
476
+ const toggleSelect = (id) => {
477
+ if (selectedItems.value.has(id)) {
478
+ selectedItems.value.delete(id)
479
+ } else {
480
+ selectedItems.value.add(id)
481
+ }
482
+ }
483
+
484
+ const toggleSelectAll = () => {
485
+ if (selectedItems.value.size === items.length) {
486
+ selectedItems.value.clear()
487
+ } else {
488
+ items.forEach(item => selectedItems.value.add(String(item.id)))
489
+ }
490
+ }
491
+ </script>
492
+
493
+ <template>
494
+ <ResponsiveList
495
+ :items="items"
496
+ :columns="columns"
497
+ key-field="id"
498
+ selectable
499
+ :selected-items="selectedItems"
500
+ breakpoint="lg"
501
+ @select="toggleSelect"
502
+ @select-all="toggleSelectAll"
503
+ >
504
+ <!-- Mobile view: avatar -->
505
+ <template #avatar="{ item }">
506
+ <div class="w-10 h-10 rounded-full bg-primary-500 flex items-center justify-center text-white">
507
+ {{ item.name[0] }}
508
+ </div>
509
+ </template>
510
+
511
+ <!-- Mobile view: content -->
512
+ <template #mobileContent="{ item }">
513
+ <h3 class="font-semibold">{{ item.name }}</h3>
514
+ <p class="text-sm text-gray-500">{{ item.email }}</p>
515
+ </template>
516
+
517
+ <!-- Mobile view: actions -->
518
+ <template #mobileActions="{ item }">
519
+ <button @click="viewItem(item)">View</button>
520
+ </template>
521
+
522
+ <!-- Desktop table: custom cell rendering -->
523
+ <template #cell-name="{ item }">
524
+ <span class="font-medium">{{ item.name }}</span>
525
+ </template>
526
+
527
+ <template #cell-status="{ item }">
528
+ <span :class="item.status === 'active' ? 'text-green-600' : 'text-red-600'">
529
+ {{ item.status }}
530
+ </span>
531
+ </template>
532
+
533
+ <!-- Desktop table: actions column -->
534
+ <template #actions="{ item }">
535
+ <Button size="sm" variant="ghost" @click="edit(item)">Edit</Button>
536
+ </template>
537
+
538
+ <!-- Empty state -->
539
+ <template #empty>
540
+ <EmptyState title="No items" message="No items to display" />
541
+ </template>
542
+ </ResponsiveList>
543
+ </template>
544
+ ```
545
+
546
+ #### ResponsiveList Props
547
+
548
+ | Prop | Type | Default | Description |
549
+ |------|------|---------|-------------|
550
+ | `items` | `Array` | required | Array of items to display |
551
+ | `columns` | `Array` | required | Column definitions with `key` or `name`, `label`, and optional `type` |
552
+ | `keyField` | `string` | `'id'` | Field to use as unique key for items |
553
+ | `selectable` | `boolean` | `false` | Enable selection mode |
554
+ | `selectedItems` | `Set<string>` | - | Set of selected item keys |
555
+ | `selectableFilter` | `Function` | - | Filter function to determine if an item is selectable |
556
+ | `breakpoint` | `string` | `'lg'` | Breakpoint for switching views: `'sm'`, `'md'`, `'lg'`, `'xl'`, `'2xl'` |
557
+
558
+ ### MobileList
559
+
560
+ A mobile-optimized card-based list component with selection support.
561
+
562
+ ```vue
563
+ <script setup>
564
+ import { MobileList } from 'cisse-vue-ui'
565
+ </script>
566
+
567
+ <template>
568
+ <MobileList
569
+ :items="items"
570
+ key-field="id"
571
+ selectable
572
+ :selected-items="selectedItems"
573
+ @select="toggleSelect"
574
+ @select-all="toggleSelectAll"
575
+ >
576
+ <template #avatar="{ item }">
577
+ <div class="w-12 h-12 rounded-full bg-blue-500" />
578
+ </template>
579
+
580
+ <template #content="{ item }">
581
+ <h3>{{ item.name }}</h3>
582
+ <p>{{ item.description }}</p>
583
+ </template>
584
+
585
+ <template #actions="{ item }">
586
+ <button>View</button>
587
+ </template>
588
+ </MobileList>
589
+ </template>
590
+ ```
591
+
592
+ ### MenuItem
593
+
594
+ ```vue
595
+ <script setup>
596
+ import { useRoute } from 'vue-router'
597
+ import { MenuItem } from 'cisse-vue-ui'
598
+
599
+ const route = useRoute()
600
+
601
+ const menuItem = {
602
+ label: 'Dashboard',
603
+ link: '/dashboard',
604
+ icon: 'lucide:layout-dashboard'
605
+ }
606
+ </script>
607
+
608
+ <template>
609
+ <!-- Auto-detect active state from current route -->
610
+ <MenuItem :menu-item="menuItem" :current-path="route.path" />
611
+
612
+ <!-- Or manually control active state -->
613
+ <MenuItem :menu-item="menuItem" :active="true" />
614
+ </template>
615
+ ```
616
+
617
+ ### BaseLayout
618
+
619
+ ```vue
620
+ <script setup>
621
+ import { useRoute } from 'vue-router'
622
+ import { BaseLayout } from 'cisse-vue-ui'
623
+
624
+ const route = useRoute()
625
+
626
+ const menuItems = [
627
+ { label: 'Dashboard', link: '/', icon: 'lucide:home' },
628
+ { label: 'Users', link: '/users', icon: 'lucide:users' },
629
+ { label: 'Settings', link: '/settings', icon: 'lucide:settings' }
630
+ ]
631
+ </script>
632
+
633
+ <template>
634
+ <BaseLayout
635
+ :menu-items="menuItems"
636
+ :current-path="route.path"
637
+ :show-dark-toggle="true"
638
+ >
639
+ <template #logo>
640
+ <img src="/logo.svg" alt="Logo" class="h-8" />
641
+ </template>
642
+
643
+ <RouterView />
644
+ </BaseLayout>
645
+ </template>
646
+ ```
647
+
648
+ ## Dark Mode
649
+
650
+ Components support dark mode via the `.dark` class on a parent element:
651
+
652
+ ```html
653
+ <html class="dark">
654
+ <!-- Components will use dark theme -->
655
+ </html>
656
+ ```
657
+
658
+ Use the `useDarkMode` composable or implement your own toggle:
659
+
660
+ ```typescript
661
+ const { isDark, toggle } = useDarkMode()
662
+ ```
663
+
664
+ ## License
665
+
666
+ MIT