@ouestfrance/sipa-bms-ui 8.6.0 → 8.8.0
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/dist/components/form/BmsAutocomplete.vue.d.ts +2 -0
- package/dist/components/form/BmsInputBooleanCheckbox.vue.d.ts +1 -1
- package/dist/components/form/BmsInputCheckboxGroup.vue.d.ts +2 -2
- package/dist/components/form/BmsInputCode.vue.d.ts +2 -2
- package/dist/components/form/BmsInputNumber.vue.d.ts +2 -2
- package/dist/components/form/BmsInputRadio.vue.d.ts +2 -2
- package/dist/components/form/BmsInputText.vue.d.ts +24 -22
- package/dist/components/form/BmsMultiSelect.vue.d.ts +3 -1
- package/dist/components/form/BmsSearch.vue.d.ts +28 -24
- package/dist/components/form/BmsSelect.vue.d.ts +7 -17
- package/dist/components/form/RawAutocomplete.vue.d.ts +17 -21
- package/dist/components/form/RawInputText.vue.d.ts +9 -9
- package/dist/components/form/RawSelect.vue.d.ts +30 -0
- package/dist/components/navigation/UiTenantSwitcher.vue.d.ts +28 -24
- package/dist/components/table/BmsServerTable.vue.d.ts +18 -0
- package/dist/components/table/BmsTable.vue.d.ts +18 -1
- package/dist/components/table/BmsTableFilters.vue.d.ts +47 -25
- package/dist/composables/search.composable.d.ts +1 -0
- package/dist/mockServiceWorker.js +16 -12
- package/dist/plugins/field/FieldDatalist.vue.d.ts +2 -0
- package/dist/plugins/field/field-component.model.d.ts +2 -2
- package/dist/sipa-bms-ui.css +220 -168
- package/dist/sipa-bms-ui.es.js +729 -524
- package/dist/sipa-bms-ui.es.js.map +1 -1
- package/dist/sipa-bms-ui.umd.js +734 -529
- package/dist/sipa-bms-ui.umd.js.map +1 -1
- package/package.json +11 -11
- package/src/assets/scss/global-variables.scss +6 -0
- package/src/components/feedback/UiTooltip.vue +1 -1
- package/src/components/form/BmsAutocomplete.vue +3 -0
- package/src/components/form/BmsInputNumber.spec.ts +26 -0
- package/src/components/form/BmsInputNumber.stories.js +20 -3
- package/src/components/form/BmsInputNumber.vue +36 -4
- package/src/components/form/BmsInputRadio.vue +1 -1
- package/src/components/form/BmsInputText.spec.ts +25 -0
- package/src/components/form/BmsInputText.stories.js +28 -3
- package/src/components/form/BmsInputText.vue +73 -12
- package/src/components/form/BmsMultiSelect.vue +66 -28
- package/src/components/form/BmsSelect.vue +60 -57
- package/src/components/form/RawAutocomplete.spec.ts +0 -8
- package/src/components/form/RawAutocomplete.vue +42 -24
- package/src/components/form/RawInputText.vue +14 -21
- package/src/components/form/RawSelect.vue +111 -0
- package/src/components/layout/BmsOverlay.vue +2 -2
- package/src/components/layout/UiPopoverMenu.vue +1 -1
- package/src/components/navigation/BmsMenu.vue +1 -1
- package/src/components/table/BmsServerTable.vue +18 -3
- package/src/components/table/BmsTable.vue +15 -2
- package/src/components/table/BmsTableFilters.vue +19 -7
- package/src/composables/search.composable.spec.ts +75 -0
- package/src/composables/search.composable.ts +54 -11
- package/src/plugins/field/FieldComponent.vue +7 -5
- package/src/plugins/field/FieldDatalist.stories.js +0 -9
- package/src/plugins/field/FieldDatalist.vue +16 -13
- package/src/plugins/field/field-component.model.ts +2 -2
- package/src/plugins/notifications/NotificationWidget.vue +1 -1
- package/src/showroom/pages/autocomplete.vue +22 -1
- package/src/showroom/pages/server-table.vue +53 -22
- package/src/showroom/pages/table.vue +42 -3
- package/src/showroom/pages/zindex.vue +39 -0
- package/dist/plugins/field/FieldDatalist.spec.d.ts +0 -1
- package/src/plugins/field/FieldDatalist.spec.ts +0 -35
|
@@ -4,7 +4,10 @@ import BmsPagination from '@/components/table/BmsPagination.vue';
|
|
|
4
4
|
import UiBmsTable from '@/components/table/UiBmsTable.vue';
|
|
5
5
|
import UiFilterButton from '@/components/table/UiFilterButton.vue';
|
|
6
6
|
import { usePagination } from '@/composables/pagination.composable';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
reflectFiltersToPath,
|
|
9
|
+
useSearch,
|
|
10
|
+
} from '@/composables/search.composable';
|
|
8
11
|
import { useSort } from '@/composables/sort.composable';
|
|
9
12
|
import {
|
|
10
13
|
Filter,
|
|
@@ -151,12 +154,15 @@ const {
|
|
|
151
154
|
isSavedFilterActive,
|
|
152
155
|
resetFilters,
|
|
153
156
|
selectSavedFilter,
|
|
157
|
+
updateFiltersFromProps,
|
|
154
158
|
} = useSearch(props.persistent, props.filters, props.defaultFiltersOpened);
|
|
155
159
|
|
|
156
160
|
const emits = defineEmits<{
|
|
157
161
|
deleteSavedFilter: [value: SavedFilter];
|
|
158
162
|
saveFilter: [value: SavedFilter];
|
|
159
163
|
'update:selectMode': [selectMode: SelectMode];
|
|
164
|
+
filterInput: [{ filterKey: string; value: any; e: InputEvent }];
|
|
165
|
+
filterChange: [{ filterKey: string; value: any }];
|
|
160
166
|
}>();
|
|
161
167
|
|
|
162
168
|
const loading = ref<boolean>(false);
|
|
@@ -196,6 +202,14 @@ watch([currentPage, isMounting], () => {
|
|
|
196
202
|
fetchData();
|
|
197
203
|
}
|
|
198
204
|
});
|
|
205
|
+
watch(
|
|
206
|
+
() => props.filters,
|
|
207
|
+
() => {
|
|
208
|
+
updateFiltersFromProps(props.filters);
|
|
209
|
+
reflectFiltersToPath(filters.value);
|
|
210
|
+
},
|
|
211
|
+
{ deep: true },
|
|
212
|
+
);
|
|
199
213
|
|
|
200
214
|
watch(
|
|
201
215
|
[() => filters.value, () => sort.value, size, search],
|
|
@@ -324,8 +338,9 @@ const onSelectAll = () => emits('update:selectMode', SelectMode.ALL);
|
|
|
324
338
|
:canSaveFilters="canSaveFilters"
|
|
325
339
|
@saveFilter="onSaveFilter"
|
|
326
340
|
@resetFilters="resetFilters"
|
|
327
|
-
|
|
328
|
-
|
|
341
|
+
@filterInput="(filterEvent) => emits('filterInput', filterEvent)"
|
|
342
|
+
@filterChange="(filterEvent) => emits('filterChange', filterEvent)"
|
|
343
|
+
/>
|
|
329
344
|
</div>
|
|
330
345
|
</Transition>
|
|
331
346
|
</template>
|
|
@@ -85,11 +85,14 @@ const {
|
|
|
85
85
|
resetFilters,
|
|
86
86
|
selectSavedFilter,
|
|
87
87
|
resetAllFilters,
|
|
88
|
+
updateFiltersFromProps,
|
|
88
89
|
} = useSearch(props.persistent, props.filters, props.defaultFiltersOpened);
|
|
89
90
|
|
|
90
91
|
const emits = defineEmits<{
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
deleteSavedFilter: [value: SavedFilter];
|
|
93
|
+
saveFilter: [value: SavedFilter];
|
|
94
|
+
filterInput: [{ filterKey: string; value: any; e: InputEvent }];
|
|
95
|
+
filterChange: [{ filterKey: string; value: any }];
|
|
93
96
|
}>();
|
|
94
97
|
|
|
95
98
|
const selectedItems: Ref<unknown[]> = defineModel('selectedItems', {
|
|
@@ -116,6 +119,14 @@ const getFilteredItems = () => {
|
|
|
116
119
|
|
|
117
120
|
const isMounting = ref(true);
|
|
118
121
|
|
|
122
|
+
watch(
|
|
123
|
+
() => props.filters,
|
|
124
|
+
() => {
|
|
125
|
+
updateFiltersFromProps(props.filters);
|
|
126
|
+
},
|
|
127
|
+
{ deep: true },
|
|
128
|
+
);
|
|
129
|
+
|
|
119
130
|
watchEffect(
|
|
120
131
|
async () => {
|
|
121
132
|
isMounting.value =
|
|
@@ -237,6 +248,8 @@ const onSelectAll = () => {
|
|
|
237
248
|
:canSaveFilters="canSaveFilters"
|
|
238
249
|
@saveFilter="onSaveFilter"
|
|
239
250
|
@reset-filters="resetFilters"
|
|
251
|
+
@filterInput="(filterEvent) => emits('filterInput', filterEvent)"
|
|
252
|
+
@filterChange="(filterEvent) => emits('filterChange', filterEvent)"
|
|
240
253
|
/>
|
|
241
254
|
</div>
|
|
242
255
|
</Transition>
|
|
@@ -51,6 +51,14 @@
|
|
|
51
51
|
"
|
|
52
52
|
:valueTo="transformValueForComponent(filter?.valueTo, filter.type)"
|
|
53
53
|
:options="getFilterOptions(filter) as any"
|
|
54
|
+
@input="
|
|
55
|
+
(e: InputEvent) =>
|
|
56
|
+
$emits('filterInput', {
|
|
57
|
+
filterKey: filter.key,
|
|
58
|
+
value: (e.target as HTMLInputElement).value,
|
|
59
|
+
e,
|
|
60
|
+
})
|
|
61
|
+
"
|
|
54
62
|
/>
|
|
55
63
|
</span>
|
|
56
64
|
</div>
|
|
@@ -101,9 +109,11 @@ const props = withDefaults(
|
|
|
101
109
|
);
|
|
102
110
|
|
|
103
111
|
const $emits = defineEmits<{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
'update:modelValue': [filters: Filter[]];
|
|
113
|
+
saveFilter: [value: SavedFilter];
|
|
114
|
+
resetFilters: [];
|
|
115
|
+
filterInput: [{ filterKey: string; value: any; e: InputEvent }];
|
|
116
|
+
filterChange: [{ filterKey: string; value: any }];
|
|
107
117
|
}>();
|
|
108
118
|
|
|
109
119
|
const savedModalOpened: Ref<boolean> = ref<boolean>(false);
|
|
@@ -112,10 +122,11 @@ const nameInput: Ref<typeof BmsInputText | null> = ref(null);
|
|
|
112
122
|
|
|
113
123
|
const onFilter = (key: string, inputValue: string, valueKey: any): void => {
|
|
114
124
|
let filter = props.modelValue.find((f) => f.key === key);
|
|
125
|
+
let updatedValue;
|
|
115
126
|
if (filter) {
|
|
116
127
|
switch (filter.type) {
|
|
117
128
|
case 'boolean':
|
|
118
|
-
|
|
129
|
+
updatedValue =
|
|
119
130
|
inputValue === 'true' ? true : inputValue === 'false' ? false : null;
|
|
120
131
|
break;
|
|
121
132
|
case 'select':
|
|
@@ -123,13 +134,14 @@ const onFilter = (key: string, inputValue: string, valueKey: any): void => {
|
|
|
123
134
|
case 'betweenDate':
|
|
124
135
|
case 'betweenDateTime':
|
|
125
136
|
case 'betweenNumber':
|
|
126
|
-
|
|
127
|
-
inputValue === 'null' ? null : inputValue;
|
|
137
|
+
updatedValue = inputValue === 'null' ? null : inputValue;
|
|
128
138
|
break;
|
|
129
139
|
default:
|
|
130
|
-
|
|
140
|
+
updatedValue = inputValue;
|
|
131
141
|
break;
|
|
132
142
|
}
|
|
143
|
+
filter[valueKey as keyof Filter] = updatedValue;
|
|
144
|
+
$emits('filterChange', { filterKey: key, value: updatedValue });
|
|
133
145
|
}
|
|
134
146
|
$emits('update:modelValue', props.modelValue);
|
|
135
147
|
};
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from './search.composable';
|
|
7
7
|
import { defineComponent, nextTick } from 'vue';
|
|
8
8
|
import { mount } from '@vue/test-utils';
|
|
9
|
+
import { Filter } from '@/models';
|
|
9
10
|
|
|
10
11
|
vi.mock('vue-router', () => ({
|
|
11
12
|
useRoute: vi.fn(),
|
|
@@ -1292,5 +1293,79 @@ describe('search and filter composable', () => {
|
|
|
1292
1293
|
]);
|
|
1293
1294
|
});
|
|
1294
1295
|
});
|
|
1296
|
+
|
|
1297
|
+
describe('updateFiltersFromProps', () => {
|
|
1298
|
+
test('should update select options', () => {
|
|
1299
|
+
const wrapper = factory();
|
|
1300
|
+
const filter: Filter = {
|
|
1301
|
+
key: 'filter0',
|
|
1302
|
+
label: 'Zéro',
|
|
1303
|
+
type: 'select',
|
|
1304
|
+
selectOptions: [{ label: 'option1', value: '1' }],
|
|
1305
|
+
value: 1,
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1308
|
+
wrapper.vm.filters = [filter];
|
|
1309
|
+
wrapper.vm.updateFiltersFromProps([{ ...filter, selectOptions: [] }]);
|
|
1310
|
+
expect(wrapper.vm.filters).toStrictEqual([
|
|
1311
|
+
{ ...filter, selectOptions: [], value: null },
|
|
1312
|
+
]);
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
test('should not change filter other than options', () => {
|
|
1316
|
+
const wrapper = factory();
|
|
1317
|
+
const filter: Filter = {
|
|
1318
|
+
key: 'filter0',
|
|
1319
|
+
label: 'Zéro',
|
|
1320
|
+
type: 'select',
|
|
1321
|
+
selectOptions: [{ label: 'option1', value: '1' }],
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
wrapper.vm.filters = [filter];
|
|
1325
|
+
wrapper.vm.updateFiltersFromProps([
|
|
1326
|
+
{
|
|
1327
|
+
key: 'filter0',
|
|
1328
|
+
label: 'new label',
|
|
1329
|
+
type: 'input',
|
|
1330
|
+
selectOptions: [{ label: 'option1', value: '1' }],
|
|
1331
|
+
},
|
|
1332
|
+
]);
|
|
1333
|
+
expect(wrapper.vm.filters).toStrictEqual([filter]);
|
|
1334
|
+
});
|
|
1335
|
+
test('should keep value if is in new options', () => {
|
|
1336
|
+
const wrapper = factory();
|
|
1337
|
+
const filter: Filter = {
|
|
1338
|
+
key: 'filter0',
|
|
1339
|
+
label: 'Zéro',
|
|
1340
|
+
type: 'select',
|
|
1341
|
+
selectOptions: [
|
|
1342
|
+
{ label: 'option1', value: '1' },
|
|
1343
|
+
{ label: 'option2', value: '2' },
|
|
1344
|
+
],
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1347
|
+
wrapper.vm.filters = [filter];
|
|
1348
|
+
wrapper.vm.updateFiltersFromProps([
|
|
1349
|
+
{
|
|
1350
|
+
key: 'filter0',
|
|
1351
|
+
label: 'Zéro',
|
|
1352
|
+
type: 'select',
|
|
1353
|
+
selectOptions: [
|
|
1354
|
+
{ label: 'option1', value: '1' },
|
|
1355
|
+
{ label: 'option3', value: '3' },
|
|
1356
|
+
],
|
|
1357
|
+
},
|
|
1358
|
+
]);
|
|
1359
|
+
expect(wrapper.vm.filters).toStrictEqual([
|
|
1360
|
+
{
|
|
1361
|
+
...filter,
|
|
1362
|
+
selectOptions: [
|
|
1363
|
+
{ label: 'option1', value: '1' },
|
|
1364
|
+
{ label: 'option3', value: '3' },
|
|
1365
|
+
],
|
|
1366
|
+
},
|
|
1367
|
+
]);
|
|
1368
|
+
});
|
|
1369
|
+
});
|
|
1295
1370
|
});
|
|
1296
1371
|
});
|
|
@@ -183,6 +183,7 @@ export const useSearch = (
|
|
|
183
183
|
reflectSearchToUserPref(search.value);
|
|
184
184
|
}
|
|
185
185
|
});
|
|
186
|
+
|
|
186
187
|
watch(
|
|
187
188
|
() => filters,
|
|
188
189
|
() => {
|
|
@@ -233,19 +234,23 @@ export const useSearch = (
|
|
|
233
234
|
});
|
|
234
235
|
};
|
|
235
236
|
|
|
237
|
+
const resetFilterValue = (filter: Filter) => {
|
|
238
|
+
let resetFilter = { ...filter };
|
|
239
|
+
const valueKeys = ['value', 'valueFrom', 'valueTo'];
|
|
240
|
+
valueKeys.forEach((key) => {
|
|
241
|
+
if (
|
|
242
|
+
resetFilter[key as keyof Filter] !== null &&
|
|
243
|
+
resetFilter[key as keyof Filter] !== undefined
|
|
244
|
+
) {
|
|
245
|
+
resetFilter[key as keyof Filter] = null;
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
return resetFilter;
|
|
249
|
+
};
|
|
250
|
+
|
|
236
251
|
const resetFilters = () => {
|
|
237
252
|
filters.value = filters.value.map((filter) => {
|
|
238
|
-
|
|
239
|
-
const valueKeys = ['value', 'valueFrom', 'valueTo'];
|
|
240
|
-
valueKeys.forEach((key) => {
|
|
241
|
-
if (
|
|
242
|
-
resetFilter[key as keyof Filter] !== null &&
|
|
243
|
-
resetFilter[key as keyof Filter] !== undefined
|
|
244
|
-
) {
|
|
245
|
-
resetFilter[key as keyof Filter] = null;
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
return resetFilter;
|
|
253
|
+
return resetFilterValue(filter);
|
|
249
254
|
});
|
|
250
255
|
};
|
|
251
256
|
|
|
@@ -264,6 +269,43 @@ export const useSearch = (
|
|
|
264
269
|
}
|
|
265
270
|
};
|
|
266
271
|
|
|
272
|
+
const updateFiltersFromProps = (updatedFilters: Filter[]) => {
|
|
273
|
+
filters.value = filters.value.map((filter) => {
|
|
274
|
+
const updatedFilter = updatedFilters.find((f) => f.key === filter.key);
|
|
275
|
+
|
|
276
|
+
if (updatedFilter) {
|
|
277
|
+
let mergedFilter = { ...filter };
|
|
278
|
+
|
|
279
|
+
if (updatedFilter.autocompleteOptions)
|
|
280
|
+
mergedFilter.autocompleteOptions = updatedFilter.autocompleteOptions;
|
|
281
|
+
if (updatedFilter.selectOptions)
|
|
282
|
+
mergedFilter.selectOptions = updatedFilter.selectOptions;
|
|
283
|
+
|
|
284
|
+
const value = filter.value || filter.valueFrom || filter.valueTo;
|
|
285
|
+
|
|
286
|
+
const isValueValidSelectOption =
|
|
287
|
+
updatedFilter.type === 'select' &&
|
|
288
|
+
updatedFilter.selectOptions?.find((option) => option.value === value);
|
|
289
|
+
|
|
290
|
+
const isValueValidAutocompleteOption =
|
|
291
|
+
updatedFilter.type === 'autocomplete' &&
|
|
292
|
+
updatedFilter.autocompleteOptions?.find((option) => option === value);
|
|
293
|
+
|
|
294
|
+
if (
|
|
295
|
+
value &&
|
|
296
|
+
!isValueValidSelectOption &&
|
|
297
|
+
!isValueValidAutocompleteOption
|
|
298
|
+
) {
|
|
299
|
+
mergedFilter = resetFilterValue(mergedFilter);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return mergedFilter;
|
|
303
|
+
} else {
|
|
304
|
+
return filter;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
|
|
267
309
|
return {
|
|
268
310
|
search,
|
|
269
311
|
reflect,
|
|
@@ -277,6 +319,7 @@ export const useSearch = (
|
|
|
277
319
|
resetFilters,
|
|
278
320
|
selectSavedFilter,
|
|
279
321
|
resetAllFilters,
|
|
322
|
+
updateFiltersFromProps,
|
|
280
323
|
};
|
|
281
324
|
};
|
|
282
325
|
|
|
@@ -24,17 +24,17 @@
|
|
|
24
24
|
</span>
|
|
25
25
|
</div>
|
|
26
26
|
|
|
27
|
-
<div v-if="captions?.length" class="field__captions">
|
|
27
|
+
<div v-if="captions?.length || errors?.length" class="field__captions">
|
|
28
28
|
<div
|
|
29
|
+
v-if="captions?.length"
|
|
29
30
|
v-for="caption in captions"
|
|
30
31
|
:key="getCaptionIdentifier(caption)"
|
|
31
32
|
class="information field__caption"
|
|
32
33
|
>
|
|
33
34
|
<BmsCaption :caption="caption" />
|
|
34
35
|
</div>
|
|
35
|
-
</div>
|
|
36
|
-
<div v-if="errors?.length" class="field__errors">
|
|
37
36
|
<div
|
|
37
|
+
v-if="errors?.length"
|
|
38
38
|
v-for="error in computedErrors"
|
|
39
39
|
:key="error.label"
|
|
40
40
|
class="field__error"
|
|
@@ -186,7 +186,9 @@ const getCaptionIdentifier = (caption: string | Caption): string => {
|
|
|
186
186
|
--field-padding: 0.5em;
|
|
187
187
|
--field-margin: 0;
|
|
188
188
|
--field-label-font-weight: normal;
|
|
189
|
-
|
|
189
|
+
.field__captions {
|
|
190
|
+
margin-top: 0.5rem;
|
|
191
|
+
}
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
&.is-error {
|
|
@@ -204,7 +206,7 @@ const getCaptionIdentifier = (caption: string | Caption): string => {
|
|
|
204
206
|
margin: 0;
|
|
205
207
|
background: var(--bms-white);
|
|
206
208
|
width: 100%;
|
|
207
|
-
z-index:
|
|
209
|
+
z-index: var(--bms-z-index-tooltip);
|
|
208
210
|
max-height: 300px;
|
|
209
211
|
overflow-y: auto;
|
|
210
212
|
|
|
@@ -27,15 +27,6 @@ Default.args = {
|
|
|
27
27
|
})),
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
export const WithSelectedItem = Template.bind({});
|
|
31
|
-
WithSelectedItem.args = {
|
|
32
|
-
currentSelectedItemIndex: 1,
|
|
33
|
-
options: ['toto', 'titi', 'tutu', 'tirlitititututatoooo'].map((i) => ({
|
|
34
|
-
label: i,
|
|
35
|
-
value: i,
|
|
36
|
-
})),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
30
|
export const CanAddNewOption = Template.bind({});
|
|
40
31
|
CanAddNewOption.args = {
|
|
41
32
|
options: ['toto', 'titi', 'tutu', 'tirlitititututatoooo'].map((i) => ({
|
|
@@ -1,29 +1,21 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<ul
|
|
3
|
-
class="options-list"
|
|
4
|
-
data-testid="select-options"
|
|
5
|
-
@keydown.up="keyUp"
|
|
6
|
-
@keydown.down="keyDown"
|
|
7
|
-
@keydown.enter="keyEnter"
|
|
8
|
-
>
|
|
2
|
+
<ul class="options-list" data-testid="select-options">
|
|
9
3
|
<li
|
|
10
4
|
v-for="(option, index) in options"
|
|
11
5
|
:key="index"
|
|
12
6
|
:data-testid="option.value"
|
|
13
7
|
:class="{
|
|
8
|
+
'datalist-option': true,
|
|
14
9
|
selected: index === currentSelectedItemIndex,
|
|
15
10
|
small,
|
|
16
11
|
}"
|
|
17
|
-
@click.
|
|
12
|
+
@click.stop="onClick(option)"
|
|
18
13
|
>
|
|
19
14
|
<slot name="option" :option="option">
|
|
20
15
|
{{ option.label === null ? 'N/A' : option.label }}
|
|
21
16
|
</slot>
|
|
22
17
|
</li>
|
|
23
|
-
<li
|
|
24
|
-
v-if="displayNewOption"
|
|
25
|
-
@click.prevent="$emits('addNewOption', newOption)"
|
|
26
|
-
>
|
|
18
|
+
<li v-if="displayNewOption" @click.stop="$emits('addNewOption', newOption)">
|
|
27
19
|
<slot name="new-option"> {{ newOption }} (nouvelle option) </slot>
|
|
28
20
|
</li>
|
|
29
21
|
</ul>
|
|
@@ -31,7 +23,7 @@
|
|
|
31
23
|
|
|
32
24
|
<script setup lang="ts">
|
|
33
25
|
import { InputOption } from '@/models';
|
|
34
|
-
import { computed, onMounted, ref } from 'vue';
|
|
26
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
|
35
27
|
|
|
36
28
|
export interface Props {
|
|
37
29
|
options: InputOption[];
|
|
@@ -52,17 +44,24 @@ const currentSelectedItemIndex = ref<number>(-1);
|
|
|
52
44
|
const $emits = defineEmits<{
|
|
53
45
|
select: [option: any];
|
|
54
46
|
addNewOption: [option: string];
|
|
47
|
+
blur: [];
|
|
55
48
|
}>();
|
|
56
49
|
|
|
57
50
|
const keyDownEventListener = (ev: KeyboardEvent) => {
|
|
58
51
|
if (props.isInputFocused) {
|
|
59
52
|
switch (ev.key) {
|
|
60
53
|
case 'ArrowDown':
|
|
54
|
+
ev.preventDefault();
|
|
61
55
|
return keyDown();
|
|
62
56
|
case 'ArrowUp':
|
|
57
|
+
ev.preventDefault();
|
|
63
58
|
return keyUp();
|
|
64
59
|
case 'Enter':
|
|
60
|
+
ev.preventDefault();
|
|
65
61
|
return keyEnter();
|
|
62
|
+
case 'Escape':
|
|
63
|
+
ev.preventDefault();
|
|
64
|
+
return keyEscape();
|
|
66
65
|
default:
|
|
67
66
|
return;
|
|
68
67
|
}
|
|
@@ -96,6 +95,10 @@ const keyEnter = () => {
|
|
|
96
95
|
}
|
|
97
96
|
};
|
|
98
97
|
|
|
98
|
+
const keyEscape = () => {
|
|
99
|
+
$emits('blur');
|
|
100
|
+
};
|
|
101
|
+
|
|
99
102
|
const onClick = (option: { label: string; value: any }) => {
|
|
100
103
|
$emits('select', option);
|
|
101
104
|
};
|
|
@@ -5,8 +5,8 @@ export interface FieldComponentProps {
|
|
|
5
5
|
required?: boolean;
|
|
6
6
|
optional?: boolean;
|
|
7
7
|
helperText?: string;
|
|
8
|
-
errors?: string
|
|
9
|
-
captions?: string
|
|
8
|
+
errors?: (string | Caption)[];
|
|
9
|
+
captions?: (string | Caption)[];
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
small?: boolean;
|
|
12
12
|
}
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
:can-add-new-option="true"
|
|
12
12
|
@add-new-option="onAddNewOption"
|
|
13
13
|
/>
|
|
14
|
-
|
|
15
14
|
Valeur: {{ inputValueIcon }}
|
|
16
15
|
<BmsAutocomplete
|
|
17
16
|
label="Autocomplete avec icones"
|
|
@@ -26,6 +25,20 @@
|
|
|
26
25
|
:options="optionsText"
|
|
27
26
|
v-model="inputText"
|
|
28
27
|
/>
|
|
28
|
+
|
|
29
|
+
<BmsSelect
|
|
30
|
+
label="Select"
|
|
31
|
+
@blur="console.log('blur')"
|
|
32
|
+
v-model="select"
|
|
33
|
+
:options="optionsLabelValue"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<BmsMultiSelect
|
|
37
|
+
label="Multi-select"
|
|
38
|
+
v-model="multiSelect"
|
|
39
|
+
:options="optionsLabelValue"
|
|
40
|
+
/>
|
|
41
|
+
<br />
|
|
29
42
|
</template>
|
|
30
43
|
|
|
31
44
|
<script setup lang="ts">
|
|
@@ -34,6 +47,14 @@ import { Heart, Cat } from 'lucide-vue-next';
|
|
|
34
47
|
import { range } from 'lodash';
|
|
35
48
|
import { ref } from 'vue';
|
|
36
49
|
import BmsButton from '@/components/button/BmsButton.vue';
|
|
50
|
+
import BmsSelect from '@/components/form/BmsSelect.vue';
|
|
51
|
+
import BmsMultiSelect from '@/components/form/BmsMultiSelect.vue';
|
|
52
|
+
import FieldDatalist from '@/plugins/field/FieldDatalist.vue';
|
|
53
|
+
import RawAutocomplete from '@/components/form/RawAutocomplete.vue';
|
|
54
|
+
import BmsInputText from '@/components/form/BmsInputText.vue';
|
|
55
|
+
|
|
56
|
+
const select = ref('');
|
|
57
|
+
const multiSelect = ref([]);
|
|
37
58
|
|
|
38
59
|
const optionsLabelValue = ref(
|
|
39
60
|
range(0, 30).map((i) =>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
+
{{ filters }}
|
|
2
3
|
<bms-server-table
|
|
3
4
|
v-model:selectedItems="selectedItems"
|
|
4
5
|
v-model:selectMode="selectMode"
|
|
@@ -13,6 +14,8 @@
|
|
|
13
14
|
:total="totalItems"
|
|
14
15
|
@saveFilter="onSaveFilter"
|
|
15
16
|
@deleteSavedFilter="onDeleteSavedFilter"
|
|
17
|
+
@filterInput="onFilterInput"
|
|
18
|
+
@filterChange="onFilterChange"
|
|
16
19
|
>
|
|
17
20
|
<template #default="{ row }: { row: any }">
|
|
18
21
|
<td>
|
|
@@ -40,7 +43,7 @@
|
|
|
40
43
|
<script lang="ts" setup>
|
|
41
44
|
import { Filter, SavedFilter, SelectMode } from '@/models';
|
|
42
45
|
import axios from 'axios';
|
|
43
|
-
import { Ref, ref } from 'vue';
|
|
46
|
+
import { computed, Ref, ref } from 'vue';
|
|
44
47
|
import { isEmptyStringOrNotDefined } from '@/helpers';
|
|
45
48
|
|
|
46
49
|
const serverHeaders = [
|
|
@@ -49,7 +52,27 @@ const serverHeaders = [
|
|
|
49
52
|
{ label: 'Type', key: 'type' },
|
|
50
53
|
];
|
|
51
54
|
|
|
52
|
-
const
|
|
55
|
+
const typeOptions = ref([
|
|
56
|
+
'Grass',
|
|
57
|
+
'Poison',
|
|
58
|
+
'Fire',
|
|
59
|
+
'Flying',
|
|
60
|
+
'Water',
|
|
61
|
+
'Bug',
|
|
62
|
+
'Normal',
|
|
63
|
+
'Electric',
|
|
64
|
+
'Ground',
|
|
65
|
+
'Fairy',
|
|
66
|
+
'Fighting',
|
|
67
|
+
'Psychic',
|
|
68
|
+
'Rock',
|
|
69
|
+
'Steel',
|
|
70
|
+
'Ice',
|
|
71
|
+
'Ghost',
|
|
72
|
+
'Dragon',
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
const filters = computed(() => [
|
|
53
76
|
{
|
|
54
77
|
label: 'Name',
|
|
55
78
|
key: 'name',
|
|
@@ -65,27 +88,9 @@ const filters = [
|
|
|
65
88
|
label: 'Type',
|
|
66
89
|
key: 'type',
|
|
67
90
|
type: 'autocomplete',
|
|
68
|
-
autocompleteOptions:
|
|
69
|
-
'Grass',
|
|
70
|
-
'Poison',
|
|
71
|
-
'Fire',
|
|
72
|
-
'Flying',
|
|
73
|
-
'Water',
|
|
74
|
-
'Bug',
|
|
75
|
-
'Normal',
|
|
76
|
-
'Electric',
|
|
77
|
-
'Ground',
|
|
78
|
-
'Fairy',
|
|
79
|
-
'Fighting',
|
|
80
|
-
'Psychic',
|
|
81
|
-
'Rock',
|
|
82
|
-
'Steel',
|
|
83
|
-
'Ice',
|
|
84
|
-
'Ghost',
|
|
85
|
-
'Dragon',
|
|
86
|
-
],
|
|
91
|
+
autocompleteOptions: typeOptions.value,
|
|
87
92
|
},
|
|
88
|
-
];
|
|
93
|
+
]);
|
|
89
94
|
|
|
90
95
|
const savedFilters: Ref<SavedFilter[]> = ref<SavedFilter[]>([]);
|
|
91
96
|
const selectedItems = ref<any[]>([]);
|
|
@@ -102,6 +107,32 @@ const onDeleteSavedFilter = (savedFilter: SavedFilter) => {
|
|
|
102
107
|
);
|
|
103
108
|
};
|
|
104
109
|
|
|
110
|
+
const onFilterInput = ({
|
|
111
|
+
filterKey,
|
|
112
|
+
e,
|
|
113
|
+
}: {
|
|
114
|
+
filterKey: string;
|
|
115
|
+
e: InputEvent;
|
|
116
|
+
}) => {
|
|
117
|
+
typeOptions.value = [];
|
|
118
|
+
console.log(
|
|
119
|
+
`user filtered key ${filterKey} with val ${(e.target as HTMLInputElement)?.value}`,
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const onFilterChange = ({
|
|
124
|
+
filterKey,
|
|
125
|
+
e,
|
|
126
|
+
}: {
|
|
127
|
+
filterKey: string;
|
|
128
|
+
e: InputEvent;
|
|
129
|
+
}) => {
|
|
130
|
+
typeOptions.value = [];
|
|
131
|
+
console.log(
|
|
132
|
+
`user filtered key ${filterKey} changed with val ${(e.target as HTMLInputElement)?.value}`,
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
105
136
|
const customFetchData = async (
|
|
106
137
|
params: any,
|
|
107
138
|
abortController: AbortController,
|