@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,104 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue';
3
+
4
+ export interface Tab {
5
+ id: string;
6
+ label: string;
7
+ count?: number;
8
+ }
9
+
10
+ interface Props {
11
+ tabs: Tab[];
12
+ modelValue?: string;
13
+ }
14
+
15
+ const props = withDefaults(defineProps<Props>(), {
16
+ modelValue: ''
17
+ });
18
+
19
+ const emit = defineEmits<{
20
+ 'update:modelValue': [tabId: string];
21
+ 'tab-change': [tabId: string];
22
+ }>();
23
+
24
+ // Internal state synced with v-model
25
+ const currentTab = computed({
26
+ get: () => props.modelValue || (props.tabs.length > 0 ? props.tabs[0].id : ''),
27
+ set: (value: string) => {
28
+ emit('update:modelValue', value);
29
+ emit('tab-change', value);
30
+ }
31
+ });
32
+
33
+
34
+
35
+ // Expose methods for parent components
36
+ defineExpose({
37
+ setActiveTab: (tabId: string) => { currentTab.value = tabId; },
38
+ getActiveTab: () => currentTab.value,
39
+ nextTab: () => {
40
+ const currentIndex = props.tabs.findIndex(t => t.id === currentTab.value);
41
+ if (currentIndex < props.tabs.length - 1) {
42
+ currentTab.value = props.tabs[currentIndex + 1].id;
43
+ }
44
+ },
45
+ previousTab: () => {
46
+ const currentIndex = props.tabs.findIndex(t => t.id === currentTab.value);
47
+ if (currentIndex > 0) {
48
+ currentTab.value = props.tabs[currentIndex - 1].id;
49
+ }
50
+ }
51
+ });
52
+ </script>
53
+
54
+ <template>
55
+ <div class="tabs-wrapper">
56
+ <div class="border-b border-gray-200">
57
+ <nav class="-mb-px flex space-x-8" aria-label="Tabs">
58
+ <button
59
+ v-for="tab in tabs"
60
+ :key="tab.id"
61
+ @click="currentTab = tab.id"
62
+ :class="[
63
+ currentTab === tab.id
64
+ ? 'border-blue-500 text-blue-600'
65
+ : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700',
66
+ 'whitespace-nowrap border-b-2 py-4 px-1 text-sm font-medium transition-colors'
67
+ ]"
68
+ :aria-current="currentTab === tab.id ? 'page' : undefined"
69
+ >
70
+ {{ tab.label }}
71
+ <span
72
+ v-if="tab.count !== undefined && tab.count > 0"
73
+ :class="[
74
+ currentTab === tab.id
75
+ ? 'bg-blue-100 text-blue-600'
76
+ : 'bg-gray-100 text-gray-900',
77
+ 'ml-3 hidden rounded-full py-0.5 px-2.5 text-xs font-medium md:inline-block'
78
+ ]"
79
+ >
80
+ {{ tab.count }}
81
+ </span>
82
+ </button>
83
+ </nav>
84
+ </div>
85
+
86
+ <div class="tabs-content mt-4">
87
+ <template v-for="tab in tabs" :key="tab.id">
88
+ <div v-if="currentTab === tab.id">
89
+ <slot :name="tab.id" :active-tab="currentTab" />
90
+ </div>
91
+ </template>
92
+ </div>
93
+ </div>
94
+ </template>
95
+
96
+ <style scoped>
97
+ .tabs-wrapper {
98
+ width: 100%;
99
+ }
100
+
101
+ .tabs-content {
102
+ margin-top: 1rem;
103
+ }
104
+ </style>
@@ -0,0 +1,5 @@
1
+ export { default as Tabs } from './Tabs.vue';
2
+ export type { Tab } from './Tabs.vue';
3
+ export { default as Stepper } from './Stepper.vue';
4
+ export type { Step } from './Stepper.vue';
5
+ export { default as Breadcrumbs } from './Breadcrumbs.vue';
@@ -0,0 +1,377 @@
1
+ <script setup lang="ts">
2
+ import { ref, computed } from 'vue';
3
+ import { Alert } from '@htlkg/components';
4
+
5
+ // State for different alert types
6
+ const showInfo = ref(true);
7
+ const showSuccess = ref(true);
8
+ const showWarning = ref(true);
9
+ const showDanger = ref(true);
10
+ const showWithActions = ref(true);
11
+ const showLoading = ref(true);
12
+
13
+ // Interactive demo state
14
+ const actionLog = ref<string[]>([]);
15
+ const showInteractive = ref(true);
16
+ const interactiveType = ref<'info' | 'success' | 'warning' | 'danger'>('warning');
17
+ const processingAction = ref(false);
18
+ const actionResult = ref<string>('');
19
+
20
+ // Actions for interactive alert
21
+ const actions = [
22
+ { name: 'Confirm', event: 'confirm' },
23
+ { name: 'Cancel', event: 'cancel' }
24
+ ];
25
+
26
+ // Interactive alert actions - changes based on state
27
+ const interactiveActions = computed(() => {
28
+ if (actionResult.value) {
29
+ return [{ name: 'Close', event: 'close' }];
30
+ }
31
+ return [
32
+ { name: 'Approve', event: 'approve' },
33
+ { name: 'Reject', event: 'reject' },
34
+ { name: 'Ask Later', event: 'later' }
35
+ ];
36
+ });
37
+
38
+ // Handle alert events
39
+ const handleAlertEvent = (event: string) => {
40
+ console.log('Alert event:', event);
41
+ if (event === 'confirm') {
42
+ alert('Confirmed!');
43
+ showWithActions.value = false;
44
+ } else if (event === 'cancel') {
45
+ showWithActions.value = false;
46
+ }
47
+ };
48
+
49
+ // Handle interactive alert events
50
+ const handleInteractiveEvent = async (event: string) => {
51
+ console.log('๐Ÿ”ฅ handleInteractiveEvent called with:', event, typeof event);
52
+
53
+ const timestamp = new Date().toLocaleTimeString();
54
+ actionLog.value.unshift(`[${timestamp}] ๐Ÿ–ฑ๏ธ Action clicked: ${event}`);
55
+
56
+ // Handle close action
57
+ if (event === 'close') {
58
+ actionLog.value.unshift(`[${timestamp}] ๐Ÿšช Alert closed`);
59
+ resetInteractive();
60
+ return;
61
+ }
62
+
63
+ processingAction.value = true;
64
+ actionResult.value = '';
65
+
66
+ // Simulate processing
67
+ await new Promise(resolve => setTimeout(resolve, 1500));
68
+
69
+ processingAction.value = false;
70
+
71
+ if (event === 'approve') {
72
+ actionResult.value = 'โœ… Request approved successfully!';
73
+ interactiveType.value = 'success';
74
+ actionLog.value.unshift(`[${timestamp}] โœ… Request approved`);
75
+ } else if (event === 'reject') {
76
+ actionResult.value = 'โŒ Request rejected';
77
+ interactiveType.value = 'danger';
78
+ actionLog.value.unshift(`[${timestamp}] โŒ Request rejected`);
79
+ } else if (event === 'later') {
80
+ actionResult.value = 'โฐ Reminder set for later';
81
+ interactiveType.value = 'info';
82
+ actionLog.value.unshift(`[${timestamp}] โฐ Reminder set`);
83
+ }
84
+
85
+ // Keep only last 15 log entries
86
+ if (actionLog.value.length > 15) {
87
+ actionLog.value = actionLog.value.slice(0, 15);
88
+ }
89
+ };
90
+
91
+ // Reset interactive demo
92
+ const resetInteractive = () => {
93
+ showInteractive.value = true;
94
+ interactiveType.value = 'warning';
95
+ actionResult.value = '';
96
+ actionLog.value = [];
97
+ processingAction.value = false;
98
+ };
99
+
100
+ // Simulate loading
101
+ setTimeout(() => {
102
+ showLoading.value = false;
103
+ }, 2000);
104
+ </script>
105
+
106
+ <template>
107
+ <div class="space-y-8">
108
+ <!-- Info Alert -->
109
+ <section>
110
+ <h2 class="text-2xl font-semibold mb-4">Info Alert</h2>
111
+ <Alert
112
+ v-model:show="showInfo"
113
+ title="System Update Available"
114
+ type="info"
115
+ >
116
+ We've updated our Design System to version 1.0. Review the changes and update your components.
117
+ </Alert>
118
+ <button
119
+ v-if="!showInfo"
120
+ @click="showInfo = true"
121
+ class="mt-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
122
+ >
123
+ Show Info Alert
124
+ </button>
125
+ </section>
126
+
127
+ <!-- Success Alert -->
128
+ <section>
129
+ <h2 class="text-2xl font-semibold mb-4">Success Alert</h2>
130
+ <Alert
131
+ v-model:show="showSuccess"
132
+ title="Operation Completed"
133
+ type="success"
134
+ >
135
+ The reservation has been created successfully and confirmation has been sent to the guest.
136
+ </Alert>
137
+ <button
138
+ v-if="!showSuccess"
139
+ @click="showSuccess = true"
140
+ class="mt-2 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
141
+ >
142
+ Show Success Alert
143
+ </button>
144
+ </section>
145
+
146
+ <!-- Warning Alert -->
147
+ <section>
148
+ <h2 class="text-2xl font-semibold mb-4">Warning Alert</h2>
149
+ <Alert
150
+ v-model:show="showWarning"
151
+ title="Attention Required"
152
+ type="warning"
153
+ >
154
+ The reservation will be permanently cancelled. This action cannot be undone.
155
+ </Alert>
156
+ <button
157
+ v-if="!showWarning"
158
+ @click="showWarning = true"
159
+ class="mt-2 px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600"
160
+ >
161
+ Show Warning Alert
162
+ </button>
163
+ </section>
164
+
165
+ <!-- Danger Alert -->
166
+ <section>
167
+ <h2 class="text-2xl font-semibold mb-4">Danger Alert</h2>
168
+ <Alert
169
+ v-model:show="showDanger"
170
+ title="Error Processing Request"
171
+ type="danger"
172
+ >
173
+ Could not connect to the server. Please try again later.
174
+ </Alert>
175
+ <button
176
+ v-if="!showDanger"
177
+ @click="showDanger = true"
178
+ class="mt-2 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
179
+ >
180
+ Show Danger Alert
181
+ </button>
182
+ </section>
183
+
184
+ <!-- Alert with Actions -->
185
+ <section>
186
+ <h2 class="text-2xl font-semibold mb-4">Alert with Actions</h2>
187
+ <Alert
188
+ v-model:show="showWithActions"
189
+ title="Confirm Deletion"
190
+ type="warning"
191
+ :actions="actions"
192
+ @alertEvent="handleAlertEvent"
193
+ >
194
+ Are you sure you want to delete this item? This action cannot be undone.
195
+ </Alert>
196
+ <button
197
+ v-if="!showWithActions"
198
+ @click="showWithActions = true"
199
+ class="mt-2 px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600"
200
+ >
201
+ Show Alert with Actions
202
+ </button>
203
+ </section>
204
+
205
+ <!-- Interactive Actions Demo -->
206
+ <section class="bg-gradient-to-br from-purple-50 to-blue-50">
207
+ <h2 class="text-2xl font-semibold mb-4">๐ŸŽฎ Interactive Actions Demo</h2>
208
+ <p class="text-gray-600 mb-4">
209
+ Click the action buttons in the alert to see real-time feedback. The alert type, title, content, and available actions all change dynamically!
210
+ </p>
211
+
212
+ <div class="mb-4 p-3 bg-blue-50 border border-blue-200 rounded text-sm">
213
+ <strong>๐Ÿ’ก Try this:</strong> Click "Approve", "Reject", or "Ask Later" and watch the alert transform with different colors, messages, and actions!
214
+ </div>
215
+
216
+ <div class="grid md:grid-cols-2 gap-6">
217
+ <!-- Alert Section -->
218
+ <div>
219
+ <div class="mb-3 flex gap-2 text-xs">
220
+ <span class="px-2 py-1 bg-gray-100 rounded">
221
+ Type: <strong>{{ interactiveType }}</strong>
222
+ </span>
223
+ <span class="px-2 py-1 bg-gray-100 rounded">
224
+ Actions: <strong>{{ interactiveActions.length }}</strong>
225
+ </span>
226
+ <span class="px-2 py-1 bg-gray-100 rounded">
227
+ Loading: <strong>{{ processingAction ? 'Yes' : 'No' }}</strong>
228
+ </span>
229
+ </div>
230
+
231
+ <Alert
232
+ v-model:show="showInteractive"
233
+ :title="processingAction ? 'Processing...' : actionResult || 'Approval Required'"
234
+ :type="interactiveType"
235
+ :actions="interactiveActions"
236
+ :loading="processingAction"
237
+ @alertEvent="handleInteractiveEvent"
238
+ @alert-event="handleInteractiveEvent"
239
+ >
240
+ <div v-if="!actionResult">
241
+ <p class="mb-2">A new request requires your attention:</p>
242
+ <ul class="list-disc list-inside space-y-1 text-sm">
243
+ <li>User: John Doe</li>
244
+ <li>Action: Update pricing</li>
245
+ <li>Priority: High</li>
246
+ </ul>
247
+ </div>
248
+ <div v-else>
249
+ <p class="font-medium">{{ actionResult }}</p>
250
+ <p class="text-sm mt-2 opacity-75">Click "Close" to reset and try again!</p>
251
+ </div>
252
+ </Alert>
253
+
254
+ <div class="mt-4 flex gap-2 flex-wrap">
255
+ <button
256
+ @click="resetInteractive"
257
+ class="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 transition-colors"
258
+ >
259
+ ๐Ÿ”„ Reset Demo
260
+ </button>
261
+ <button
262
+ @click="showInteractive = !showInteractive"
263
+ class="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600 transition-colors"
264
+ >
265
+ {{ showInteractive ? '๐Ÿ‘๏ธ Hide' : '๐Ÿ‘๏ธ Show' }} Alert
266
+ </button>
267
+ <button
268
+ @click="handleInteractiveEvent('approve')"
269
+ class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 transition-colors text-sm"
270
+ >
271
+ ๐Ÿงช Test Approve
272
+ </button>
273
+ </div>
274
+ </div>
275
+
276
+ <!-- Action Log Section -->
277
+ <div>
278
+ <h3 class="text-lg font-semibold mb-2">๐Ÿ“‹ Action Log</h3>
279
+ <div class="bg-gray-900 text-green-400 p-4 rounded font-mono text-sm h-64 overflow-y-auto">
280
+ <div v-if="actionLog.length === 0" class="text-gray-500">
281
+ No actions yet. Click a button to see the log...
282
+ </div>
283
+ <div v-for="(log, index) in actionLog" :key="index" class="mb-1">
284
+ {{ log }}
285
+ </div>
286
+ </div>
287
+ <div class="mt-2 text-sm text-gray-600">
288
+ Total actions: {{ actionLog.length }}
289
+ </div>
290
+ </div>
291
+ </div>
292
+ </section>
293
+
294
+ <!-- Loading Alert -->
295
+ <section>
296
+ <h2 class="text-2xl font-semibold mb-4">Loading Alert</h2>
297
+ <Alert
298
+ v-model:show="showLoading"
299
+ title="Loading Information..."
300
+ type="info"
301
+ :loading="true"
302
+ >
303
+ Preparing system update
304
+ </Alert>
305
+ <p v-if="!showLoading" class="text-gray-600 mt-2">
306
+ Loading complete!
307
+ </p>
308
+ </section>
309
+
310
+ <!-- Rich Content Alert -->
311
+ <section>
312
+ <h2 class="text-2xl font-semibold mb-4">Alert with Rich Content</h2>
313
+ <Alert
314
+ title="New Features Available"
315
+ type="success"
316
+ :actions="[
317
+ { name: 'Explore', event: 'explore' },
318
+ { name: 'Later', event: 'later' }
319
+ ]"
320
+ @alertEvent="(e) => console.log('Rich content alert:', e)"
321
+ >
322
+ <p class="mb-2">
323
+ <strong>Update 1.0:</strong> We've added new features:
324
+ </p>
325
+ <ul class="list-disc list-inside space-y-1">
326
+ <li>Improved pricing calendar</li>
327
+ <li>New notification system</li>
328
+ <li>Performance optimization</li>
329
+ </ul>
330
+ </Alert>
331
+ </section>
332
+
333
+ <!-- Code Example -->
334
+ <section class="mt-12">
335
+ <h2 class="text-2xl font-semibold mb-4">Usage Example</h2>
336
+ <pre class="bg-gray-100 p-4 rounded overflow-x-auto"><code>&lt;script setup&gt;
337
+ import { ref } from 'vue';
338
+ import { Alert } from '@htlkg/components';
339
+
340
+ const showAlert = ref(true);
341
+ const actions = [
342
+ { name: 'Confirm', event: 'confirm' },
343
+ { name: 'Cancel', event: 'cancel' }
344
+ ];
345
+
346
+ const handleAlertEvent = (event) => {
347
+ console.log('Alert event:', event);
348
+ if (event === 'confirm') {
349
+ // Handle confirmation
350
+ showAlert.value = false;
351
+ }
352
+ };
353
+ &lt;/script&gt;
354
+
355
+ &lt;template&gt;
356
+ &lt;Alert
357
+ v-model:show="showAlert"
358
+ title="Confirm Action"
359
+ type="warning"
360
+ :actions="actions"
361
+ @alertEvent="handleAlertEvent"
362
+ &gt;
363
+ Are you sure you want to proceed?
364
+ &lt;/Alert&gt;
365
+ &lt;/template&gt;</code></pre>
366
+ </section>
367
+ </div>
368
+ </template>
369
+
370
+ <style scoped>
371
+ section {
372
+ padding: 1rem;
373
+ border: 1px solid #e5e7eb;
374
+ border-radius: 0.5rem;
375
+ background: white;
376
+ }
377
+ </style>