@dative-gpi/foundation-shared-components 0.1.120 → 1.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 (197) hide show
  1. package/assets/images/map/imagery.png +0 -0
  2. package/assets/images/map/map.png +0 -0
  3. package/components/FSAccordion.vue +2 -1
  4. package/components/FSAccordionPanel.vue +20 -1
  5. package/components/FSBadge.vue +7 -3
  6. package/components/FSBreadcrumbs.vue +4 -2
  7. package/components/FSButton.vue +15 -8
  8. package/components/FSCalendar.vue +5 -2
  9. package/components/FSCalendarTwin.vue +6 -3
  10. package/components/FSCard.vue +4 -2
  11. package/components/FSCardPlaceholder.vue +80 -0
  12. package/components/FSCheckbox.vue +10 -7
  13. package/components/FSChip.vue +4 -2
  14. package/components/FSClickable.vue +5 -3
  15. package/components/FSClock.vue +18 -4
  16. package/components/FSCol.vue +12 -5
  17. package/components/FSColor.vue +4 -2
  18. package/components/FSColorIcon.vue +5 -3
  19. package/components/FSDialog.vue +28 -87
  20. package/components/FSDialogContent.vue +133 -0
  21. package/components/FSDialogForm.vue +25 -236
  22. package/components/FSDialogFormBody.vue +273 -0
  23. package/components/FSDialogMenu.vue +5 -2
  24. package/components/FSDialogMultiForm.vue +21 -197
  25. package/components/FSDialogMultiFormBody.vue +231 -0
  26. package/components/FSDialogSubmit.vue +4 -2
  27. package/components/FSDivider.vue +6 -4
  28. package/components/FSEditImage.vue +16 -9
  29. package/components/FSErrorToast.vue +2 -1
  30. package/components/FSFadeOut.vue +1 -1
  31. package/components/FSForm.vue +7 -7
  32. package/components/FSGrid.vue +4 -2
  33. package/components/FSGridMosaic.vue +3 -2
  34. package/components/FSIcon.vue +3 -2
  35. package/components/FSIconCard.vue +10 -3
  36. package/components/FSIconCheck.vue +2 -1
  37. package/components/FSIconFlag.vue +2 -1
  38. package/components/FSImage.vue +2 -1
  39. package/components/FSImageCard.vue +72 -0
  40. package/components/FSLabel.vue +4 -2
  41. package/components/FSLink.vue +5 -3
  42. package/components/FSLoader.vue +2 -1
  43. package/components/FSOptionGroup.vue +28 -20
  44. package/components/FSOptionItem.vue +8 -18
  45. package/components/FSPagination.vue +4 -2
  46. package/components/FSRadio.vue +23 -11
  47. package/components/FSRadioGroup.vue +23 -10
  48. package/components/FSRow.vue +8 -1
  49. package/components/FSSlideGroup.vue +27 -6
  50. package/components/FSSlider.vue +4 -2
  51. package/components/FSSpan.vue +2 -1
  52. package/components/FSSwitch.vue +13 -10
  53. package/components/FSTab.vue +4 -2
  54. package/components/FSTabs.vue +5 -14
  55. package/components/FSTag.vue +11 -4
  56. package/components/FSTagGroup.vue +4 -2
  57. package/components/FSText.vue +4 -2
  58. package/components/FSToggleSet.vue +30 -17
  59. package/components/FSTooltip.vue +15 -4
  60. package/components/FSWindow.vue +2 -2
  61. package/components/FSWrapGroup.vue +2 -1
  62. package/components/autocompletes/FSAutoCompleteAddress.vue +104 -0
  63. package/components/autocompletes/FSAutocompleteLanguage.vue +18 -26
  64. package/components/autocompletes/FSAutocompleteTag.vue +138 -0
  65. package/components/autocompletes/FSAutocompleteTimeZone.vue +19 -30
  66. package/components/buttons/FSButtonAdd.vue +28 -0
  67. package/components/buttons/FSButtonAddIcon.vue +28 -0
  68. package/components/buttons/FSButtonAddLabel.vue +27 -0
  69. package/components/buttons/FSButtonAddMini.vue +27 -0
  70. package/components/buttons/FSButtonFile.vue +4 -4
  71. package/components/buttons/FSButtonFileIcon.vue +4 -4
  72. package/components/buttons/FSButtonFileLabel.vue +4 -4
  73. package/components/buttons/FSButtonFileMini.vue +4 -4
  74. package/components/deviceOrganisations/FSConnectivity.vue +3 -2
  75. package/components/deviceOrganisations/FSConnectivityCard.vue +3 -2
  76. package/components/deviceOrganisations/FSStatus.vue +3 -2
  77. package/components/deviceOrganisations/FSStatusCard.vue +3 -2
  78. package/components/deviceOrganisations/FSStatusesCarousel.vue +3 -2
  79. package/components/deviceOrganisations/FSStatusesRow.vue +3 -2
  80. package/components/deviceOrganisations/FSWorstAlert.vue +5 -4
  81. package/components/deviceOrganisations/FSWorstAlertCard.vue +4 -2
  82. package/components/fields/FSAutocompleteField.vue +219 -97
  83. package/components/fields/FSBaseField.vue +2 -1
  84. package/components/fields/FSColorField.vue +65 -94
  85. package/components/fields/FSDateField.vue +12 -25
  86. package/components/fields/FSDateRangeField.vue +15 -27
  87. package/components/fields/FSDateTimeField.vue +22 -32
  88. package/components/fields/FSDateTimeRangeField.vue +43 -51
  89. package/components/fields/FSGradientField.vue +143 -0
  90. package/components/fields/FSIconField.vue +9 -6
  91. package/components/fields/FSMagicConfigField.vue +154 -0
  92. package/components/fields/FSMagicField.vue +185 -0
  93. package/components/fields/FSNumberField.vue +3 -1
  94. package/components/fields/FSPasswordField.vue +10 -10
  95. package/components/fields/FSRichTextField.vue +136 -50
  96. package/components/fields/FSSearchField.vue +41 -62
  97. package/components/fields/FSSelectField.vue +148 -53
  98. package/components/fields/FSTagField.vue +19 -16
  99. package/components/fields/FSTermField.vue +192 -186
  100. package/components/fields/FSTextArea.vue +4 -4
  101. package/components/fields/FSTextField.vue +29 -6
  102. package/components/fields/FSTimeField.vue +55 -20
  103. package/components/fields/FSTimeSlotField.vue +6 -5
  104. package/components/fields/FSTranslateField.vue +234 -0
  105. package/components/fields/FSTranslateRichTextField.vue +185 -0
  106. package/components/fields/FSTreeViewField.vue +520 -0
  107. package/components/lists/FSDataIteratorItem.vue +18 -3
  108. package/components/lists/FSDataTableUI.vue +138 -51
  109. package/components/lists/FSFilterButton.vue +4 -2
  110. package/components/lists/FSHiddenButton.vue +4 -2
  111. package/components/map/FSMap.vue +598 -0
  112. package/components/map/FSMapEditPointAddressOverlay.vue +164 -0
  113. package/components/map/FSMapLayerButton.vue +77 -0
  114. package/components/map/FSMapOverlay.vue +150 -0
  115. package/components/selects/FSSelectAutoRefresh.vue +62 -0
  116. package/components/selects/FSSelectDashboardVariableType.vue +47 -0
  117. package/components/selects/FSSelectDateSetting.vue +39 -37
  118. package/components/selects/FSSelectDays.vue +62 -0
  119. package/components/tiles/FSDashboardOrganisationTileUI.vue +8 -8
  120. package/components/tiles/FSDashboardOrganisationTypeTileUI.vue +8 -8
  121. package/components/tiles/FSDashboardShallowTileUI.vue +8 -8
  122. package/components/tiles/FSDeviceOrganisationTileUI.vue +20 -15
  123. package/components/tiles/FSFolderTileUI.vue +7 -7
  124. package/components/tiles/FSGroupTileUI.vue +34 -27
  125. package/components/tiles/FSServiceAccountOrganisationTileUI.vue +142 -0
  126. package/components/tiles/{FSSimpleIconTileUI.vue → FSSimpleTileUI.vue} +48 -25
  127. package/components/tiles/FSTile.vue +51 -13
  128. package/components/tiles/FSUserOrganisationTileUI.vue +25 -34
  129. package/components/toggleSets/FSToggleSetPosition.vue +61 -0
  130. package/composables/index.ts +5 -1
  131. package/composables/useAddress.ts +158 -0
  132. package/composables/useAutocomplete.ts +4 -3
  133. package/composables/useColors.ts +8 -25
  134. package/composables/useDebounce.ts +2 -1
  135. package/composables/useMagicFieldProvider.ts +22 -0
  136. package/composables/useRules.ts +4 -12
  137. package/composables/useSlots.ts +65 -27
  138. package/composables/useTables.ts +29 -0
  139. package/composables/useTreeView.ts +48 -0
  140. package/elements/FSFormElement.ts +2 -1
  141. package/icons/flags/France.vue +21 -5
  142. package/icons/flags/Germany.vue +16 -4
  143. package/icons/flags/GreatBritain.vue +25 -6
  144. package/icons/flags/Italy.vue +21 -5
  145. package/icons/flags/Portugal.vue +225 -51
  146. package/icons/flags/Spain.vue +2781 -543
  147. package/icons/flags/UnitedStates.vue +31 -7
  148. package/icons/widgetTemplates/DevicesWidget.vue +189 -189
  149. package/icons/widgetTemplates/ProfileWidget.vue +9 -9
  150. package/icons/widgetTemplates/TextWidget.vue +6 -6
  151. package/models/breadcrumbs.ts +1 -1
  152. package/models/deviceAlerts.ts +1 -1
  153. package/models/deviceConnectivities.ts +1 -1
  154. package/models/index.ts +2 -0
  155. package/models/magicFields.ts +9 -0
  156. package/models/map.ts +18 -0
  157. package/models/richTextVariable.ts +5 -0
  158. package/models/rules.ts +11 -2
  159. package/models/tables.ts +30 -21
  160. package/models/variableNode.ts +104 -0
  161. package/package.json +21 -18
  162. package/plugins/colorPlugin.ts +2 -2
  163. package/plugins/index.ts +2 -1
  164. package/plugins/mapsPlugin.ts +37 -0
  165. package/shims-plugin.d.ts +2 -2
  166. package/shims-window.d.ts +3 -0
  167. package/styles/components/fs_button.scss +5 -0
  168. package/styles/components/fs_card.scss +0 -1
  169. package/styles/components/fs_col.scss +1 -0
  170. package/styles/components/fs_color_field.scss +12 -2
  171. package/styles/components/fs_data_iterator_item.scss +19 -6
  172. package/styles/components/fs_dialog.scss +12 -22
  173. package/styles/components/fs_gradient_field.scss +16 -0
  174. package/styles/components/fs_image_card.scss +18 -0
  175. package/styles/components/fs_magic_config_field.scss +13 -0
  176. package/styles/components/fs_map.scss +129 -0
  177. package/styles/components/fs_map_overlay.scss +38 -0
  178. package/styles/components/fs_meta_field.scss +6 -0
  179. package/styles/components/fs_option_group.scss +1 -0
  180. package/styles/components/fs_radio.scss +1 -1
  181. package/styles/components/fs_rich_text_field.scss +17 -5
  182. package/styles/components/fs_row.scss +1 -1
  183. package/styles/components/fs_select_field.scss +9 -14
  184. package/styles/components/fs_text.scss +1 -1
  185. package/styles/components/fs_time_field.scss +0 -4
  186. package/styles/components/fs_translate_field.scss +3 -0
  187. package/styles/components/fs_tree_view_field.scss +53 -0
  188. package/styles/components/index.scss +8 -1
  189. package/styles/globals/overrides.scss +54 -8
  190. package/styles/globals/scrollbars.scss +2 -2
  191. package/themes/default.ts +1 -1
  192. package/utils/gradient.ts +1601 -0
  193. package/utils/index.ts +3 -1
  194. package/utils/leafletMarkers.ts +23 -0
  195. package/utils/lexical.ts +3 -1
  196. package/components/selects/FSSelectTimeZone.vue +0 -67
  197. package/styles/components/fs_date_field.scss +0 -8
@@ -0,0 +1,185 @@
1
+ <template>
2
+ <FSCol
3
+ v-if="$props.translationsExpanded"
4
+ >
5
+ <FSCol
6
+ gap="16px"
7
+ >
8
+ <FSRichTextField
9
+ :editable="false"
10
+ :label="$tr('ui.translateRichTextField.defaultValue', 'Default value')"
11
+ :modelValue="$props.modelValue"
12
+ v-bind="$attrs"
13
+ />
14
+ <FSRichTextField
15
+ v-for="(language, index) in languages"
16
+ :editable="$props.editable"
17
+ :key="index"
18
+ :modelValue="getTranslation(language.code)"
19
+ @update:modelValue="setTranslation(language.code, $event)"
20
+ v-bind="$attrs"
21
+ >
22
+ <template
23
+ #label
24
+ >
25
+ <FSRow
26
+ :wrap="false"
27
+ >
28
+ <FSSpan
29
+ class="fs-translate-field-label"
30
+ font="text-overline"
31
+ >
32
+ {{ $tr('ui.translateRichTextField.translateIn', 'Translate in {0}', language.label) }}
33
+ </FSSpan>
34
+ <FSIcon>{{ language.icon }}</FSIcon>
35
+ </FSRow>
36
+ </template>
37
+ </FSRichTextField>
38
+ </FSCol>
39
+ <FSRow
40
+ :wrap="false"
41
+ >
42
+ <FSButton
43
+ prependIcon="mdi-cancel"
44
+ :label="$tr('ui.translateRichTextField.cancelButton.label', 'Cancel')"
45
+ :fullWidth="true"
46
+ @click="onCancelTranslations"
47
+ />
48
+ <FSButton
49
+ v-if="$props.editable"
50
+ prependIcon="mdi-check"
51
+ color="primary"
52
+ :label="$tr('ui.translateRichTextField.validateButton.label', 'Validate translations')"
53
+ :fullWidth="true"
54
+ @click="onSubmitTranslations"
55
+ />
56
+ </FSRow>
57
+ </FSCol>
58
+ <FSRichTextField
59
+ v-else
60
+ :editable="$props.editable"
61
+ :modelValue="$props.modelValue"
62
+ @update:modelValue="$emit('update:modelValue', $event)"
63
+ v-bind="$attrs"
64
+ >
65
+ <template
66
+ #append-inner
67
+ >
68
+ <FSButton
69
+ prependIcon="mdi-translate"
70
+ color="primary"
71
+ :label="$tr('ui.translateRichTextField.translateButton.label', 'Manage translations')"
72
+ :fullWidth="true"
73
+ @click="() => $emit('update:translationsExpanded', true)"
74
+ />
75
+ </template>
76
+ </FSRichTextField>
77
+ </template>
78
+
79
+ <script lang="ts">
80
+ import { defineComponent, type PropType, ref } from 'vue';
81
+
82
+ import { useAppLanguages } from "@dative-gpi/foundation-shared-services/composables";
83
+
84
+ import { emptyLexicalState } from '../../utils';
85
+
86
+ import FSRichTextField from './FSRichTextField.vue';
87
+ import FSButton from '../FSButton.vue';
88
+ import FSIcon from '../FSIcon.vue';
89
+ import FSSpan from '../FSSpan.vue';
90
+ import FSCol from '../FSCol.vue';
91
+ import FSRow from '../FSRow.vue';
92
+
93
+ export default defineComponent({
94
+ name: 'FSTranslateRichTextField',
95
+ components: {
96
+ FSRichTextField,
97
+ FSButton,
98
+ FSIcon,
99
+ FSSpan,
100
+ FSCol,
101
+ FSRow
102
+ },
103
+ props: {
104
+ translationsExpanded: {
105
+ type: Boolean,
106
+ default: false,
107
+ },
108
+ editable: {
109
+ type: Boolean,
110
+ default: true,
111
+ },
112
+ modelValue: {
113
+ type: String as PropType<string | null>,
114
+ required: false,
115
+ default: null
116
+ },
117
+ translations: {
118
+ type: Array as PropType<{ languageCode: string; [key: string]: string }[]>,
119
+ required: false,
120
+ default: () => []
121
+ },
122
+ property: {
123
+ type: String as PropType<string>,
124
+ required: false,
125
+ default: "label"
126
+ }
127
+ },
128
+ emits: ['update:translationsExpanded', 'update:modelValue', 'update:translations'],
129
+ setup(props, { emit }) {
130
+ const { languages } = useAppLanguages();
131
+
132
+ const innerTranslations = ref(props.translations);
133
+
134
+ const getTranslation = (languageCode: string): string => {
135
+ if (!innerTranslations.value) {
136
+ return emptyLexicalState;
137
+ }
138
+ const translation = innerTranslations.value.find((t) => t.languageCode === languageCode);
139
+ if (!translation || !translation[props.property]) {
140
+ return emptyLexicalState;
141
+ }
142
+ return translation[props.property].toString();
143
+ };
144
+
145
+ const setTranslation = (languageCode: string, value: string): void => {
146
+ if (!innerTranslations.value) {
147
+ innerTranslations.value = [{
148
+ languageCode,
149
+ [props.property]: value
150
+ }]
151
+ return;
152
+ }
153
+ const translation = innerTranslations.value.find((t) => t.languageCode === languageCode);
154
+ if (translation) {
155
+ translation[props.property] = value;
156
+ }
157
+ else {
158
+ innerTranslations.value.push({
159
+ languageCode,
160
+ [props.property]: value
161
+ });
162
+ }
163
+ };
164
+
165
+ const onSubmitTranslations = (): void => {
166
+ if (props.editable) {
167
+ emit("update:translations", innerTranslations.value);
168
+ }
169
+ emit('update:translationsExpanded', false);
170
+ };
171
+
172
+ const onCancelTranslations = (): void => {
173
+ emit('update:translationsExpanded', false);
174
+ };
175
+
176
+ return {
177
+ languages,
178
+ onCancelTranslations,
179
+ onSubmitTranslations,
180
+ getTranslation,
181
+ setTranslation
182
+ };
183
+ }
184
+ });
185
+ </script>
@@ -0,0 +1,520 @@
1
+ <template>
2
+ <template
3
+ v-if="$props.loading"
4
+ >
5
+ <FSCol>
6
+ <FSLoader
7
+ v-if="!$props.hideHeader"
8
+ variant="text-overline"
9
+ />
10
+ <FSLoader
11
+ v-if="$props.loading"
12
+ width="100%"
13
+ :height="['40px', '36px']"
14
+ />
15
+ </FSCol>
16
+ </template>
17
+ <template
18
+ v-else
19
+ >
20
+ <FSCol>
21
+ <template
22
+ v-if="isExtraSmall"
23
+ >
24
+ <FSTextField
25
+ class="fs-tree-view-field"
26
+ :validationValue="$props.modelValue"
27
+ :description="$props.description"
28
+ :hideHeader="$props.hideHeader"
29
+ :clearable="$props.clearable"
30
+ :editable="$props.editable"
31
+ :required="$props.required"
32
+ :validateOn="validateOn"
33
+ :label="$props.label"
34
+ :rules="$props.rules"
35
+ :messages="messages"
36
+ :readonly="true"
37
+ :style="style"
38
+ :modelValue="innerValue"
39
+ @update:modelValue="$emit('update:modelValue', $event)"
40
+ @click="openMobileOverlay"
41
+ v-bind="$attrs"
42
+ >
43
+ <template
44
+ v-for="(_, name) in fieldSlots"
45
+ v-slot:[name]="slotData"
46
+ >
47
+ <slot
48
+ :name="name"
49
+ v-bind="slotData"
50
+ />
51
+ </template>
52
+ </FSTextField>
53
+ <FSDialogMenu
54
+ v-model="dialog"
55
+ >
56
+ <template
57
+ #body
58
+ >
59
+ <FSFadeOut
60
+ :height="height"
61
+ >
62
+ <v-treeview
63
+ :itemTitle="$props.itemTitle"
64
+ :itemValue="$props.itemValue"
65
+ :items="treeItems"
66
+ >
67
+ <template
68
+ v-for="(_, name) in menuSlots"
69
+ v-slot:[name]="slotData"
70
+ >
71
+ <slot
72
+ :name="name"
73
+ v-bind="slotData"
74
+ />
75
+ </template>
76
+ <template
77
+ #prepend="{ item }"
78
+ >
79
+ <FSCheckbox
80
+ v-if="$props.multiple"
81
+ :class="listItemClass(item[$props.itemValue])"
82
+ :editable="$props.editable"
83
+ :modelValue="$props.modelValue?.includes(item[$props.itemValue])"
84
+ @update:modelValue="() => onCheckboxChange(item[$props.itemValue])"
85
+ >
86
+ <template
87
+ #label="{ font }"
88
+ >
89
+ <slot
90
+ name="menu-prepend"
91
+ :item="item"
92
+ />
93
+ <FSSpan
94
+ :font="font"
95
+ >
96
+ {{ item[$props.itemTitle] }}
97
+ </FSSpan>
98
+ </template>
99
+ </FSCheckbox>
100
+ <FSRadio
101
+ v-if="!$props.multiple"
102
+ :selected="$props.modelValue === item[$props.itemValue]"
103
+ :class="listItemClass(item[$props.itemValue])"
104
+ :editable="$props.editable"
105
+ :modelValue="item[$props.itemValue]"
106
+ @update:modelValue="onRadioChange"
107
+ >
108
+ <template
109
+ #label="{ font }"
110
+ >
111
+ <slot
112
+ name="menu-prepend"
113
+ :item="item"
114
+ />
115
+ <FSSpan
116
+ :font="font"
117
+ >
118
+ {{ item[$props.itemTitle] }}
119
+ </FSSpan>
120
+ </template>
121
+ </FSRadio>
122
+ </template>
123
+ <template
124
+ #title
125
+ />
126
+ </v-treeview>
127
+ </FSFadeOut>
128
+ </template>
129
+ </FSDialogMenu>
130
+ </template>
131
+ <template
132
+ v-else
133
+ >
134
+ <v-menu
135
+ :closeOnContentClick="false"
136
+ :modelValue="menu && $props.editable"
137
+ @update:modelValue="menu = $event"
138
+ >
139
+ <template
140
+ #activator="{ props }"
141
+ >
142
+ <FSTextField
143
+ class="fs-tree-view-field"
144
+ :validationValue="$props.modelValue"
145
+ :description="$props.description"
146
+ :hideHeader="$props.hideHeader"
147
+ :clearable="$props.clearable"
148
+ :editable="$props.editable"
149
+ :required="$props.required"
150
+ :validateOn="validateOn"
151
+ :label="$props.label"
152
+ :rules="$props.rules"
153
+ :messages="messages"
154
+ :readonly="true"
155
+ :style="style"
156
+ :modelValue="innerValue"
157
+ @update:modelValue="$emit('update:modelValue', $event)"
158
+ v-bind="{ ...$attrs, ...props }"
159
+ >
160
+ <template
161
+ v-for="(_, name) in fieldSlots"
162
+ v-slot:[name]="slotData"
163
+ >
164
+ <slot
165
+ :name="name"
166
+ v-bind="slotData"
167
+ />
168
+ </template>
169
+ </FSTextField>
170
+ </template>
171
+ <v-treeview
172
+ :itemTitle="$props.itemTitle"
173
+ :itemValue="$props.itemValue"
174
+ :items="treeItems"
175
+ >
176
+ <template
177
+ v-for="(_, name) in menuSlots"
178
+ v-slot:[name]="slotData"
179
+ >
180
+ <slot
181
+ :name="name"
182
+ v-bind="slotData"
183
+ />
184
+ </template>
185
+ <template
186
+ #prepend="{ item }"
187
+ >
188
+ <FSCheckbox
189
+ v-if="$props.multiple"
190
+ :class="listItemClass(item[$props.itemValue])"
191
+ :editable="$props.editable"
192
+ :modelValue="$props.modelValue?.includes(item[$props.itemValue])"
193
+ @update:modelValue="() => onCheckboxChange(item[$props.itemValue])"
194
+ >
195
+ <template
196
+ #label="{ font }"
197
+ >
198
+ <slot
199
+ name="menu-prepend"
200
+ :item="item"
201
+ />
202
+ <FSSpan
203
+ :font="font"
204
+ >
205
+ {{ item[$props.itemTitle] }}
206
+ </FSSpan>
207
+ </template>
208
+ </FSCheckbox>
209
+ <FSRadio
210
+ v-if="!$props.multiple"
211
+ :selected="$props.modelValue === item[$props.itemValue]"
212
+ :class="listItemClass(item[$props.itemValue])"
213
+ :editable="$props.editable"
214
+ :modelValue="item[$props.itemValue]"
215
+ @update:modelValue="onRadioChange"
216
+ >
217
+ <template
218
+ #label="{ font }"
219
+ >
220
+ <slot
221
+ name="menu-prepend"
222
+ :item="item"
223
+ />
224
+ <FSSpan
225
+ :font="font"
226
+ >
227
+ {{ item[$props.itemTitle] }}
228
+ </FSSpan>
229
+ </template>
230
+ </FSRadio>
231
+ </template>
232
+ <template
233
+ #title
234
+ />
235
+ </v-treeview>
236
+ </v-menu>
237
+ </template>
238
+ </FSCol>
239
+ </template>
240
+ </template>
241
+
242
+ <script lang="ts">
243
+ import type { PropType} from "vue";
244
+ import { computed, defineComponent, ref } from "vue";
245
+
246
+ import { useBreakpoints, useColors, useRules, useSlots } from "@dative-gpi/foundation-shared-components/composables";
247
+ import { ColorEnum } from "@dative-gpi/foundation-shared-components/models";
248
+
249
+ import { VTreeview } from "vuetify/labs/VTreeview";
250
+
251
+ import FSDialogMenu from "../FSDialogMenu.vue";
252
+ import FSTextField from "./FSTextField.vue";
253
+ import FSCheckbox from "../FSCheckbox.vue";
254
+ import FSFadeOut from "../FSFadeOut.vue";
255
+ import FSLoader from "../FSLoader.vue";
256
+ import FSRadio from "../FSRadio.vue";
257
+ import FSSpan from "../FSSpan.vue";
258
+ import FSCol from "../FSCol.vue";
259
+
260
+ export default defineComponent({
261
+ name: "FSTreeViewField",
262
+ components: {
263
+ VTreeview,
264
+ FSDialogMenu,
265
+ FSTextField,
266
+ FSCheckbox,
267
+ FSFadeOut,
268
+ FSLoader,
269
+ FSRadio,
270
+ FSSpan,
271
+ FSCol,
272
+ },
273
+ props: {
274
+ label: {
275
+ type: String as PropType<string | null>,
276
+ required: false,
277
+ default: null
278
+ },
279
+ description: {
280
+ type: String as PropType<string | null>,
281
+ required: false,
282
+ default: null
283
+ },
284
+ items: {
285
+ type: Array as PropType<any[]>,
286
+ required: true
287
+ },
288
+ itemValue: {
289
+ type: String,
290
+ required: false,
291
+ default: "id"
292
+ },
293
+ itemTitle: {
294
+ type: String,
295
+ required: false,
296
+ default: "label"
297
+ },
298
+ itemParent: {
299
+ type: String,
300
+ required: false,
301
+ default: "parentId"
302
+ },
303
+ exclude: {
304
+ type: Array as PropType<string[]>,
305
+ required: false,
306
+ default: () => []
307
+ },
308
+ modelValue: {
309
+ type: [Array, String, Number] as PropType<(string | number)[] | string | number | null>,
310
+ required: false,
311
+ default: null
312
+ },
313
+ hideHeader: {
314
+ type: Boolean,
315
+ required: false,
316
+ default: false
317
+ },
318
+ required: {
319
+ type: Boolean,
320
+ required: false,
321
+ default: false
322
+ },
323
+ multiple: {
324
+ type: Boolean,
325
+ required: false,
326
+ default: false
327
+ },
328
+ rules: {
329
+ type: Array as PropType<any[]>,
330
+ required: false,
331
+ default: () => []
332
+ },
333
+ messages: {
334
+ type: Array as PropType<string[]>,
335
+ required: false,
336
+ default: null
337
+ },
338
+ clearable: {
339
+ type: Boolean,
340
+ required: false,
341
+ default: true
342
+ },
343
+ editable: {
344
+ type: Boolean,
345
+ required: false,
346
+ default: true
347
+ },
348
+ loading: {
349
+ type: Boolean,
350
+ required: false,
351
+ default: false
352
+ }
353
+ },
354
+ emits: ["update:modelValue"],
355
+ setup(props, { emit }) {
356
+ const { validateOn, getMessages } = useRules();
357
+ const { isExtraSmall } = useBreakpoints();
358
+ const { getColors } = useColors();
359
+ const { slots } = useSlots();
360
+
361
+ delete slots.label;
362
+ delete slots.description;
363
+ delete slots["menu-prepend"];
364
+
365
+ const backgrounds = getColors(ColorEnum.Background);
366
+
367
+ const dialog = ref(false);
368
+ const menu = ref(false);
369
+
370
+ const style = computed((): { [key: string]: string | undefined | null } => {
371
+ if (!props.editable) {
372
+ return {
373
+ "--fs-tree-view-field-cursor": "default"
374
+ };
375
+ }
376
+ else {
377
+ return {
378
+ "--fs-tree-view-field-cursor" : "pointer",
379
+ "--fs-tree-view-field-background-color": backgrounds.base
380
+ };
381
+ }
382
+ });
383
+
384
+ const messages = computed((): string[] => props.messages ?? getMessages(props.modelValue, props.rules));
385
+
386
+ const height = computed(() => {
387
+ const other = 8 + 8; // Paddings
388
+ return `calc(100vh - 40px - ${other}px)`;
389
+ });
390
+
391
+ const fieldSlots = computed((): any => {
392
+ return Object.keys(slots).filter(k => !k.startsWith("menu-")).reduce((acc, key) => {
393
+ acc[key] = slots[key];
394
+ return acc;
395
+ }, {});
396
+ });
397
+
398
+ const menuSlots = computed((): any => {
399
+ return Object.keys(slots).filter(k => k.startsWith("menu-")).reduce((acc, key) => {
400
+ acc[key.substring("menu-".length)] = slots[key];
401
+ return acc;
402
+ }, {});
403
+ });
404
+
405
+ const innerValue = computed((): string | null => {
406
+ if (props.multiple) {
407
+ if (Array.isArray(props.modelValue)) {
408
+ return props.modelValue.map((value: any) => {
409
+ const item = props.items.find((item: object) => item[props.itemValue] === value);
410
+ if (item) {
411
+ return item[props.itemTitle];
412
+ }
413
+ }).filter(value => !!value).join(", ");
414
+ }
415
+ }
416
+ if (props.modelValue) {
417
+ const item = props.items.find((item: object) => item[props.itemValue] === props.modelValue);
418
+ if (item) {
419
+ return item[props.itemTitle];
420
+ }
421
+ }
422
+ return null;
423
+ });
424
+
425
+ const treeItems = computed((): any[] => {
426
+ const filter = ((parentId: string | null) => {
427
+ return props.items.filter((item: any) => {
428
+ if (props.exclude.includes(item[props.itemValue])) {
429
+ return false;
430
+ }
431
+ return item[props.itemParent] == parentId;
432
+ });
433
+ });
434
+ const process = ((item: any) => {
435
+ if (props.items.some((child: any) => child[props.itemParent] === item[props.itemValue])) {
436
+ return {
437
+ ...item,
438
+ children: filter(item[props.itemValue]).map(process)
439
+ };
440
+ }
441
+ return item;
442
+ });
443
+ return filter(null).map(process);
444
+ });
445
+
446
+ const listItemClass = (value: string): string[] => {
447
+ const classNames: string[] = [];
448
+ if (props.multiple) {
449
+ if (Array.isArray(props.modelValue)) {
450
+ if (props.modelValue.includes(value)) {
451
+ classNames.push("fs-tree-view-item-selected");
452
+ }
453
+ }
454
+ else if (props.modelValue === value) {
455
+ classNames.push("fs-tree-view-item-selected");
456
+ }
457
+ }
458
+ else {
459
+ if (props.modelValue === value) {
460
+ classNames.push("fs-tree-view-item-selected");
461
+ }
462
+ }
463
+ return classNames;
464
+ };
465
+
466
+ const openMobileOverlay = () => {
467
+ if (!props.editable) {
468
+ return;
469
+ }
470
+ dialog.value = true;
471
+ };
472
+
473
+ const onRadioChange = (value: any): void => {
474
+ emit("update:modelValue", value);
475
+ dialog.value = false;
476
+ menu.value = false;
477
+ };
478
+
479
+ const onCheckboxChange = (value: string) => {
480
+ if (Array.isArray(props.modelValue)) {
481
+ if (props.modelValue.includes(value)) {
482
+ emit("update:modelValue", props.modelValue.filter((item: any) => item !== value));
483
+ }
484
+ else {
485
+ emit("update:modelValue", [...props.modelValue, value]);
486
+ }
487
+ }
488
+ else if (props.modelValue != null) {
489
+ if (props.modelValue === value) {
490
+ emit("update:modelValue", []);
491
+ }
492
+ else {
493
+ emit("update:modelValue", [props.modelValue, value]);
494
+ }
495
+ }
496
+ else {
497
+ emit("update:modelValue", [value]);
498
+ }
499
+ };
500
+
501
+ return {
502
+ isExtraSmall,
503
+ innerValue,
504
+ fieldSlots,
505
+ validateOn,
506
+ menuSlots,
507
+ treeItems,
508
+ messages,
509
+ dialog,
510
+ height,
511
+ style,
512
+ menu,
513
+ openMobileOverlay,
514
+ onCheckboxChange,
515
+ onRadioChange,
516
+ listItemClass
517
+ };
518
+ }
519
+ });
520
+ </script>