@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,80 @@
1
+ # DataList Component
2
+
3
+ A simple list component for displaying collections of items with customizable rendering.
4
+
5
+ ## Features
6
+
7
+ - **Generic typing**: TypeScript support for any item type
8
+ - **Slot-based rendering**: Custom item templates
9
+ - **Loading state**: Built-in loading indicator
10
+ - **Empty state**: Customizable empty message
11
+ - **Click handling**: Item click events
12
+
13
+ ## Import
14
+
15
+ ```typescript
16
+ import { DataList } from '@htlkg/components';
17
+ // or
18
+ import { DataList } from '@htlkg/components/data';
19
+ ```
20
+
21
+ ## Props
22
+
23
+ | Prop | Type | Default | Description |
24
+ |------|------|---------|-------------|
25
+ | `items` | `T[]` | required | Array of items to display |
26
+ | `loading` | `boolean` | `false` | Show loading state |
27
+ | `emptyMessage` | `string` | `'No items to display'` | Empty state message |
28
+
29
+ ## Events
30
+
31
+ | Event | Payload | Description |
32
+ |-------|---------|-------------|
33
+ | `item-click` | `T` | Item clicked |
34
+
35
+ ## Slots
36
+
37
+ | Slot | Props | Description |
38
+ |------|-------|-------------|
39
+ | `default` | `{item: T, index: number}` | Custom item template |
40
+
41
+ ## Exposed Methods
42
+
43
+ | Method | Description |
44
+ |--------|-------------|
45
+ | `getItems()` | Get current items array |
46
+
47
+ ## Usage Examples
48
+
49
+ ### Basic List
50
+
51
+ ```vue
52
+ <script setup>
53
+ import { DataList } from '@htlkg/components';
54
+
55
+ const items = [
56
+ { id: 1, name: 'Item 1' },
57
+ { id: 2, name: 'Item 2' },
58
+ { id: 3, name: 'Item 3' }
59
+ ];
60
+
61
+ const handleClick = (item) => {
62
+ console.log('Clicked:', item);
63
+ };
64
+ </script>
65
+
66
+ <template>
67
+ <DataList
68
+ :items="items"
69
+ @item-click="handleClick"
70
+ >
71
+ <template #default="{ item }">
72
+ <div class="font-medium">{{ item.name }}</div>
73
+ </template>
74
+ </DataList>
75
+ </template>
76
+ ```
77
+
78
+ ## Demo
79
+
80
+ See the [DataList demo page](/components/data-list) for interactive examples.
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import DataList from './DataList.vue';
4
+
5
+ describe('DataList Component', () => {
6
+ const mockItems = [
7
+ { id: 1, name: 'Item 1', description: 'Description 1' },
8
+ { id: 2, name: 'Item 2', description: 'Description 2' },
9
+ { id: 3, name: 'Item 3', description: 'Description 3' }
10
+ ];
11
+
12
+ it('renders with basic props', () => {
13
+ const wrapper = mount(DataList, {
14
+ props: {
15
+ items: mockItems
16
+ }
17
+ });
18
+
19
+ expect(wrapper.exists()).toBe(true);
20
+ });
21
+
22
+ it('renders all items', () => {
23
+ const wrapper = mount(DataList, {
24
+ props: {
25
+ items: mockItems
26
+ }
27
+ });
28
+
29
+ // DataList renders items in divs with cursor-pointer class
30
+ const items = wrapper.findAll('.cursor-pointer');
31
+ expect(items.length).toBe(mockItems.length);
32
+ });
33
+
34
+ it('emits item-click event when item is clicked', async () => {
35
+ const wrapper = mount(DataList, {
36
+ props: {
37
+ items: mockItems
38
+ }
39
+ });
40
+
41
+ const component = wrapper.vm as any;
42
+ if (component.handleItemClick) {
43
+ component.handleItemClick(mockItems[0]);
44
+ expect(wrapper.emitted('item-click')).toBeTruthy();
45
+ expect(wrapper.emitted('item-click')?.[0]).toEqual([mockItems[0]]);
46
+ }
47
+ });
48
+
49
+ it('handles empty items array', () => {
50
+ const wrapper = mount(DataList, {
51
+ props: {
52
+ items: []
53
+ }
54
+ });
55
+
56
+ expect(wrapper.exists()).toBe(true);
57
+ });
58
+
59
+ it('renders with loading state', () => {
60
+ const wrapper = mount(DataList, {
61
+ props: {
62
+ items: mockItems,
63
+ loading: true
64
+ }
65
+ });
66
+
67
+ expect(wrapper.exists()).toBe(true);
68
+ });
69
+ });
@@ -0,0 +1,46 @@
1
+ <script setup lang="ts" generic="T extends Record<string, any>">
2
+ interface Props {
3
+ items: T[];
4
+ loading?: boolean;
5
+ emptyMessage?: string;
6
+ }
7
+
8
+ const props = withDefaults(defineProps<Props>(), {
9
+ loading: false,
10
+ emptyMessage: 'No items to display'
11
+ });
12
+
13
+ const emit = defineEmits<{
14
+ 'item-click': [item: T];
15
+ }>();
16
+
17
+ // Expose methods for parent components
18
+ defineExpose({
19
+ getItems: () => props.items
20
+ });
21
+ </script>
22
+
23
+ <template>
24
+ <div class="w-full">
25
+ <div v-if="loading" class="p-8 text-center text-gray-600">
26
+ Loading...
27
+ </div>
28
+
29
+ <div v-else-if="items.length === 0" class="p-8 text-center text-gray-600">
30
+ {{ emptyMessage }}
31
+ </div>
32
+
33
+ <div v-else class="flex flex-col gap-2">
34
+ <div
35
+ v-for="(item, index) in items"
36
+ :key="index"
37
+ class="p-4 border border-gray-200 rounded-md cursor-pointer hover:bg-gray-50 transition-colors"
38
+ @click="emit('item-click', item)"
39
+ >
40
+ <slot :item="item" :index="index">
41
+ {{ JSON.stringify(item) }}
42
+ </slot>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </template>
@@ -0,0 +1,107 @@
1
+ # SearchableSelect Component
2
+
3
+ A searchable dropdown select component with single and multiple selection support. Wraps `@hotelinking/ui`'s `uiSelect` and `uiSelectMultiple` components.
4
+
5
+ ## Features
6
+
7
+ - **Single/Multiple selection**: Toggle between modes
8
+ - **Generic typing**: TypeScript support for any option type
9
+ - **Customizable keys**: Configure label and value properties
10
+ - **v-model support**: Two-way binding
11
+ - **Loading state**: Built-in loading indicator
12
+ - **Validation**: Error display and color states
13
+
14
+ ## Import
15
+
16
+ ```typescript
17
+ import { SearchableSelect } from '@htlkg/components';
18
+ // or
19
+ import { SearchableSelect } from '@htlkg/components/data';
20
+ ```
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `options` | `T[]` | required | Available options |
27
+ | `modelValue` | `T \| T[] \| null` | `null` | Selected value(s) (v-model) |
28
+ | `labelKey` | `string` | `'label'` | Property to use for display |
29
+ | `valueKey` | `string` | `'id'` | Property to use for value |
30
+ | `placeholder` | `string` | `'Select an option'` | Placeholder text |
31
+ | `label` | `string` | `''` | Field label |
32
+ | `error` | `string` | `''` | Error message |
33
+ | `color` | `string` | `'gray'` | Field color state |
34
+ | `loading` | `boolean` | `false` | Show loading state |
35
+ | `disabled` | `boolean` | `false` | Disable selection |
36
+ | `requiredText` | `string` | `''` | Required indicator |
37
+ | `multiple` | `boolean` | `false` | Enable multiple selection |
38
+
39
+ ## Events
40
+
41
+ | Event | Payload | Description |
42
+ |-------|---------|-------------|
43
+ | `update:modelValue` | `T \| T[] \| null` | Selection changed |
44
+ | `change` | `T \| T[] \| null` | Selection changed |
45
+
46
+ ## Exposed Methods
47
+
48
+ | Method | Description |
49
+ |--------|-------------|
50
+ | `getSelected()` | Get current selection |
51
+
52
+ ## Usage Examples
53
+
54
+ ### Single Selection
55
+
56
+ ```vue
57
+ <script setup>
58
+ import { ref } from 'vue';
59
+ import { SearchableSelect } from '@htlkg/components';
60
+
61
+ const selected = ref(null);
62
+
63
+ const options = [
64
+ { id: 1, label: 'Option 1' },
65
+ { id: 2, label: 'Option 2' },
66
+ { id: 3, label: 'Option 3' }
67
+ ];
68
+ </script>
69
+
70
+ <template>
71
+ <SearchableSelect
72
+ v-model="selected"
73
+ :options="options"
74
+ label="Choose an option"
75
+ />
76
+ </template>
77
+ ```
78
+
79
+ ### Multiple Selection
80
+
81
+ ```vue
82
+ <script setup>
83
+ import { ref } from 'vue';
84
+ import { SearchableSelect } from '@htlkg/components';
85
+
86
+ const selected = ref([]);
87
+
88
+ const options = [
89
+ { id: 1, label: 'Tag 1' },
90
+ { id: 2, label: 'Tag 2' },
91
+ { id: 3, label: 'Tag 3' }
92
+ ];
93
+ </script>
94
+
95
+ <template>
96
+ <SearchableSelect
97
+ v-model="selected"
98
+ :options="options"
99
+ :multiple="true"
100
+ label="Select tags"
101
+ />
102
+ </template>
103
+ ```
104
+
105
+ ## Demo
106
+
107
+ See the [SearchableSelect demo page](/components/searchable-select) for interactive examples.
@@ -0,0 +1,124 @@
1
+ <script setup lang="ts" generic="T extends Record<string, any>">
2
+ import { ref, computed } from 'vue';
3
+ import { uiSelect, uiSelectMultiple } from '@hotelinking/ui';
4
+
5
+ interface Props {
6
+ options: T[];
7
+ modelValue?: T | T[] | null;
8
+ labelKey?: string;
9
+ valueKey?: string;
10
+ placeholder?: string;
11
+ label?: string;
12
+ error?: string;
13
+ color?: 'primary' | 'secondary' | 'light' | 'green' | 'yellow' | 'red' | 'black' | 'gray' | 'white';
14
+ loading?: boolean;
15
+ disabled?: boolean;
16
+ requiredText?: string;
17
+ multiple?: boolean;
18
+ }
19
+
20
+ const props = withDefaults(defineProps<Props>(), {
21
+ modelValue: null,
22
+ labelKey: 'label',
23
+ valueKey: 'id',
24
+ placeholder: 'Select an option',
25
+ label: '',
26
+ error: '',
27
+ color: 'gray',
28
+ loading: false,
29
+ disabled: false,
30
+ requiredText: '',
31
+ multiple: false
32
+ });
33
+
34
+ const emit = defineEmits<{
35
+ 'update:modelValue': [value: T | T[] | null];
36
+ 'change': [value: T | T[] | null];
37
+ }>();
38
+
39
+ // Transform options to match uiSelect/uiSelectMultiple format
40
+ const transformedItems = computed(() => {
41
+ return props.options.map(option => ({
42
+ id: String(option[props.valueKey]),
43
+ name: String(option[props.labelKey]),
44
+ label: option.label ? String(option.label) : undefined
45
+ }));
46
+ });
47
+
48
+ // Transform modelValue to match uiSelect format (single selection)
49
+ const selectedItem = computed(() => {
50
+ if (props.multiple || !props.modelValue) {
51
+ return { id: '', name: '', label: undefined };
52
+ }
53
+
54
+ const value = props.modelValue as T;
55
+ return {
56
+ id: String(value[props.valueKey]),
57
+ name: String(value[props.labelKey]),
58
+ label: value.label ? String(value.label) : undefined
59
+ };
60
+ });
61
+
62
+ // Transform modelValue to match uiSelectMultiple format (multiple selection)
63
+ const selectedItems = computed(() => {
64
+ if (!props.multiple || !props.modelValue) return [];
65
+
66
+ const values = Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue];
67
+ return values.map(value => ({
68
+ id: String(value[props.valueKey]),
69
+ name: String(value[props.labelKey]),
70
+ label: value.label ? String(value.label) : undefined
71
+ }));
72
+ });
73
+
74
+ function handleSelectChange(selected: any) {
75
+ if (props.multiple) {
76
+ // Multiple selection: transform back to original format
77
+ const originalValues = selected.map((item: any) => {
78
+ return props.options.find(opt => String(opt[props.valueKey]) === item.id);
79
+ }).filter(Boolean);
80
+
81
+ emit('update:modelValue', originalValues as T[]);
82
+ emit('change', originalValues as T[]);
83
+ } else {
84
+ // Single selection: transform back to original format
85
+ const originalValue = props.options.find(opt => String(opt[props.valueKey]) === selected.id);
86
+ emit('update:modelValue', originalValue || null);
87
+ emit('change', originalValue || null);
88
+ }
89
+ }
90
+
91
+ // Expose methods for parent components
92
+ defineExpose({
93
+ getSelected: () => props.modelValue
94
+ });
95
+ </script>
96
+
97
+ <template>
98
+ <uiSelectMultiple
99
+ v-if="multiple"
100
+ :items="transformedItems"
101
+ :label="label"
102
+ :placeholder="placeholder"
103
+ :error="error"
104
+ :color="color"
105
+ :select="selectedItems"
106
+ :loading="loading"
107
+ :disabled="disabled"
108
+ :required-text="requiredText"
109
+ @selectChanged="handleSelectChange"
110
+ />
111
+ <uiSelect
112
+ v-else
113
+ :items="transformedItems"
114
+ :label="label"
115
+ :placeholder="placeholder"
116
+ :error="error"
117
+ :color="color"
118
+ :select="selectedItem"
119
+ :loading="loading"
120
+ :disabled="disabled"
121
+ :required-text="requiredText"
122
+ @selectChanged="handleSelectChange"
123
+ />
124
+ </template>