@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.
- package/components/FSImage.vue +3 -2
- package/components/fields/FSRichTextField.vue +29 -1
- 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_rich_text_field.scss +2 -1
- 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
package/components/FSImage.vue
CHANGED
|
@@ -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="
|
|
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,
|
|
@@ -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.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.
|
|
14
|
-
"@dative-gpi/foundation-shared-services": "1.0.
|
|
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": "
|
|
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 {
|
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
|
+
};
|