@dative-gpi/foundation-shared-components 1.0.54 → 1.0.56

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.
@@ -12,7 +12,7 @@ import { computed, defineComponent, type PropType } from "vue";
12
12
 
13
13
  import { IMAGE_RAW_URL } from "@dative-gpi/foundation-shared-services/config/urls";
14
14
 
15
- import { useImage } from "@dative-gpi/foundation-shared-services/composables";
15
+ import { useAppAuthToken, useImage } from "@dative-gpi/foundation-shared-services/composables";
16
16
 
17
17
  import FSImageUI from "./FSImageUI.vue";
18
18
 
@@ -30,9 +30,10 @@ export default defineComponent({
30
30
  },
31
31
  setup(props) {
32
32
  const { get: getImage, entity: image } = useImage();
33
+ const { authToken } = useAppAuthToken();
33
34
 
34
35
  const source = computed(() => {
35
- return props.imageId ? IMAGE_RAW_URL(props.imageId) : null;
36
+ return props.imageId ? IMAGE_RAW_URL(props.imageId, authToken.value) : null;
36
37
  });
37
38
 
38
39
  const onError = (): void => {
@@ -179,8 +179,14 @@
179
179
  </FSIcon>
180
180
  </template>
181
181
  </FSRow>
182
+ <FSText
183
+ v-if="readonly && !$props.modelValue && $props.emptyLabel"
184
+ variant="soft"
185
+ >
186
+ {{ $props.emptyLabel }}
187
+ </FSText>
182
188
  <div
183
- class="fs-rich-text-field"
189
+ :class="classes"
184
190
  :style="style"
185
191
  >
186
192
  <div
@@ -237,6 +243,7 @@ import FSAutoCompleteField from "./FSAutocompleteField.vue";
237
243
  import FSTextField from "./FSTextField.vue";
238
244
  import FSIcon from "../FSIcon.vue";
239
245
  import FSCard from "../FSCard.vue";
246
+ import FSText from "../FSText.vue";
240
247
  import FSCol from "../FSCol.vue";
241
248
  import FSRow from "../FSRow.vue";
242
249
 
@@ -245,6 +252,7 @@ export default defineComponent({
245
252
  components: {
246
253
  FSAutoCompleteField,
247
254
  FSTextField,
255
+ FSText,
248
256
  FSIcon,
249
257
  FSCard,
250
258
  FSCol,
@@ -261,6 +269,11 @@ export default defineComponent({
261
269
  required: false,
262
270
  default: null
263
271
  },
272
+ emptyLabel: {
273
+ type: String as PropType<string | null>,
274
+ required: false,
275
+ default: null
276
+ },
264
277
  modelValue: {
265
278
  type: String as PropType<string | null>,
266
279
  required: false,
@@ -350,6 +363,10 @@ export default defineComponent({
350
363
  onError: console.error
351
364
  }
352
365
 
366
+ const isEmpty = computed((): boolean => {
367
+ return editor.getEditorState().isEmpty();
368
+ });
369
+
353
370
  const editor = createEditor(config);
354
371
 
355
372
  onMounted((): void => {
@@ -423,6 +440,14 @@ export default defineComponent({
423
440
  }
424
441
  });
425
442
 
443
+ const classes = computed((): string[] => {
444
+ const innerClasses = ["fs-rich-text-field"];
445
+ if (!readonly.value) {
446
+ innerClasses.push("fs-rich-text-field-readonly");
447
+ }
448
+ return innerClasses;
449
+ });
450
+
426
451
  const toolbarColors = computed((): { [code: string]: string } => {
427
452
  if (props.editable) {
428
453
  return {
@@ -655,8 +680,11 @@ export default defineComponent({
655
680
  toolbarColors,
656
681
  menuVariable,
657
682
  UNDO_COMMAND,
683
+ ColorEnum,
658
684
  readonly,
659
685
  linkUrl,
686
+ classes,
687
+ isEmpty,
660
688
  editor,
661
689
  isLink,
662
690
  style,
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <FSTextField
3
+ class="fs-search-field"
3
4
  :editable="$props.editable"
4
5
  :placeholder="placeholder"
5
6
  @keydown.enter="onSearch"
@@ -697,7 +697,7 @@ import { useBreakpoints, useColors, useSlots } from "@dative-gpi/foundation-shar
697
697
  import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui/composables";
698
698
  import { uuidv4 } from "@dative-gpi/bones-ui/tools/uuid"
699
699
 
700
- import { alphanumericSort, sizeToVar } from "../../utils";
700
+ import { alphanumericSort, containsSearchTerm, sizeToVar } from "../../utils";
701
701
 
702
702
  import FSDataIteratorItem from "./FSDataIteratorItem.vue";
703
703
  import FSSearchField from "../fields/FSSearchField.vue";
@@ -1024,14 +1024,13 @@ export default defineComponent({
1024
1024
  return acc.concat(filters.value[key].filter((filter) => filter.hidden).map((filter) => ({ key, filter })));
1025
1025
  }, [] as { key: string, filter: FSDataTableFilter }[]);
1026
1026
  if (props.items && props.items.length) {
1027
+ const innerSearchFormatted = innerSearch.value ? innerSearch.value.toLowerCase() : null;
1027
1028
  return props.items.filter((item) => {
1028
1029
  if (props.selectedOnly && !props.modelValue.includes(item[props.itemValue])) {
1029
1030
  return false;
1030
1031
  }
1031
- if (innerSearch.value) {
1032
- if (!JSON.stringify(item).toLowerCase().includes(innerSearch.value.toString().toLowerCase())) {
1033
- return false;
1034
- }
1032
+ if (innerSearchFormatted) {
1033
+ return containsSearchTerm(item, innerSearchFormatted);
1035
1034
  }
1036
1035
  if (activeFilters.some(af => af.filter.filter && af.filter.filter(af.filter.value, item[af.key], item))) {
1037
1036
  return false;
@@ -1,91 +1,147 @@
1
1
  <template>
2
- <component
3
- :is="$props.direction == 'row' ? FSRow : FSCol"
4
- v-bind="$attrs"
2
+ <FSCol
3
+ v-bind="$props"
4
+ gap="12px"
5
5
  >
6
- <FSTile
7
- v-for="item in items"
8
- :key="item.id"
9
- v-bind="tileProps(item)"
10
- :width="$props.direction == 'row' ? 'fit-content' : '100%'"
11
- height="fit-content"
6
+ <FSText
7
+ v-if="$props.label"
8
+ font="text-button"
12
9
  >
13
- <slot
14
- name="item"
15
- :item="item"
10
+ {{ $props.label }}
11
+ </FSText>
12
+ <FSSearchField
13
+ v-if="$props.searchable"
14
+ padding="0 12px 0 0"
15
+ :hideHeader="true"
16
+ :modelValue="actualSearch"
17
+ placeholder="Search"
18
+ @update:modelValue="onSearch"
19
+ />
20
+ <FSFadeOut
21
+ padding="0 4px 0 0"
22
+ :maxHeight="$props.maxHeight"
23
+ :maskHeight="0"
24
+ >
25
+ <component
26
+ :is="$props.direction == 'row' ? FSRow : FSCol"
16
27
  >
17
- <FSRow
18
- align="center-left"
28
+ <template
29
+ v-if="$props.loading"
19
30
  >
20
- <FSButtonDragIcon
21
- v-if="showDraggable"
31
+ <FSLoader
32
+ v-for="i in 4"
33
+ :key="i"
34
+ :width="$props.direction == 'row' ? '220px' : '100%'"
35
+ height="50px"
22
36
  />
23
- <slot
24
- name="itemContent"
25
- :item="item"
26
- >
27
- <!-- TODO : add draggable option -->
28
- <FSImage
29
- v-if="item.imageId"
30
- :imageId="item.imageId"
31
- width="24px"
32
- height="24px"
33
- />
34
- <FSIcon
35
- size="24px"
36
- v-else-if="item.icon"
37
- :icon="item.icon"
38
- />
39
- <FSSpan>{{ item[itemLabel] }}</FSSpan>
40
- </slot>
41
- <FSRow
42
- align="center-right"
37
+ </template>
38
+ <template
39
+ v-else
40
+ >
41
+ <FSTile
42
+ v-for="item in filteredItems"
43
+ :key="item.id"
44
+ v-bind="tileProps(item)"
45
+ :width="$props.direction == 'row' ? 'fit-content' : '100%'"
46
+ height="fit-content"
47
+ :editable="false"
48
+ :singleSelect="$props.clickable"
49
+ @update:modelValue="$emit('click:item', item.id)"
43
50
  >
44
- <FSButtonEditIcon
45
- v-if="showEdit"
46
- @click="$emit('click:edit', item.id)"
47
- />
48
- <FSButtonRemoveIcon
49
- v-if="showRemove"
50
- @click="$emit('click:remove', item.id)"
51
- />
52
- </FSRow>
53
- </FSRow>
54
- </slot>
55
- </FSTile>
56
- </component>
51
+ <slot
52
+ name="item"
53
+ :item="item"
54
+ >
55
+ <FSRow
56
+ align="center-left"
57
+ :wrap="false"
58
+ >
59
+ <FSButtonDragIcon
60
+ v-if="showDraggable"
61
+ />
62
+ <slot
63
+ name="itemContent"
64
+ :item="item"
65
+ >
66
+ <!-- TODO : add draggable option -->
67
+ <FSImage
68
+ v-if="item.imageId"
69
+ :imageId="item.imageId"
70
+ width="24px"
71
+ height="24px"
72
+ />
73
+ <FSIcon
74
+ size="24px"
75
+ v-else-if="item.icon"
76
+ :icon="item.icon"
77
+ />
78
+ <FSSpan>{{ item[itemLabel] }}</FSSpan>
79
+ </slot>
80
+ <FSRow
81
+ align="center-right"
82
+ >
83
+ <FSButtonEditIcon
84
+ v-if="showEdit"
85
+ @click="$emit('click:edit', item.id)"
86
+ />
87
+ <FSButtonRemoveIcon
88
+ v-if="showRemove"
89
+ @click="$emit('click:remove', item.id)"
90
+ />
91
+ </FSRow>
92
+ </FSRow>
93
+ </slot>
94
+ </FSTile>
95
+ </template>
96
+ </component>
97
+ </FSFadeOut>
98
+ </FSCol>
57
99
  </template>
58
100
 
59
101
 
60
102
  <script lang="ts">
61
- import { defineComponent, type PropType } from "vue";
103
+ import { computed } from "vue";
104
+ import { defineComponent, type PropType, ref, watch } from "vue";
62
105
 
63
106
  import { ColorEnum } from "../../models"
107
+ import { filterItems } from "../../utils";
64
108
 
65
109
  import FSRow from "../FSRow.vue";
66
110
  import FSCol from "../FSCol.vue";
67
- import FSImage from "../FSImage.vue";
111
+ import FSText from "../FSText.vue";
68
112
  import FSIcon from "../FSIcon.vue";
69
113
  import FSSpan from "../FSSpan.vue";
114
+ import FSImage from "../FSImage.vue";
115
+ import FSLoader from "../FSLoader.vue";
70
116
  import FSTile from "../tiles/FSTile.vue";
71
- import FSButtonRemoveIcon from "../buttons/FSButtonRemoveIcon.vue";
117
+ import FSFadeOut from "../FSFadeOut.vue";
118
+ import FSSearchField from "../fields/FSSearchField.vue";
72
119
  import FSButtonEditIcon from "../buttons/FSButtonEditIcon.vue";
73
120
  import FSButtonDragIcon from "../buttons/FSButtonDragIcon.vue";
121
+ import FSButtonRemoveIcon from "../buttons/FSButtonRemoveIcon.vue";
74
122
 
75
123
  export default defineComponent({
76
124
  name: "FSSimpleList",
77
125
  components: {
78
126
  FSRow,
79
127
  FSCol,
128
+ FSText,
80
129
  FSTile,
81
- FSImage,
82
130
  FSIcon,
83
131
  FSSpan,
84
- FSButtonRemoveIcon,
132
+ FSImage,
133
+ FSLoader,
134
+ FSFadeOut,
135
+ FSSearchField,
85
136
  FSButtonEditIcon,
86
137
  FSButtonDragIcon,
138
+ FSButtonRemoveIcon,
87
139
  },
88
140
  props: {
141
+ label: {
142
+ type: String,
143
+ required: false
144
+ },
89
145
  items: {
90
146
  type: Array as PropType<{id: string, label?: string, icon?: string, imageId?: string, [index: string]: any}[]>,
91
147
  required: true
@@ -110,6 +166,31 @@ export default defineComponent({
110
166
  required: false,
111
167
  default: false
112
168
  },
169
+ clickable: {
170
+ type: Boolean,
171
+ required: false,
172
+ default: false
173
+ },
174
+ searchable: {
175
+ type: Boolean,
176
+ required: false,
177
+ default: false
178
+ },
179
+ search: {
180
+ type: String,
181
+ required: false,
182
+ default: ""
183
+ },
184
+ noFilter: {
185
+ type: Boolean,
186
+ required: false,
187
+ default: false
188
+ },
189
+ maxHeight: {
190
+ type: [Array, String, Number] as PropType<string[] | number[] | string | number | null | undefined>,
191
+ required: false,
192
+ default: undefined
193
+ },
113
194
  direction: {
114
195
  type: String as PropType<"row" | "column">,
115
196
  required: false,
@@ -119,14 +200,42 @@ export default defineComponent({
119
200
  type: String,
120
201
  required: false,
121
202
  default: "label"
203
+ },
204
+ loading: {
205
+ type: Boolean,
206
+ required: false,
207
+ default: false
122
208
  }
123
209
  },
124
- emits: ["click:edit", "click:remove"],
125
- setup(){
210
+ emits: ["click:edit", "click:remove", "click:item", "update:search"],
211
+ setup(props, { emit }) {
212
+ const actualSearch = ref<string | null>(props.search);
213
+ const filteredItems = computed(() => {
214
+ if(!actualSearch.value) {
215
+ return props.items;
216
+ }
217
+ return filterItems(props.items, actualSearch.value);
218
+ });
219
+
220
+ const onSearch = (value: string) => {
221
+ if(props.noFilter) {
222
+ emit("update:search", value);
223
+ } else {
224
+ actualSearch.value = value;
225
+ }
226
+ }
227
+
228
+ watch(() => props.search, (value) => {
229
+ actualSearch.value = value;
230
+ });
231
+
126
232
  return {
233
+ actualSearch,
234
+ filteredItems,
127
235
  ColorEnum,
236
+ onSearch,
128
237
  FSRow,
129
- FSCol
238
+ FSCol,
130
239
  }
131
240
  }
132
241
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dative-gpi/foundation-shared-components",
3
3
  "sideEffects": false,
4
- "version": "1.0.54",
4
+ "version": "1.0.56",
5
5
  "description": "",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -10,8 +10,8 @@
10
10
  "author": "",
11
11
  "license": "ISC",
12
12
  "dependencies": {
13
- "@dative-gpi/foundation-shared-domain": "1.0.54",
14
- "@dative-gpi/foundation-shared-services": "1.0.54"
13
+ "@dative-gpi/foundation-shared-domain": "1.0.56",
14
+ "@dative-gpi/foundation-shared-services": "1.0.56"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "@dative-gpi/bones-ui": "^0.0.75",
@@ -35,5 +35,5 @@
35
35
  "sass": "1.71.1",
36
36
  "sass-loader": "13.3.2"
37
37
  },
38
- "gitHead": "332546e736d95d481dc97c06af935200d8344b2c"
38
+ "gitHead": "c77d95e9a7724fa4a39d94b0b2bd0fa9d4fbdefc"
39
39
  }
@@ -25,7 +25,9 @@
25
25
  &:focus-within {
26
26
  border-color: var(--fs-rich-text-field-active-border-color) !important;
27
27
  }
28
+ }
28
29
 
30
+ .fs-rich-text-field-readonly {
29
31
  @include web {
30
32
  padding: 10px 12px !important;
31
33
  }
@@ -33,7 +35,6 @@
33
35
  @include mobile {
34
36
  padding: 6px 16px !important;
35
37
  }
36
-
37
38
  }
38
39
 
39
40
  .fs-rich-text-field-undo {
@@ -0,0 +1,3 @@
1
+ .fs-search-field {
2
+ max-width: 500px;
3
+ }
@@ -51,6 +51,7 @@
51
51
  @import "fs_radio.scss";
52
52
  @import "fs_rich_text_field.scss";
53
53
  @import "fs_row.scss";
54
+ @import "fs_search_field.scss";
54
55
  @import "fs_select_field.scss";
55
56
  @import "fs_slide_group.scss";
56
57
  @import "fs_slider.scss";
@@ -0,0 +1,15 @@
1
+ export const containsSearchTerm = (obj: any, searchTerm: string): boolean => {
2
+ if (typeof obj !== 'object' || obj === null) {
3
+ return String(obj).toLowerCase().includes(searchTerm);
4
+ }
5
+ if (Array.isArray(obj)) {
6
+ return obj.some(element => containsSearchTerm(element, searchTerm));
7
+ }
8
+ return Object.values(obj).some(value => containsSearchTerm(value, searchTerm));
9
+ };
10
+
11
+ export const filterItems = <T>(items: T[], filter: string): T[] => {
12
+ const searchTerm = filter.toLowerCase();
13
+
14
+ return items.filter(item => containsSearchTerm(item, searchTerm));
15
+ };
package/utils/index.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./badge";
2
2
  export * from "./color";
3
3
  export * from "./css";
4
4
  export * from "./error";
5
+ export * from "./filter";
5
6
  export * from "./gradient";
6
7
  export * from "./icons";
7
8
  export * from "./leafletMarkers"