@htlkg/components 0.0.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 (79) hide show
  1. package/dist/composables/index.js +388 -0
  2. package/dist/composables/index.js.map +1 -0
  3. package/package.json +41 -0
  4. package/src/composables/index.ts +6 -0
  5. package/src/composables/useForm.test.ts +229 -0
  6. package/src/composables/useForm.ts +130 -0
  7. package/src/composables/useFormValidation.test.ts +189 -0
  8. package/src/composables/useFormValidation.ts +83 -0
  9. package/src/composables/useModal.property.test.ts +164 -0
  10. package/src/composables/useModal.ts +43 -0
  11. package/src/composables/useNotifications.test.ts +166 -0
  12. package/src/composables/useNotifications.ts +81 -0
  13. package/src/composables/useTable.property.test.ts +198 -0
  14. package/src/composables/useTable.ts +134 -0
  15. package/src/composables/useTabs.property.test.ts +247 -0
  16. package/src/composables/useTabs.ts +101 -0
  17. package/src/data/Chart.demo.vue +340 -0
  18. package/src/data/Chart.md +525 -0
  19. package/src/data/Chart.vue +133 -0
  20. package/src/data/DataList.md +80 -0
  21. package/src/data/DataList.test.ts +69 -0
  22. package/src/data/DataList.vue +46 -0
  23. package/src/data/SearchableSelect.md +107 -0
  24. package/src/data/SearchableSelect.vue +124 -0
  25. package/src/data/Table.demo.vue +296 -0
  26. package/src/data/Table.md +588 -0
  27. package/src/data/Table.property.test.ts +548 -0
  28. package/src/data/Table.test.ts +562 -0
  29. package/src/data/Table.unit.test.ts +544 -0
  30. package/src/data/Table.vue +321 -0
  31. package/src/data/index.ts +5 -0
  32. package/src/domain/BrandCard.md +81 -0
  33. package/src/domain/BrandCard.vue +63 -0
  34. package/src/domain/BrandSelector.md +84 -0
  35. package/src/domain/BrandSelector.vue +65 -0
  36. package/src/domain/ProductBadge.md +60 -0
  37. package/src/domain/ProductBadge.vue +47 -0
  38. package/src/domain/UserAvatar.md +84 -0
  39. package/src/domain/UserAvatar.vue +60 -0
  40. package/src/domain/domain-components.property.test.ts +449 -0
  41. package/src/domain/index.ts +4 -0
  42. package/src/forms/DateRange.demo.vue +273 -0
  43. package/src/forms/DateRange.md +337 -0
  44. package/src/forms/DateRange.vue +110 -0
  45. package/src/forms/JsonSchemaForm.demo.vue +549 -0
  46. package/src/forms/JsonSchemaForm.md +112 -0
  47. package/src/forms/JsonSchemaForm.property.test.ts +817 -0
  48. package/src/forms/JsonSchemaForm.test.ts +601 -0
  49. package/src/forms/JsonSchemaForm.unit.test.ts +801 -0
  50. package/src/forms/JsonSchemaForm.vue +615 -0
  51. package/src/forms/index.ts +3 -0
  52. package/src/index.ts +17 -0
  53. package/src/navigation/Breadcrumbs.demo.vue +142 -0
  54. package/src/navigation/Breadcrumbs.md +102 -0
  55. package/src/navigation/Breadcrumbs.test.ts +69 -0
  56. package/src/navigation/Breadcrumbs.vue +58 -0
  57. package/src/navigation/Stepper.demo.vue +337 -0
  58. package/src/navigation/Stepper.md +174 -0
  59. package/src/navigation/Stepper.vue +146 -0
  60. package/src/navigation/Tabs.demo.vue +293 -0
  61. package/src/navigation/Tabs.md +163 -0
  62. package/src/navigation/Tabs.test.ts +176 -0
  63. package/src/navigation/Tabs.vue +104 -0
  64. package/src/navigation/index.ts +5 -0
  65. package/src/overlays/Alert.demo.vue +377 -0
  66. package/src/overlays/Alert.md +248 -0
  67. package/src/overlays/Alert.test.ts +166 -0
  68. package/src/overlays/Alert.vue +70 -0
  69. package/src/overlays/Drawer.md +140 -0
  70. package/src/overlays/Drawer.test.ts +92 -0
  71. package/src/overlays/Drawer.vue +76 -0
  72. package/src/overlays/Modal.demo.vue +149 -0
  73. package/src/overlays/Modal.md +385 -0
  74. package/src/overlays/Modal.test.ts +128 -0
  75. package/src/overlays/Modal.vue +86 -0
  76. package/src/overlays/Notification.md +150 -0
  77. package/src/overlays/Notification.test.ts +96 -0
  78. package/src/overlays/Notification.vue +58 -0
  79. package/src/overlays/index.ts +4 -0
@@ -0,0 +1,76 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+
4
+ interface Props {
5
+ open?: boolean;
6
+ position?: 'left' | 'right' | 'top' | 'bottom';
7
+ title?: string;
8
+ }
9
+
10
+ const props = withDefaults(defineProps<Props>(), {
11
+ open: false,
12
+ position: 'right',
13
+ title: ''
14
+ });
15
+
16
+ const emit = defineEmits<{
17
+ 'update:open': [value: boolean];
18
+ 'close': [];
19
+ }>();
20
+
21
+ const isOpen = computed({
22
+ get: () => props.open,
23
+ set: (value: boolean) => {
24
+ emit('update:open', value);
25
+ if (!value) emit('close');
26
+ }
27
+ });
28
+
29
+ function close() {
30
+ isOpen.value = false;
31
+ }
32
+
33
+ // Expose methods for parent components
34
+ defineExpose({
35
+ open: () => { isOpen.value = true; },
36
+ close: () => { isOpen.value = false; },
37
+ toggle: () => { isOpen.value = !isOpen.value; }
38
+ });
39
+ </script>
40
+
41
+ <template>
42
+ <!-- Overlay -->
43
+ <div
44
+ v-if="isOpen"
45
+ class="fixed inset-0 bg-black bg-opacity-50 z-50"
46
+ @click="close"
47
+ >
48
+ <!-- Drawer -->
49
+ <div
50
+ class="fixed bg-white shadow-xl z-51"
51
+ :class="{
52
+ 'top-0 right-0 bottom-0 w-96 max-w-[90vw]': position === 'right',
53
+ 'top-0 left-0 bottom-0 w-96 max-w-[90vw]': position === 'left',
54
+ 'top-0 left-0 right-0 h-96 max-h-[90vh]': position === 'top',
55
+ 'bottom-0 left-0 right-0 h-96 max-h-[90vh]': position === 'bottom'
56
+ }"
57
+ @click.stop
58
+ >
59
+ <!-- Header -->
60
+ <div class="flex justify-between items-center p-4 border-b border-gray-200">
61
+ <h3 class="text-lg font-medium">{{ title }}</h3>
62
+ <button
63
+ @click="close"
64
+ class="text-2xl text-gray-500 hover:text-gray-700 bg-transparent border-none cursor-pointer"
65
+ >
66
+ ×
67
+ </button>
68
+ </div>
69
+
70
+ <!-- Content -->
71
+ <div class="p-4 overflow-auto">
72
+ <slot />
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </template>
@@ -0,0 +1,149 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue';
3
+ import { Modal } from '@htlkg/components/overlays';
4
+
5
+ const isOpen = ref(false);
6
+ const isConfirmOpen = ref(false);
7
+ const formOpen = ref(false);
8
+ const formData = ref({
9
+ name: '',
10
+ email: ''
11
+ });
12
+
13
+ const openModal = () => {
14
+ isOpen.value = true;
15
+ };
16
+
17
+ const closeModal = () => {
18
+ isOpen.value = false;
19
+ };
20
+
21
+ const openConfirm = () => {
22
+ isConfirmOpen.value = true;
23
+ };
24
+
25
+ const handleConfirm = () => {
26
+ alert('Confirmed!');
27
+ isConfirmOpen.value = false;
28
+ };
29
+
30
+ const openForm = () => {
31
+ formOpen.value = true;
32
+ };
33
+
34
+ const handleSubmit = () => {
35
+ alert(`Submitted: ${formData.value.name}, ${formData.value.email}`);
36
+ formOpen.value = false;
37
+ formData.value = { name: '', email: '' };
38
+ };
39
+ </script>
40
+
41
+ <template>
42
+ <div class="space-y-4">
43
+ <div>
44
+ <button
45
+ @click="openModal"
46
+ class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
47
+ >
48
+ Open Basic Modal
49
+ </button>
50
+ </div>
51
+
52
+ <div>
53
+ <button
54
+ @click="openConfirm"
55
+ class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 transition-colors"
56
+ >
57
+ Open Confirmation Modal
58
+ </button>
59
+ </div>
60
+
61
+ <div>
62
+ <button
63
+ @click="openForm"
64
+ class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 transition-colors"
65
+ >
66
+ Open Form Modal
67
+ </button>
68
+ </div>
69
+
70
+ <!-- Basic Modal -->
71
+ <Modal v-model:open="isOpen" title="Basic Modal">
72
+ <div class="p-4">
73
+ <p class="text-gray-700">This is a basic modal with some content.</p>
74
+ <p class="text-gray-700 mt-2">You can close it by clicking the X button or outside the modal.</p>
75
+ </div>
76
+ <template #footer>
77
+ <button
78
+ @click="closeModal"
79
+ class="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 transition-colors"
80
+ >
81
+ Close
82
+ </button>
83
+ </template>
84
+ </Modal>
85
+
86
+ <!-- Confirmation Modal -->
87
+ <Modal v-model:open="isConfirmOpen" title="Confirm Action">
88
+ <div class="p-4">
89
+ <p class="text-gray-700">Are you sure you want to proceed with this action?</p>
90
+ </div>
91
+ <template #footer>
92
+ <div class="flex gap-2">
93
+ <button
94
+ @click="isConfirmOpen = false"
95
+ class="px-4 py-2 bg-gray-300 text-gray-700 rounded hover:bg-gray-400 transition-colors"
96
+ >
97
+ Cancel
98
+ </button>
99
+ <button
100
+ @click="handleConfirm"
101
+ class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 transition-colors"
102
+ >
103
+ Confirm
104
+ </button>
105
+ </div>
106
+ </template>
107
+ </Modal>
108
+
109
+ <!-- Form Modal -->
110
+ <Modal v-model:open="formOpen" title="User Form">
111
+ <div class="p-4 space-y-4">
112
+ <div>
113
+ <label class="block text-sm font-medium text-gray-700 mb-1">Name</label>
114
+ <input
115
+ v-model="formData.name"
116
+ type="text"
117
+ class="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-green-500 focus:border-transparent"
118
+ placeholder="Enter name"
119
+ />
120
+ </div>
121
+ <div>
122
+ <label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
123
+ <input
124
+ v-model="formData.email"
125
+ type="email"
126
+ class="w-full px-3 py-2 border border-gray-300 rounded focus:ring-2 focus:ring-green-500 focus:border-transparent"
127
+ placeholder="Enter email"
128
+ />
129
+ </div>
130
+ </div>
131
+ <template #footer>
132
+ <div class="flex gap-2">
133
+ <button
134
+ @click="formOpen = false"
135
+ class="px-4 py-2 bg-gray-300 text-gray-700 rounded hover:bg-gray-400 transition-colors"
136
+ >
137
+ Cancel
138
+ </button>
139
+ <button
140
+ @click="handleSubmit"
141
+ class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 transition-colors"
142
+ >
143
+ Submit
144
+ </button>
145
+ </div>
146
+ </template>
147
+ </Modal>
148
+ </div>
149
+ </template>
@@ -0,0 +1,385 @@
1
+ # Modal Component
2
+
3
+ A stateful Vue wrapper around `@hotelinking/ui`'s `uiModal` component. Provides dialog overlays for confirmations, forms, and content display.
4
+
5
+ ## Features
6
+
7
+ - **v-model support**: Two-way binding for open/close state
8
+ - **Action buttons**: Configurable action buttons with custom events
9
+ - **Three sizes**: Small, medium, and large modal sizes
10
+ - **Slot support**: Custom header, content, and footer
11
+ - **Exposed methods**: Programmatic open/close/toggle control
12
+ - **Auto-close**: Closes on action, X button, or outside click
13
+
14
+ ## Import
15
+
16
+ ```typescript
17
+ import { Modal } from '@htlkg/components';
18
+ // or
19
+ import { Modal } from '@htlkg/components/overlays';
20
+ ```
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `open` | `boolean` | `false` | Control visibility (v-model) |
27
+ | `title` | `string` | `''` | Modal title |
28
+ | `content` | `string` | `''` | Modal content text |
29
+ | `modalName` | `string` | `'modal'` | Unique modal identifier |
30
+ | `actions` | `Array<{name: string, action: string, type?: string}>` | `[]` | Action buttons |
31
+ | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | Modal size |
32
+
33
+ ## Events
34
+
35
+ | Event | Payload | Description |
36
+ |-------|---------|-------------|
37
+ | `update:open` | `boolean` | Emitted when visibility changes (v-model) |
38
+ | `close` | - | Emitted when modal is closed |
39
+ | `action` | `{modal: string, action: string}` | Emitted when action button is clicked |
40
+
41
+ ## Slots
42
+
43
+ | Slot | Description |
44
+ |------|-------------|
45
+ | `default` | Modal content (replaces `content` prop) |
46
+ | `header` | Custom header content |
47
+ | `footer` | Custom footer content |
48
+
49
+ ## Exposed Methods
50
+
51
+ | Method | Description |
52
+ |--------|-------------|
53
+ | `open()` | Open the modal |
54
+ | `close()` | Close the modal |
55
+ | `toggle()` | Toggle modal visibility |
56
+
57
+ ## Usage Examples
58
+
59
+ ### Basic Modal
60
+
61
+ ```vue
62
+ <script setup>
63
+ import { ref } from 'vue';
64
+ import { Modal } from '@htlkg/components';
65
+
66
+ const isOpen = ref(false);
67
+ </script>
68
+
69
+ <template>
70
+ <button @click="isOpen = true">Open Modal</button>
71
+
72
+ <Modal
73
+ v-model:open="isOpen"
74
+ title="Welcome"
75
+ content="This is a basic modal dialog."
76
+ />
77
+ </template>
78
+ ```
79
+
80
+ ### Confirmation Modal
81
+
82
+ ```vue
83
+ <script setup>
84
+ import { ref } from 'vue';
85
+ import { Modal } from '@htlkg/components';
86
+
87
+ const showConfirm = ref(false);
88
+
89
+ const actions = [
90
+ { name: 'Confirm', action: 'confirm', type: 'primary' },
91
+ { name: 'Cancel', action: 'cancel', type: 'secondary' }
92
+ ];
93
+
94
+ const handleAction = (data) => {
95
+ if (data.action === 'confirm') {
96
+ // Proceed with action
97
+ console.log('Confirmed');
98
+ }
99
+ // Modal auto-closes on any action
100
+ };
101
+ </script>
102
+
103
+ <template>
104
+ <Modal
105
+ v-model:open="showConfirm"
106
+ title="Confirm Deletion"
107
+ content="Are you sure you want to delete this item? This action cannot be undone."
108
+ :actions="actions"
109
+ @action="handleAction"
110
+ />
111
+ </template>
112
+ ```
113
+
114
+ ### Modal with Custom Content
115
+
116
+ ```vue
117
+ <script setup>
118
+ import { ref } from 'vue';
119
+ import { Modal } from '@htlkg/components';
120
+
121
+ const isOpen = ref(false);
122
+ </script>
123
+
124
+ <template>
125
+ <Modal
126
+ v-model:open="isOpen"
127
+ title="User Details"
128
+ >
129
+ <div class="space-y-4">
130
+ <div>
131
+ <label class="font-medium">Name:</label>
132
+ <p>John Doe</p>
133
+ </div>
134
+ <div>
135
+ <label class="font-medium">Email:</label>
136
+ <p>john@example.com</p>
137
+ </div>
138
+ <div>
139
+ <label class="font-medium">Role:</label>
140
+ <p>Administrator</p>
141
+ </div>
142
+ </div>
143
+ </Modal>
144
+ </template>
145
+ ```
146
+
147
+ ### Form Modal
148
+
149
+ ```vue
150
+ <script setup>
151
+ import { ref } from 'vue';
152
+ import { Modal } from '@htlkg/components';
153
+
154
+ const isOpen = ref(false);
155
+ const formData = ref({ name: '', email: '' });
156
+
157
+ const actions = [
158
+ { name: 'Save', action: 'save', type: 'primary' },
159
+ { name: 'Cancel', action: 'cancel', type: 'secondary' }
160
+ ];
161
+
162
+ const handleAction = (data) => {
163
+ if (data.action === 'save') {
164
+ // Save form data
165
+ console.log('Saving:', formData.value);
166
+ }
167
+ };
168
+ </script>
169
+
170
+ <template>
171
+ <Modal
172
+ v-model:open="isOpen"
173
+ title="Create User"
174
+ :actions="actions"
175
+ @action="handleAction"
176
+ >
177
+ <form class="space-y-4">
178
+ <div>
179
+ <label class="block text-sm font-medium mb-1">Name</label>
180
+ <input
181
+ v-model="formData.name"
182
+ type="text"
183
+ class="w-full px-3 py-2 border rounded"
184
+ />
185
+ </div>
186
+ <div>
187
+ <label class="block text-sm font-medium mb-1">Email</label>
188
+ <input
189
+ v-model="formData.email"
190
+ type="email"
191
+ class="w-full px-3 py-2 border rounded"
192
+ />
193
+ </div>
194
+ </form>
195
+ </Modal>
196
+ </template>
197
+ ```
198
+
199
+ ### Modal Sizes
200
+
201
+ ```vue
202
+ <script setup>
203
+ import { ref } from 'vue';
204
+ import { Modal } from '@htlkg/components';
205
+
206
+ const smallModal = ref(false);
207
+ const mediumModal = ref(false);
208
+ const largeModal = ref(false);
209
+ </script>
210
+
211
+ <template>
212
+ <div class="space-x-2">
213
+ <button @click="smallModal = true">Small Modal</button>
214
+ <button @click="mediumModal = true">Medium Modal</button>
215
+ <button @click="largeModal = true">Large Modal</button>
216
+ </div>
217
+
218
+ <Modal
219
+ v-model:open="smallModal"
220
+ title="Small Modal"
221
+ content="This is a small modal."
222
+ size="small"
223
+ />
224
+
225
+ <Modal
226
+ v-model:open="mediumModal"
227
+ title="Medium Modal"
228
+ content="This is a medium modal (default)."
229
+ size="medium"
230
+ />
231
+
232
+ <Modal
233
+ v-model:open="largeModal"
234
+ title="Large Modal"
235
+ content="This is a large modal for more content."
236
+ size="large"
237
+ />
238
+ </template>
239
+ ```
240
+
241
+ ### Custom Header and Footer
242
+
243
+ ```vue
244
+ <script setup>
245
+ import { ref } from 'vue';
246
+ import { Modal } from '@htlkg/components';
247
+
248
+ const isOpen = ref(false);
249
+ </script>
250
+
251
+ <template>
252
+ <Modal v-model:open="isOpen">
253
+ <template #header>
254
+ <div class="flex items-center gap-2">
255
+ <span class="text-2xl">🎉</span>
256
+ <h2 class="text-xl font-bold">Congratulations!</h2>
257
+ </div>
258
+ </template>
259
+
260
+ <p>You've successfully completed the setup process.</p>
261
+
262
+ <template #footer>
263
+ <div class="flex justify-end gap-2">
264
+ <button @click="isOpen = false" class="btn btn-secondary">
265
+ Close
266
+ </button>
267
+ <button @click="handleNext" class="btn btn-primary">
268
+ Next Step
269
+ </button>
270
+ </div>
271
+ </template>
272
+ </Modal>
273
+ </template>
274
+ ```
275
+
276
+ ### Programmatic Control
277
+
278
+ ```vue
279
+ <script setup>
280
+ import { ref } from 'vue';
281
+ import { Modal } from '@htlkg/components';
282
+
283
+ const modalRef = ref();
284
+
285
+ const openModal = () => modalRef.value?.open();
286
+ const closeModal = () => modalRef.value?.close();
287
+ const toggleModal = () => modalRef.value?.toggle();
288
+ </script>
289
+
290
+ <template>
291
+ <div class="space-x-2">
292
+ <button @click="openModal">Open</button>
293
+ <button @click="closeModal">Close</button>
294
+ <button @click="toggleModal">Toggle</button>
295
+ </div>
296
+
297
+ <Modal
298
+ ref="modalRef"
299
+ title="Controlled Modal"
300
+ content="This modal is controlled programmatically."
301
+ />
302
+ </template>
303
+ ```
304
+
305
+ ### Nested Modals
306
+
307
+ ```vue
308
+ <script setup>
309
+ import { ref } from 'vue';
310
+ import { Modal } from '@htlkg/components';
311
+
312
+ const firstModal = ref(false);
313
+ const secondModal = ref(false);
314
+ </script>
315
+
316
+ <template>
317
+ <button @click="firstModal = true">Open First Modal</button>
318
+
319
+ <Modal
320
+ v-model:open="firstModal"
321
+ title="First Modal"
322
+ modalName="first-modal"
323
+ >
324
+ <p>This is the first modal.</p>
325
+ <button @click="secondModal = true" class="btn btn-primary mt-4">
326
+ Open Second Modal
327
+ </button>
328
+ </Modal>
329
+
330
+ <Modal
331
+ v-model:open="secondModal"
332
+ title="Second Modal"
333
+ modalName="second-modal"
334
+ >
335
+ <p>This is a nested modal.</p>
336
+ </Modal>
337
+ </template>
338
+ ```
339
+
340
+ ## Best Practices
341
+
342
+ ### UX Guidelines
343
+
344
+ - Use modals sparingly - they interrupt user flow
345
+ - Provide clear action buttons (primary and secondary)
346
+ - Always include a way to close (X button, Cancel, or outside click)
347
+ - Use appropriate sizes based on content
348
+ - Keep content concise and focused
349
+ - Use confirmation modals for destructive actions
350
+
351
+ ### Accessibility
352
+
353
+ - Modal traps focus when open
354
+ - Escape key closes the modal
355
+ - Focus returns to trigger element on close
356
+ - Use semantic HTML in modal content
357
+ - Provide clear button labels
358
+
359
+ ### Performance
360
+
361
+ - Lazy load modal content when possible
362
+ - Avoid nesting too many modals
363
+ - Clean up event listeners on unmount
364
+ - Use v-if instead of v-show for heavy content
365
+
366
+ ## Design System Integration
367
+
368
+ This component wraps `@hotelinking/ui`'s `uiModal` component, providing:
369
+
370
+ - Reactive state management with Vue 3 Composition API
371
+ - v-model support for open/close state
372
+ - Exposed methods for programmatic control
373
+ - Event handling for actions
374
+ - Slot support for custom content
375
+ - Consistent API with other overlay components
376
+
377
+ ## Related Components
378
+
379
+ - **Alert**: For inline alerts and notifications
380
+ - **Notification**: For toast-style notifications
381
+ - **Drawer**: For side panel overlays
382
+
383
+ ## Demo
384
+
385
+ See the [Modal demo page](/components/modal) for interactive examples.