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

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.
@@ -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.55",
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.55",
14
+ "@dative-gpi/foundation-shared-services": "1.0.55"
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": "1525fd18d7f83f946b100866bc563d6409049586"
39
39
  }
@@ -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"