@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.
- package/components/fields/FSSearchField.vue +1 -0
- package/components/lists/FSDataTableUI.vue +4 -5
- package/components/lists/FSSimpleList.vue +166 -57
- package/package.json +4 -4
- package/styles/components/fs_search_field.scss +3 -0
- package/styles/components/index.scss +1 -0
- package/utils/filter.ts +15 -0
- package/utils/index.ts +1 -0
|
@@ -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 (
|
|
1032
|
-
|
|
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
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<FSCol
|
|
3
|
+
v-bind="$props"
|
|
4
|
+
gap="12px"
|
|
5
5
|
>
|
|
6
|
-
<
|
|
7
|
-
v-
|
|
8
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
<
|
|
18
|
-
|
|
28
|
+
<template
|
|
29
|
+
v-if="$props.loading"
|
|
19
30
|
>
|
|
20
|
-
<
|
|
21
|
-
v-
|
|
31
|
+
<FSLoader
|
|
32
|
+
v-for="i in 4"
|
|
33
|
+
:key="i"
|
|
34
|
+
:width="$props.direction == 'row' ? '220px' : '100%'"
|
|
35
|
+
height="50px"
|
|
22
36
|
/>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
14
|
-
"@dative-gpi/foundation-shared-services": "1.0.
|
|
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": "
|
|
38
|
+
"gitHead": "1525fd18d7f83f946b100866bc563d6409049586"
|
|
39
39
|
}
|
package/utils/filter.ts
ADDED
|
@@ -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
|
+
};
|