@dative-gpi/foundation-shared-components 0.0.229 → 0.1.68

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 (228) hide show
  1. package/components/FSAccordionPanel.vue +8 -10
  2. package/components/FSBadge.vue +2 -4
  3. package/components/FSBreadcrumbs.vue +9 -10
  4. package/components/FSButton.vue +81 -31
  5. package/components/FSCalendar.vue +35 -33
  6. package/components/FSCalendarTwin.vue +77 -84
  7. package/components/FSCard.vue +29 -16
  8. package/components/FSCardPlaceholder.vue +1 -2
  9. package/components/FSCheckbox.vue +5 -7
  10. package/components/FSChip.vue +9 -11
  11. package/components/FSClickable.vue +67 -57
  12. package/components/FSClock.vue +4 -4
  13. package/components/FSCol.vue +2 -3
  14. package/components/FSColor.vue +13 -31
  15. package/components/FSColorIcon.vue +25 -9
  16. package/components/FSDialog.vue +1 -2
  17. package/components/FSDialogFormBody.vue +4 -4
  18. package/components/FSDialogMenu.vue +2 -4
  19. package/components/FSDialogMultiFormBody.vue +63 -50
  20. package/components/FSDialogRemove.vue +1 -0
  21. package/components/FSDialogSubmit.vue +5 -7
  22. package/components/FSDivider.vue +7 -9
  23. package/components/FSEditImage.vue +27 -274
  24. package/components/FSEditImageUI.vue +303 -0
  25. package/components/FSErrorToast.vue +3 -4
  26. package/components/FSFadeOut.vue +18 -13
  27. package/components/FSGrid.vue +6 -9
  28. package/components/FSGridMosaic.vue +1 -2
  29. package/components/FSIcon.vue +7 -8
  30. package/components/FSIconCard.vue +23 -30
  31. package/components/FSIconCheck.vue +1 -2
  32. package/components/FSImage.vue +21 -193
  33. package/components/FSImageCard.vue +2 -2
  34. package/components/FSImageUI.vue +212 -0
  35. package/components/FSLabel.vue +18 -20
  36. package/components/FSLink.vue +19 -21
  37. package/components/FSLoader.vue +10 -14
  38. package/components/FSOptionGroup.vue +58 -12
  39. package/components/FSPagination.vue +3 -5
  40. package/components/FSRadio.vue +5 -7
  41. package/components/FSRadioGroup.vue +2 -4
  42. package/components/FSRow.vue +3 -4
  43. package/components/FSSlideGroup.vue +15 -4
  44. package/components/FSSlider.vue +9 -11
  45. package/components/FSSpan.vue +11 -9
  46. package/components/FSSwitch.vue +65 -37
  47. package/components/FSTab.vue +2 -4
  48. package/components/FSTabs.vue +36 -16
  49. package/components/FSTag.vue +23 -14
  50. package/components/FSTagGroup.vue +3 -5
  51. package/components/FSText.vue +8 -12
  52. package/components/FSWindow.vue +11 -5
  53. package/components/FSWrapGroup.vue +15 -4
  54. package/components/agenda/FSAgenda.vue +204 -0
  55. package/components/agenda/FSAgendaDialogCalendar.vue +76 -0
  56. package/components/agenda/FSAgendaHeader.vue +190 -0
  57. package/components/agenda/FSAgendaHorizontalEvent.vue +162 -0
  58. package/components/agenda/FSAgendaHorizontalTimeLineMarker.vue +46 -0
  59. package/components/agenda/FSAgendaHoursCol.vue +103 -0
  60. package/components/agenda/FSAgendaHoursRow.vue +124 -0
  61. package/components/agenda/FSAgendaVerticalEvent.vue +160 -0
  62. package/components/agenda/FSAgendaVerticalTimeLineMarker.vue +46 -0
  63. package/components/agenda/FSDayAgenda.vue +200 -0
  64. package/components/agenda/FSMonthAgenda.vue +257 -0
  65. package/components/agenda/FSSelectAgendaMode.vue +54 -0
  66. package/components/agenda/FSWeekAgenda.vue +328 -0
  67. package/components/autocompletes/FSAutocompleteLanguage.vue +18 -39
  68. package/components/autocompletes/FSAutocompleteTimeZone.vue +20 -38
  69. package/components/buttons/FSButtonCancelMini.vue +1 -0
  70. package/components/buttons/FSButtonCopy.vue +28 -0
  71. package/components/buttons/FSButtonCopyIcon.vue +28 -0
  72. package/components/buttons/FSButtonCopyLabel.vue +27 -0
  73. package/components/buttons/FSButtonCopyMini.vue +28 -0
  74. package/components/buttons/FSButtonDragIcon.vue +27 -0
  75. package/components/buttons/FSButtonDuplicateMini.vue +1 -0
  76. package/components/buttons/FSButtonEditMini.vue +1 -0
  77. package/components/buttons/FSButtonFileMini.vue +1 -0
  78. package/components/buttons/FSButtonNextMini.vue +1 -0
  79. package/components/buttons/FSButtonPreviousMini.vue +1 -0
  80. package/components/buttons/FSButtonRedoMini.vue +1 -0
  81. package/components/buttons/FSButtonRemoveMini.vue +1 -0
  82. package/components/buttons/FSButtonSaveMini.vue +1 -0
  83. package/components/buttons/FSButtonSearchMini.vue +1 -0
  84. package/components/buttons/FSButtonUndoMini.vue +1 -0
  85. package/components/buttons/FSButtonUpdateMini.vue +1 -0
  86. package/components/buttons/FSButtonValidateMini.vue +1 -0
  87. package/components/deviceOrganisations/FSConnectivity.vue +11 -1
  88. package/components/deviceOrganisations/FSConnectivityCard.vue +20 -49
  89. package/components/deviceOrganisations/FSStatus.vue +11 -1
  90. package/components/deviceOrganisations/FSStatusCard.vue +40 -60
  91. package/components/deviceOrganisations/FSStatusesCarousel.vue +1 -0
  92. package/components/deviceOrganisations/FSStatusesRow.vue +10 -5
  93. package/components/deviceOrganisations/FSWorstAlert.vue +19 -13
  94. package/components/deviceOrganisations/FSWorstAlertCard.vue +36 -47
  95. package/components/fields/FSAutocompleteField.vue +501 -323
  96. package/components/fields/FSAutocompleteTag.vue +100 -0
  97. package/components/fields/FSBaseField.vue +26 -16
  98. package/components/fields/FSColorField.vue +63 -55
  99. package/components/fields/FSCommentField.vue +93 -0
  100. package/components/fields/FSDateField.vue +2 -2
  101. package/components/fields/FSDateRangeField.vue +2 -2
  102. package/components/fields/FSDateTimeField.vue +4 -3
  103. package/components/fields/FSDateTimeRangeField.vue +7 -6
  104. package/components/fields/FSEntityFieldUI.vue +271 -0
  105. package/components/fields/FSGradientField.vue +27 -33
  106. package/components/fields/FSIconField.vue +2 -3
  107. package/components/fields/FSMagicConfigField.vue +28 -19
  108. package/components/fields/FSMagicField.vue +25 -17
  109. package/components/fields/FSNumberField.vue +9 -7
  110. package/components/fields/FSPasswordField.vue +2 -3
  111. package/components/fields/FSRichTextField.vue +32 -5
  112. package/components/fields/FSSearchField.vue +2 -2
  113. package/components/fields/FSSelectField.vue +483 -251
  114. package/components/fields/FSTagField.vue +4 -6
  115. package/components/fields/FSTermField.vue +25 -13
  116. package/components/fields/FSTextArea.vue +18 -6
  117. package/components/fields/FSTextField.vue +13 -10
  118. package/components/fields/FSTimeField.vue +1 -1
  119. package/components/fields/FSTimeSlotField.vue +3 -4
  120. package/components/fields/FSTimeStepField.vue +157 -0
  121. package/components/fields/FSTranslateField.vue +3 -2
  122. package/components/fields/FSTranslateRichTextField.vue +4 -3
  123. package/components/fields/FSTranslateTextArea.vue +233 -0
  124. package/components/fields/FSTreeViewField.vue +7 -9
  125. package/components/fields/periodicField/FSPeriodicDailyField.vue +120 -0
  126. package/components/fields/periodicField/FSPeriodicField.vue +131 -0
  127. package/components/fields/periodicField/FSPeriodicMonthlyField.vue +222 -0
  128. package/components/fields/periodicField/FSPeriodicWeeklyField.vue +120 -0
  129. package/components/fields/periodicField/FSPeriodicYearlyField.vue +144 -0
  130. package/components/lists/FSDataIteratorItem.vue +8 -10
  131. package/components/lists/FSDataTableUI.vue +47 -39
  132. package/components/lists/FSFilterButton.vue +20 -22
  133. package/components/lists/FSHiddenButton.vue +10 -12
  134. package/components/lists/FSLoadDataTable.vue +4 -6
  135. package/components/lists/FSSimpleList.vue +229 -0
  136. package/components/map/FSMap.vue +256 -405
  137. package/components/map/FSMapFeatureGroup.vue +51 -0
  138. package/components/map/FSMapLayerButton.vue +6 -3
  139. package/components/map/FSMapMarker.vue +116 -0
  140. package/components/map/FSMapMarkerClusterGroup.vue +72 -0
  141. package/components/map/FSMapOverlay.vue +69 -83
  142. package/components/map/FSMapPolygon.vue +81 -0
  143. package/components/map/FSMapTileLayer.vue +50 -0
  144. package/components/map/keys.ts +4 -0
  145. package/components/selects/FSSelectAutoRefresh.vue +1 -1
  146. package/components/selects/FSSelectDashboardVariableType.vue +1 -1
  147. package/components/selects/FSSelectDateSetting.vue +3 -3
  148. package/components/selects/FSSelectDays.vue +1 -1
  149. package/components/selects/FSSelectListMode.vue +51 -0
  150. package/components/selects/FSSelectMonths.vue +67 -0
  151. package/components/tiles/FSChartTileUI.vue +116 -0
  152. package/components/tiles/FSCommentTileUI.vue +149 -0
  153. package/components/tiles/FSDashboardOrganisationTileUI.vue +6 -8
  154. package/components/tiles/FSDashboardOrganisationTypeTileUI.vue +6 -8
  155. package/components/tiles/FSDashboardShallowTileUI.vue +6 -8
  156. package/components/tiles/FSDeviceOrganisationTileUI.vue +30 -18
  157. package/components/tiles/FSFolderTileUI.vue +6 -6
  158. package/components/tiles/FSGroupTileUI.vue +31 -22
  159. package/components/tiles/FSLoadTile.vue +5 -7
  160. package/components/tiles/FSLocationTileUI.vue +157 -0
  161. package/components/tiles/FSModelTileUI.vue +18 -0
  162. package/components/tiles/FSServiceAccountOrganisationTileUI.vue +142 -0
  163. package/components/tiles/FSSimpleTileUI.vue +57 -36
  164. package/components/tiles/FSTile.vue +115 -55
  165. package/components/tiles/FSUserOrganisationTileUI.vue +25 -35
  166. package/components/toggleSets/FSToggleSetPosition.vue +1 -1
  167. package/components/views/FSEntityHeader.vue +343 -0
  168. package/components/views/FSEntityView.vue +163 -0
  169. package/components/views/FSListHeader.vue +83 -0
  170. package/components/views/FSListView.vue +83 -0
  171. package/components/views/FSSkeletonView.vue +100 -0
  172. package/composables/useAddress.ts +2 -2
  173. package/composables/useColors.ts +15 -5
  174. package/composables/useMagicFieldProvider.ts +7 -6
  175. package/composables/useSlots.ts +51 -28
  176. package/models/agenda.ts +9 -0
  177. package/models/deviceAlerts.ts +1 -1
  178. package/models/deviceConnectivities.ts +1 -1
  179. package/models/index.ts +1 -0
  180. package/models/magicFields.ts +1 -0
  181. package/models/map.ts +2 -2
  182. package/models/rules.ts +10 -5
  183. package/models/tables.ts +3 -1
  184. package/package.json +4 -4
  185. package/styles/components/fs_agenda.scss +32 -0
  186. package/styles/components/fs_agenda_event.scss +41 -0
  187. package/styles/components/fs_agenda_hours_col.scss +4 -0
  188. package/styles/components/fs_agenda_hours_row.scss +13 -0
  189. package/styles/components/fs_agenda_time_line_marker.scss +19 -0
  190. package/styles/components/fs_card.scss +0 -1
  191. package/styles/components/fs_clickable.scss +5 -3
  192. package/styles/components/fs_color_field.scss +1 -0
  193. package/styles/components/fs_data_table.scss +2 -0
  194. package/styles/components/fs_dialog.scss +11 -15
  195. package/styles/components/fs_edit_image.scss +8 -0
  196. package/styles/components/fs_fade_out.scss +2 -1
  197. package/styles/components/fs_filter_button.scss +1 -6
  198. package/styles/components/fs_gradient_field.scss +11 -11
  199. package/styles/components/fs_magic_config_field.scss +2 -2
  200. package/styles/components/fs_map.scss +36 -30
  201. package/styles/components/fs_option_group.scss +15 -5
  202. package/styles/components/fs_radio.scss +11 -0
  203. package/styles/components/fs_rich_text_field.scss +2 -1
  204. package/styles/components/fs_search_field.scss +3 -0
  205. package/styles/components/fs_select_date_settings.scss +3 -0
  206. package/styles/components/fs_slide_group.scss +5 -0
  207. package/styles/components/fs_span.scss +2 -1
  208. package/styles/components/fs_switch.scss +1 -0
  209. package/styles/components/fs_tabs.scss +9 -0
  210. package/styles/components/fs_text_area.scss +15 -1
  211. package/styles/components/fs_tile.scss +22 -6
  212. package/styles/components/fs_window.scss +5 -0
  213. package/styles/components/fs_wrap_group.scss +4 -0
  214. package/styles/components/index.scss +6 -1
  215. package/styles/globals/overrides.scss +22 -19
  216. package/styles/globals/text_fonts.scss +17 -55
  217. package/tools/alertsTools.ts +54 -0
  218. package/tools/chartsTools.ts +300 -0
  219. package/tools/index.ts +2 -0
  220. package/utils/badge.ts +9 -0
  221. package/utils/filter.ts +18 -0
  222. package/utils/index.ts +2 -0
  223. package/utils/leafletMarkers.ts +8 -2
  224. package/utils/statuses.ts +1 -1
  225. package/utils/time.ts +27 -1
  226. package/components/autocompletes/FSAutocompleteTag.vue +0 -138
  227. package/components/map/FSMapEditPointAddressOverlay.vue +0 -164
  228. package/styles/components/fs_map_overlay.scss +0 -38
@@ -1,253 +1,382 @@
1
1
  <template>
2
- <template
2
+ <FSCol
3
3
  v-if="$props.loading"
4
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
5
+ <FSLoader
6
+ v-if="!$props.hideHeader"
7
+ variant="text-overline"
8
+ />
9
+ <FSLoader
10
+ v-if="$props.loading"
11
+ width="100%"
12
+ :height="['40px', '36px']"
13
+ />
14
+ </FSCol>
15
+ <FSCol
16
+ v-else-if="isExtraSmall"
19
17
  >
20
- <template
21
- v-if="isExtraSmall"
18
+ <FSTextField
19
+ :validationValue="$props.modelValue"
20
+ :description="$props.description"
21
+ :hideHeader="$props.hideHeader"
22
+ :clearable="$props.clearable"
23
+ :editable="$props.editable"
24
+ :required="$props.required"
25
+ :label="$props.label"
26
+ :rules="$props.rules"
27
+ :messages="messages"
28
+ :readonly="true"
29
+ :modelValue="mobileValue"
30
+ @click="openMobileOverlay"
31
+ v-bind="$attrs"
22
32
  >
23
- <FSTextField
24
- :validationValue="$props.modelValue"
25
- :description="$props.description"
26
- :hideHeader="$props.hideHeader"
27
- :clearable="$props.clearable"
28
- :editable="$props.editable"
29
- :required="$props.required"
30
- :label="$props.label"
31
- :rules="$props.rules"
32
- :messages="messages"
33
- :readonly="true"
34
- :modelValue="mobileValue"
35
- @update:modelValue="$emit('update:modelValue', $event)"
36
- @click="openMobileOverlay"
37
- v-bind="$attrs"
33
+ <template
34
+ v-for="(_, name) in $slots"
35
+ v-slot:[name]="slotData"
38
36
  >
39
- <template
40
- v-for="(_, name) in $slots"
41
- v-slot:[name]="slotData"
42
- >
43
- <slot
44
- :name="name"
45
- v-bind="slotData"
46
- />
47
- </template>
48
- <template
49
- v-if="mobileSelectionProps"
50
- #prepend-inner
37
+ <slot
38
+ :name="name"
39
+ v-bind="slotData"
40
+ />
41
+ </template>
42
+ <template
43
+ #prepend-inner
44
+ >
45
+ <slot
46
+ v-if="selectedItem && showExtra"
47
+ name="item-prepend"
48
+ v-bind="{ item: selectedItem }"
49
+ />
50
+ </template>
51
+ <template
52
+ #clear
53
+ >
54
+ <FSRow
55
+ :wrap="false"
51
56
  >
52
57
  <slot
53
- name="selection-mobile"
54
- v-bind="mobileSelectionProps"
58
+ v-if="selectedItem && showExtra"
59
+ name="item-append"
60
+ v-bind="{ item: selectedItem }"
55
61
  />
56
- </template>
57
- <template
58
- #append-inner
59
- >
60
62
  <slot
61
- name="append-inner"
63
+ name="clear"
62
64
  >
63
65
  <FSButton
64
- icon="mdi-chevron-down"
66
+ v-if="$props.clearable && $props.editable && !!$props.modelValue"
67
+ icon="mdi-close"
65
68
  variant="icon"
66
- :editable="$props.editable"
67
69
  :color="ColorEnum.Dark"
68
- @click="openMobileOverlay"
70
+ @click="onClear"
69
71
  />
70
72
  </slot>
71
- </template>
72
- </FSTextField>
73
- <FSDialogMenu
74
- v-model="dialog"
73
+ </FSRow>
74
+ </template>
75
+ <template
76
+ #append-inner
75
77
  >
76
- <template
77
- #body
78
+ <FSButton
79
+ icon="mdi-chevron-down"
80
+ variant="icon"
81
+ :editable="$props.editable"
82
+ :color="ColorEnum.Dark"
83
+ @click="openMobileOverlay"
84
+ />
85
+ </template>
86
+ </FSTextField>
87
+ <FSSlideGroup
88
+ v-if="$props.multiple && Array.isArray($props.modelValue)"
89
+ >
90
+ <FSCard
91
+ v-for="(item, index) in $props.items.filter((item: any) => $props.modelValue.includes(item[$props.itemValue!]))"
92
+ variant="standard"
93
+ :height="['40px', '36px']"
94
+ :color="ColorEnum.Light"
95
+ :border="false"
96
+ :key="index"
97
+ >
98
+ <FSRow
99
+ align="center-left"
100
+ padding="0 8px"
101
+ :wrap="false"
78
102
  >
79
- <FSSearchField
80
- :hideHeader="true"
81
- v-model="search"
103
+ <slot
104
+ name="item-prepend"
105
+ v-bind="{ item }"
82
106
  />
83
- <FSFadeOut
84
- :height="height"
107
+ <FSSpan>
108
+ {{ item[$props.itemTitle!] }}
109
+ </FSSpan>
110
+ <slot
111
+ name="item-append"
112
+ v-bind="{ item }"
113
+ />
114
+ <FSButton
115
+ icon="mdi-close"
116
+ variant="icon"
117
+ :color="ColorEnum.Dark"
118
+ @click="() => onCheckboxChange(item[$props.itemValue!])"
119
+ />
120
+ </FSRow>
121
+ </FSCard>
122
+ </FSSlideGroup>
123
+ <FSDialogMenu
124
+ v-model="dialog"
125
+ >
126
+ <template
127
+ #body
128
+ >
129
+ <FSSearchField
130
+ :hideHeader="true"
131
+ v-model="search"
132
+ />
133
+ <FSFadeOut
134
+ :maxHeight="maxHeight"
135
+ >
136
+ <FSCol
137
+ gap="12px"
85
138
  >
86
- <FSCol
87
- v-if="$props.multiple"
88
- gap="12px"
139
+ <FSRow
140
+ v-for="(item, index) in searchItems"
141
+ :key="index"
89
142
  >
90
- <FSRow
91
- v-for="(item, index) in searchItems"
92
- :key="index"
143
+ <FSCheckbox
144
+ v-if="$props.multiple"
145
+ :label="item[$props.itemTitle!]"
146
+ :editable="$props.editable"
147
+ :modelValue="$props.modelValue?.includes(item[$props.itemValue!])"
148
+ @update:modelValue="() => onCheckboxChange(item[$props.itemValue!])"
93
149
  >
94
- <FSCheckbox
95
- :label="item[$props.itemTitle]"
96
- :editable="$props.editable"
97
- :modelValue="$props.modelValue?.includes(item[$props.itemValue])"
98
- @update:modelValue="() => onCheckboxChange(item[$props.itemValue])"
150
+ <template
151
+ #label="{ font }"
99
152
  >
100
- <template
101
- #label="{ font }"
153
+ <FSRow
154
+ align="center-left"
155
+ :wrap="false"
102
156
  >
103
157
  <slot
104
- name="item-label"
105
- v-bind="mobileItemProps(item, font)"
158
+ name="item-prepend"
159
+ v-bind="{ item }"
106
160
  />
107
- </template>
108
- </FSCheckbox>
109
- </FSRow>
110
- </FSCol>
111
- <FSRadioGroup
112
- v-else
113
- gap="12px"
114
- :values="searchItems.map((item: any) => ({ value: item[$props.itemValue], label: item[$props.itemTitle], item: item }))"
115
- :editable="$props.editable"
116
- :modelValue="$props.modelValue"
117
- @update:modelValue="onRadioChange"
118
- >
119
- <template
120
- #label="{ item, font }"
161
+ <FSSpan
162
+ :font="font"
163
+ >
164
+ {{ item[$props.itemTitle!] }}
165
+ </FSSpan>
166
+ </FSRow>
167
+ </template>
168
+ </FSCheckbox>
169
+ <FSRadio
170
+ v-else
171
+ :selected="$props.modelValue === item[$props.itemValue!]"
172
+ :label="item[$props.itemTitle!]"
173
+ :editable="$props.editable"
174
+ :item="item"
175
+ :modelValue="item[$props.itemValue!]"
176
+ @update:modelValue="() => onRadioChange(item[$props.itemValue!])"
177
+ >
178
+ <template
179
+ #label="{ font }"
180
+ >
181
+ <FSRow
182
+ align="center-left"
183
+ :wrap="false"
184
+ >
185
+ <slot
186
+ name="item-prepend"
187
+ v-bind="{ item }"
188
+ />
189
+ <FSSpan
190
+ :font="font"
191
+ >
192
+ {{ item[$props.itemTitle!] }}
193
+ </FSSpan>
194
+ </FSRow>
195
+ </template>
196
+ </FSRadio>
197
+ <FSRow
198
+ align="center-right"
121
199
  >
122
200
  <slot
123
- name="item-label"
124
- v-bind="mobileItemProps(item, font)"
201
+ name="item-append"
202
+ v-bind="{ item }"
125
203
  />
126
- </template>
127
- </FSRadioGroup>
128
- </FSFadeOut>
129
- </template>
130
- </FSDialogMenu>
131
- </template>
132
- <template
204
+ </FSRow>
205
+ </FSRow>
206
+ </FSCol>
207
+ </FSFadeOut>
208
+ <FSRow
209
+ v-if="allowAddItem"
210
+ padding="4px 3px"
211
+ >
212
+ <FSButton
213
+ variant="icon"
214
+ :label="$tr('ui.autocomplete-field.add-item', 'Add new item')"
215
+ :color="ColorEnum.Primary"
216
+ @click="$emit('add:item', search)"
217
+ />
218
+ </FSRow>
219
+ <FSRow
220
+ v-if="!allowAddItem && searchItems.length === 0"
221
+ padding="4px 3px"
222
+ >
223
+ <FSSpan>
224
+ {{ $tr("ui.autocomplete-field.no-data", "No data") }}
225
+ </FSSpan>
226
+ </FSRow>
227
+ </template>
228
+ </FSDialogMenu>
229
+ </FSCol>
230
+ <FSBaseField
231
+ v-else
232
+ :description="$props.description"
233
+ :hideHeader="$props.hideHeader"
234
+ :required="$props.required"
235
+ :editable="$props.editable"
236
+ :label="$props.label"
237
+ :messages="messages"
238
+ >
239
+ <FSToggleSet
240
+ v-if="$props.toggleSet"
241
+ :editable="$props.editable"
242
+ :multiple="$props.multiple"
243
+ :required="$props.required"
244
+ :values="$props.items"
245
+ :rules="$props.rules"
246
+ :modelValue="$props.modelValue"
247
+ @update:modelValue="$emit('update:modelValue', $event)"
248
+ v-bind="$attrs"
249
+ >
250
+ <template
251
+ v-for="(_, name) in toggleSetSlots"
252
+ v-slot:[name]="slotData"
253
+ >
254
+ <slot
255
+ :name="`toggle-set-${name}`"
256
+ v-bind="slotData"
257
+ />
258
+ </template>
259
+ </FSToggleSet>
260
+ <FSCol
133
261
  v-else
134
262
  >
135
- <FSBaseField
136
- :description="$props.description"
137
- :hideHeader="$props.hideHeader"
138
- :required="$props.required"
139
- :editable="$props.editable"
140
- :label="$props.label"
141
- :messages="messages"
263
+ <v-autocomplete
264
+ class="fs-autocomplete-field"
265
+ variant="outlined"
266
+ :clearable="$props.clearable && $props.editable && !!$props.modelValue"
267
+ :itemTitle="$props.itemTitle"
268
+ :itemValue="$props.itemValue"
269
+ :readonly="!$props.editable"
270
+ :multiple="$props.multiple"
271
+ :validateOn="validateOn"
272
+ :autoSelectFirst="true"
273
+ :persistentClear="true"
274
+ :listProps="listStyle"
275
+ :returnObject="false"
276
+ :items="$props.items"
277
+ :rules="$props.rules"
278
+ :hideDetails="true"
279
+ :menuIcon="null"
280
+ :style="style"
281
+ :modelValue="$props.modelValue"
282
+ @update:modelValue="onSingleChange"
283
+ @click="onClick"
284
+ @blur="onBlur"
285
+ v-model:search="search"
286
+ v-bind="$attrs"
142
287
  >
143
- <FSToggleSet
144
- v-if="$props.toggleSet"
145
- :editable="$props.editable"
146
- :multiple="$props.multiple"
147
- :required="$props.required"
148
- :values="$props.items"
149
- :rules="$props.rules"
150
- :modelValue="$props.modelValue"
151
- @update:modelValue="$emit('update:modelValue', $event)"
152
- v-bind="$attrs"
288
+ <template
289
+ v-for="(_, name) in autocompleteSlots"
290
+ v-slot:[name]="slotData"
153
291
  >
154
- <template
155
- v-for="(_, name) in toggleSetSlots"
156
- v-slot:[name]="slotData"
157
- >
158
- <slot
159
- :name="`toggle-set-${name}`"
160
- v-bind="slotData"
161
- />
162
- </template>
163
- </FSToggleSet>
164
- <v-autocomplete
165
- v-else
166
- class="fs-autocomplete-field"
167
- variant="outlined"
168
- :clearable="$props.clearable && $props.editable && !!$props.modelValue"
169
- :itemTitle="$props.itemTitle"
170
- :itemValue="$props.itemValue"
171
- :readonly="!$props.editable"
172
- :multiple="$props.multiple"
173
- :validateOn="validateOn"
174
- :autoSelectFirst="true"
175
- :persistentClear="true"
176
- :listProps="listStyle"
177
- :returnObject="false"
178
- :items="$props.items"
179
- :rules="$props.rules"
180
- :hideDetails="true"
181
- :menuIcon="null"
182
- :class="classes"
183
- :style="style"
184
- :modelValue="$props.modelValue"
185
- @update:modelValue="$emit('update:modelValue', $event)"
186
- @click="onClick"
187
- v-model:search="search"
188
- v-bind="$attrs"
292
+ <slot
293
+ :name="`autocomplete-${name}`"
294
+ v-bind="slotData"
295
+ />
296
+ </template>
297
+ <template
298
+ #item="{ props, item }"
189
299
  >
190
- <template
191
- v-for="(_, name) in autocompleteSlots"
192
- v-slot:[name]="slotData"
193
- >
194
- <slot
195
- :name="`autocomplete-${name}`"
196
- v-bind="slotData"
197
- />
198
- </template>
199
- <template
200
- #item="{ props, item }"
300
+ <v-list-item
301
+ v-bind="{ ...props, title: '' }"
201
302
  >
202
- <v-list-item
203
- v-bind="{ ...props, title: '' }"
303
+ <FSRow
304
+ align="center-left"
305
+ :wrap="false"
204
306
  >
205
- <FSRow
206
- align="center-left"
307
+ <FSCheckbox
308
+ v-if="$props.multiple"
309
+ :modelValue="$props.modelValue?.includes(item.raw[$props.itemValue!])"
310
+ @click="props.onClick"
207
311
  >
208
- <FSCheckbox
209
- v-if="$props.multiple"
210
- :modelValue="$props.modelValue?.includes(item.raw[$props.itemValue])"
211
- @click="props.onClick"
312
+ <template
313
+ #label="{ font }"
212
314
  >
213
- <template
214
- #label="{ font }"
315
+ <slot
316
+ name="item-prepend"
317
+ v-bind="{ item: item.raw }"
318
+ />
319
+ <FSSpan
320
+ :font="font"
215
321
  >
216
- <slot
217
- name="item-label"
218
- v-bind="{ item, font }"
219
- >
220
- <FSSpan
221
- :font="font"
222
- >
223
- {{ item.raw[$props.itemTitle] }}
224
- </FSSpan>
225
- </slot>
226
- </template>
227
- </FSCheckbox>
322
+ {{ item.raw[$props.itemTitle!] }}
323
+ </FSSpan>
324
+ </template>
325
+ </FSCheckbox>
326
+ <template
327
+ v-else
328
+ >
329
+ <slot
330
+ name="item-prepend"
331
+ v-bind="{ item: item.raw }"
332
+ />
228
333
  <FSSpan
229
- v-else
334
+ :font="$props.modelValue === item.raw[$props.itemTitle!] ? 'text-button' : 'text-body'"
230
335
  >
231
- <slot
232
- name="item-label"
233
- v-bind="{
234
- item,
235
- font: $props.modelValue === item.raw[$props.itemTitle] ? 'text-button' : 'text-body'
236
- }"
237
- >
238
- <FSSpan
239
- :font="$props.modelValue === item.raw[$props.itemTitle] ? 'text-button' : 'text-body'"
240
- >
241
- {{ item.raw[$props.itemTitle] }}
242
- </FSSpan>
243
- </slot>
336
+ {{ item.raw[$props.itemTitle!] }}
244
337
  </FSSpan>
338
+ </template>
339
+ <FSRow
340
+ align="center-right"
341
+ >
342
+ <slot
343
+ name="item-append"
344
+ v-bind="{ item: item.raw }"
345
+ />
245
346
  </FSRow>
246
- </v-list-item>
247
- </template>
248
- <template
249
- #clear
347
+ </FSRow>
348
+ </v-list-item>
349
+ </template>
350
+ <template
351
+ #prepend-inner
352
+ >
353
+ <slot
354
+ v-if="selectedItem && showExtra"
355
+ name="item-prepend"
356
+ v-bind="{ item: selectedItem }"
357
+ />
358
+ </template>
359
+ <template
360
+ v-if="$props.multiple"
361
+ #selection="{ index }"
362
+ >
363
+ <FSSpan
364
+ v-if="index === $props.modelValue.length - 1 && showExtra"
365
+ >
366
+ {{ $props.placeholder }}
367
+ </FSSpan>
368
+ </template>
369
+ <template
370
+ #clear
371
+ >
372
+ <FSRow
373
+ :wrap="false"
250
374
  >
375
+ <slot
376
+ v-if="selectedItem && showExtra"
377
+ name="item-append"
378
+ v-bind="{ item: selectedItem }"
379
+ />
251
380
  <slot
252
381
  name="clear"
253
382
  >
@@ -256,63 +385,101 @@
256
385
  icon="mdi-close"
257
386
  variant="icon"
258
387
  :color="ColorEnum.Dark"
259
- @click="$emit('update:modelValue', null)"
388
+ @click="onClear"
260
389
  />
261
390
  </slot>
262
- </template>
263
- <template
264
- #append-inner
391
+ </FSRow>
392
+ </template>
393
+ <template
394
+ #append-inner
395
+ >
396
+ <slot
397
+ name="append-inner"
265
398
  >
266
- <slot
267
- name="append-inner"
268
- >
269
- <FSButton
270
- icon="mdi-chevron-down"
271
- variant="icon"
272
- :editable="$props.editable"
273
- :color="ColorEnum.Dark"
274
- />
275
- </slot>
276
- </template>
277
- <template
278
- #no-data
399
+ <FSButton
400
+ icon="mdi-chevron-down"
401
+ variant="icon"
402
+ :editable="$props.editable"
403
+ :color="ColorEnum.Dark"
404
+ />
405
+ </slot>
406
+ </template>
407
+ <template
408
+ #append-item
409
+ >
410
+ <FSRow
411
+ v-if="allowAddItem"
412
+ padding="15px"
279
413
  >
280
- <FSRow
281
- v-if="showSearch"
282
- padding="17px"
283
- >
284
- <FSButton
285
- variant="icon"
286
- :label="$tr('ui.common.add', 'Add this item')"
287
- :color="ColorEnum.Primary"
288
- @click="$emit('add:item', search)"
289
- />
290
- </FSRow>
291
- <FSRow
292
- v-else
293
- padding="17px"
294
- >
295
- <FSSpan>
296
- {{ $tr("ui.common.no-data", "No data") }}
297
- </FSSpan>
298
- </FSRow>
299
- </template>
300
- </v-autocomplete>
301
- </FSBaseField>
302
- </template>
303
- </template>
414
+ <FSButton
415
+ variant="icon"
416
+ :label="$tr('ui.autocomplete.add-item', 'Add new item')"
417
+ :color="ColorEnum.Primary"
418
+ @click="$emit('add:item', search)"
419
+ />
420
+ </FSRow>
421
+ </template>
422
+ <template
423
+ #no-data
424
+ >
425
+ <FSRow
426
+ v-if="!allowAddItem"
427
+ padding="15px"
428
+ >
429
+ <FSSpan>
430
+ {{ $tr("ui.common.no-data", "No data") }}
431
+ </FSSpan>
432
+ </FSRow>
433
+ </template>
434
+ </v-autocomplete>
435
+ <FSSlideGroup
436
+ v-if="$props.multiple && Array.isArray($props.modelValue)"
437
+ >
438
+ <FSCard
439
+ v-for="(item, index) in $props.items.filter((item: any) => $props.modelValue.includes(item[$props.itemValue!]))"
440
+ variant="standard"
441
+ :height="['40px', '36px']"
442
+ :color="ColorEnum.Light"
443
+ :border="false"
444
+ :key="index"
445
+ >
446
+ <FSRow
447
+ align="center-left"
448
+ padding="0 8px"
449
+ >
450
+ <slot
451
+ name="item-prepend"
452
+ v-bind="{ item }"
453
+ />
454
+ <FSSpan>
455
+ {{ item[$props.itemTitle!] }}
456
+ </FSSpan>
457
+ <slot
458
+ name="item-append"
459
+ v-bind="{ item }"
460
+ />
461
+ <FSButton
462
+ icon="mdi-close"
463
+ variant="icon"
464
+ :color="ColorEnum.Dark"
465
+ @click="() => onCheckboxChange(item[$props.itemValue!])"
466
+ />
467
+ </FSRow>
468
+ </FSCard>
469
+ </FSSlideGroup>
470
+ </FSCol>
471
+ </FSBaseField>
304
472
  </template>
305
473
 
306
474
  <script lang="ts">
307
- import type { PropType} from "vue";
308
- import { computed, defineComponent, ref } from "vue";
475
+ import { computed, defineComponent, type PropType, ref, type Slot, type StyleValue, watch } from "vue";
309
476
 
310
477
  import { useBreakpoints, useColors, useRules, useSlots } from "@dative-gpi/foundation-shared-components/composables";
311
478
  import { ColorEnum } from "@dative-gpi/foundation-shared-components/models";
312
479
 
313
480
  import FSSearchField from "./FSSearchField.vue";
314
481
  import FSDialogMenu from "../FSDialogMenu.vue";
315
- import FSRadioGroup from "../FSRadioGroup.vue";
482
+ import FSSlideGroup from "../FSSlideGroup.vue";
316
483
  import FSToggleSet from "../FSToggleSet.vue";
317
484
  import FSBaseField from "./FSBaseField.vue";
318
485
  import FSTextField from "./FSTextField.vue";
@@ -320,6 +487,8 @@ import FSCheckbox from "../FSCheckbox.vue";
320
487
  import FSFadeOut from "../FSFadeOut.vue";
321
488
  import FSButton from "../FSButton.vue";
322
489
  import FSLoader from "../FSLoader.vue";
490
+ import FSRadio from "../FSRadio.vue";
491
+ import FSCard from "../FSCard.vue";
323
492
  import FSSpan from "../FSSpan.vue";
324
493
  import FSCol from "../FSCol.vue";
325
494
  import FSRow from "../FSRow.vue";
@@ -330,7 +499,7 @@ export default defineComponent({
330
499
  components: {
331
500
  FSSearchField,
332
501
  FSDialogMenu,
333
- FSRadioGroup,
502
+ FSSlideGroup,
334
503
  FSBaseField,
335
504
  FSTextField,
336
505
  FSToggleSet,
@@ -338,6 +507,8 @@ export default defineComponent({
338
507
  FSFadeOut,
339
508
  FSButton,
340
509
  FSLoader,
510
+ FSRadio,
511
+ FSCard,
341
512
  FSSpan,
342
513
  FSCol,
343
514
  FSRow
@@ -348,6 +519,11 @@ export default defineComponent({
348
519
  required: false,
349
520
  default: null
350
521
  },
522
+ placeholder: {
523
+ type: String as PropType<string | null>,
524
+ required: false,
525
+ default: null
526
+ },
351
527
  description: {
352
528
  type: String as PropType<string | null>,
353
529
  required: false,
@@ -440,8 +616,9 @@ export default defineComponent({
440
616
 
441
617
  const dialog = ref(false);
442
618
  const search = ref("");
619
+ const showExtra = ref(true);
443
620
 
444
- const style = computed((): { [key: string]: string | undefined } => {
621
+ const style = computed((): StyleValue => {
445
622
  if (!props.editable) {
446
623
  return {
447
624
  "--fs-autocomplete-field-cursor": "default",
@@ -462,101 +639,79 @@ export default defineComponent({
462
639
  };
463
640
  });
464
641
 
465
- const autocompleteSlots = computed((): any => {
466
- return Object.keys(slots).filter(k => k.startsWith("autocomplete-")).reduce((acc, key) => {
642
+ const autocompleteSlots = computed((): { [key: string]: Slot<any> } => {
643
+ return Object.keys(slots).filter(k => k.startsWith("autocomplete-")).reduce((acc: { [key: string]: Slot<any> }, key) => {
467
644
  acc[key.substring("autocomplete-".length)] = slots[key];
468
645
  return acc;
469
646
  }, {});
470
647
  });
471
648
 
472
- const toggleSetSlots = computed((): any => {
473
- return Object.keys(slots).filter(k => k.startsWith("toggle-set-")).reduce((acc, key) => {
649
+ const toggleSetSlots = computed((): { [key: string]: Slot<any> } => {
650
+ return Object.keys(slots).filter(k => k.startsWith("toggle-set-")).reduce((acc: { [key: string]: Slot<any> }, key) => {
474
651
  acc[key.substring("toggle-set-".length)] = slots[key];
475
652
  return acc;
476
653
  }, {});
477
654
  });
478
655
 
479
- const listStyle = computed((): any => {
656
+ const listStyle = computed((): { style: StyleValue } => {
480
657
  return {
481
658
  style: style.value
482
659
  };
483
660
  });
484
661
 
485
- const classes = computed((): string[] => {
486
- const classNames = ["fs-autocomplete-field"];
487
- if (props.multiple) {
488
- classNames.push("fs-autocomplete-multiple-field");
489
- }
490
- return classNames;
491
- });
492
-
493
662
  const messages = computed((): string[] => props.messages ?? getMessages(props.modelValue, props.rules));
494
663
 
495
- const searchItems = computed(() => {
664
+ const searchItems = computed((): any[] => {
496
665
  return props.items.filter((item: any) => {
497
666
  return item[props.itemTitle].toLowerCase().includes(search.value.toLowerCase());
498
667
  });
499
668
  });
500
669
 
501
- const height = computed(() => {
502
- const other = 8 + 8 // Paddings
503
- + (isMobileSized ? 36 : 40) + 8; // Header
504
- return `calc(100vh - 40px - ${other}px)`;
670
+ const selectedItem = computed((): any => {
671
+ if (props.multiple) {
672
+ return null;
673
+ }
674
+ if (Array.isArray(props.modelValue) && props.modelValue.length > 0) {
675
+ return props.items.find((item: any) => item[props.itemValue] === props.modelValue[0]) ?? null;
676
+ }
677
+ else if (props.modelValue) {
678
+ return props.items.find((item: any) => item[props.itemValue] === props.modelValue) ?? null;
679
+ }
680
+ return null;
505
681
  });
506
682
 
507
- const mobileValue = computed((): string | null => {
508
- if (props.multiple) {
509
- if (Array.isArray(props.modelValue)) {
510
- return props.modelValue.map((value: any) => {
511
- const item = props.items.find((item: object) => item[props.itemValue] === value);
512
- if (item) {
513
- return item[props.itemTitle];
514
- }
515
- }).filter(value => !!value).join(", ");
516
- }
683
+ const selectedItems = computed((): any[] => {
684
+ if (Array.isArray(props.modelValue) && props.modelValue.length > 0) {
685
+ return props.items.filter((item: any) => props.modelValue.includes(item[props.itemValue]));
517
686
  }
518
- if (props.modelValue) {
519
- const item = props.items.find((item: object) => item[props.itemValue] === props.modelValue);
687
+ else if (props.modelValue) {
688
+ const item = props.items.find((item: any) => item[props.itemValue] === props.modelValue);
520
689
  if (item) {
521
- return item[props.itemTitle];
690
+ return [item];
522
691
  }
523
692
  }
524
- return null;
693
+ return [];
525
694
  });
526
695
 
527
- const mobileSelectionProps = computed((): any | null => {
528
- const item = props.items.find((item: any) => item[props.itemValue] === props.modelValue);
529
- if (item) {
530
- return {
531
- item: {
532
- title: "",
533
- value: item[props.itemValue],
534
- props: {
535
- title: item[props.itemTitle],
536
- value: item[props.itemValue]
537
- },
538
- raw: { ...item }
539
- },
540
- font: "text-body"
541
- };
542
- }
543
- return null;
696
+ const allowAddItem = computed((): boolean => {
697
+ return props.showSearch && search.value.trim().length > 0;
544
698
  });
545
699
 
546
- const mobileItemProps = (item: any, font: "text-body" | "text-button" | null): any => {
547
- return {
548
- item: {
549
- title: "",
550
- value: item[props.itemValue],
551
- props: {
552
- title: item[props.itemTitle],
553
- value: item[props.itemValue]
554
- },
555
- raw: { ...item }
556
- },
557
- font
700
+ const maxHeight = computed(() => {
701
+ const other = 8 + 8 // Paddings
702
+ + (isMobileSized ? 36 : 40) + 8; // Header
703
+ return `calc(100vh - 40px - ${other}px)`;
704
+ });
705
+
706
+ const mobileValue = computed((): string | null => {
707
+ if (props.multiple && Array.isArray(props.modelValue) && props.modelValue.length > 0) {
708
+ return props.placeholder;
558
709
  }
559
- };
710
+ if (selectedItem.value) {
711
+ return selectedItem.value[props.itemTitle];
712
+ }
713
+ return null;
714
+ });
560
715
 
561
716
  const openMobileOverlay = () => {
562
717
  if (!props.editable) {
@@ -592,34 +747,57 @@ export default defineComponent({
592
747
  }
593
748
  };
594
749
 
595
- const onClick = () => {
596
- if (props.modelValue && !props.multiple) {
597
- search.value="";
598
- }
750
+ const onSingleChange = (value: string) => {
751
+ emit("update:modelValue", value);
752
+ if (value && !Array.isArray(value)) {
753
+ showExtra.value = true;
754
+ }
755
+ };
756
+
757
+ const onClear = () => {
758
+ emit("update:modelValue", null);
759
+ search.value = "";
599
760
  };
600
761
 
762
+ const onClick = (): void => {
763
+ search.value = "";
764
+ showExtra.value = false;
765
+ };
766
+
767
+ const onBlur = () => {
768
+ showExtra.value = true;
769
+ };
770
+
771
+ watch(search, () => {
772
+ emit("update:search", search.value);
773
+ });
774
+
601
775
  return {
602
- mobileSelectionProps,
603
776
  autocompleteSlots,
604
777
  toggleSetSlots,
778
+ selectedItems,
605
779
  isExtraSmall,
780
+ allowAddItem,
781
+ selectedItem,
606
782
  mobileValue,
607
783
  searchItems,
608
784
  validateOn,
609
785
  ColorEnum,
610
786
  listStyle,
787
+ maxHeight,
788
+ showExtra,
611
789
  messages,
612
- classes,
613
790
  dialog,
614
- height,
615
791
  search,
616
792
  slots,
617
793
  style,
618
794
  openMobileOverlay,
619
795
  onCheckboxChange,
620
- mobileItemProps,
796
+ onSingleChange,
621
797
  onRadioChange,
622
- onClick
798
+ onClear,
799
+ onClick,
800
+ onBlur
623
801
  };
624
802
  }
625
803
  });