selectic 3.0.21 → 3.1.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/selectic.common.js +545 -67
- package/dist/selectic.esm.js +546 -69
- package/doc/changeIcons.md +118 -0
- package/doc/changeText.md +1 -1
- package/doc/domProperties.md +57 -19
- package/doc/extendedProperties.md +83 -72
- package/doc/main.md +2 -0
- package/doc/params.md +177 -112
- package/doc/properties.md +42 -0
- package/package.json +4 -4
- package/src/ExtendedList.tsx +53 -6
- package/src/Filter.tsx +11 -9
- package/src/Icon.tsx +199 -0
- package/src/List.tsx +12 -6
- package/src/MainInput.tsx +15 -11
- package/src/Store.tsx +290 -123
- package/src/css/selectic.css +24 -0
- package/src/icons/caret-down.tsx +21 -0
- package/src/icons/caret-up.tsx +21 -0
- package/src/icons/check.tsx +23 -0
- package/src/icons/question.tsx +21 -0
- package/src/icons/search.tsx +21 -0
- package/src/icons/spinner.tsx +21 -0
- package/src/icons/strikeThrough.tsx +21 -0
- package/src/icons/times.tsx +21 -0
- package/src/index.tsx +78 -37
- package/test/Store/Store_computed.spec.js +84 -0
- package/test/Store/changeIcons.spec.js +154 -0
- package/test/Store/selectGroup.spec.js +389 -0
- package/test/Store/selectItem.spec.js +100 -46
- package/test/helper.js +38 -34
- package/types/ExtendedList.d.ts +7 -2
- package/types/Icon.d.ts +25 -0
- package/types/Store.d.ts +142 -5
- package/types/icons/caret-down.d.ts +6 -0
- package/types/icons/caret-up.d.ts +6 -0
- package/types/icons/check.d.ts +6 -0
- package/types/icons/question.d.ts +6 -0
- package/types/icons/search.d.ts +6 -0
- package/types/icons/spinner.d.ts +6 -0
- package/types/icons/strikeThrough.d.ts +6 -0
- package/types/icons/times.d.ts +6 -0
- package/types/index.d.ts +74 -1
package/src/Store.tsx
CHANGED
|
@@ -61,45 +61,45 @@ export type GetCallback = (_ids: OptionId[])
|
|
|
61
61
|
export type FormatCallback = (_option: OptionItem) => OptionItem;
|
|
62
62
|
|
|
63
63
|
export type SelectionOverflow =
|
|
64
|
-
|
|
64
|
+
/** Items are reduced in width and an ellipsis is displayed in their name. */
|
|
65
65
|
'collapsed'
|
|
66
66
|
/* The container extends in height in order to display all items. */
|
|
67
67
|
| 'multiline';
|
|
68
68
|
|
|
69
69
|
export type ListPosition =
|
|
70
|
-
|
|
70
|
+
/** Display the list at bottom */
|
|
71
71
|
'bottom'
|
|
72
|
-
|
|
72
|
+
/** Display the list at bottom */
|
|
73
73
|
| 'top'
|
|
74
|
-
|
|
74
|
+
/** Display the list at bottom but if there is not enough space, display it at top */
|
|
75
75
|
| 'auto';
|
|
76
76
|
|
|
77
77
|
export type HideFilter =
|
|
78
|
-
|
|
78
|
+
/** Display or hide the filter panel */
|
|
79
79
|
boolean
|
|
80
|
-
|
|
80
|
+
/** The handler to open the filter panel is hidden only if there is less
|
|
81
81
|
* than 10 options */
|
|
82
82
|
| 'auto'
|
|
83
|
-
|
|
83
|
+
/** The panel filter is always open */
|
|
84
84
|
| 'open';
|
|
85
85
|
|
|
86
86
|
export type SelectAllOption =
|
|
87
|
-
|
|
87
|
+
/** Display the "select all" only when data are all fetched or allowRevert */
|
|
88
88
|
'auto'
|
|
89
|
-
|
|
89
|
+
/** Always display the "select all" in mulitple mode. */
|
|
90
90
|
| 'visible';
|
|
91
91
|
|
|
92
92
|
export interface SelecticStoreStateParams {
|
|
93
|
-
|
|
93
|
+
/** Equivalent of <select>'s "multiple" attribute */
|
|
94
94
|
multiple?: boolean;
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
/** Equivalent of <input>'s "placeholder" attribute */
|
|
97
97
|
placeholder?: string;
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
/** Hide filter component when enabled */
|
|
100
100
|
hideFilter?: HideFilter;
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
/** Allow to reverse selection.
|
|
103
103
|
* If true, parent should support the selectionIsExcluded property.
|
|
104
104
|
* If false, the action is never available.
|
|
105
105
|
* If undefined, the action is available only when it is not needed to
|
|
@@ -107,26 +107,26 @@ export interface SelecticStoreStateParams {
|
|
|
107
107
|
*/
|
|
108
108
|
allowRevert?: boolean;
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
/** Force the availability of the "select all" even if all data is not fetched yet. */
|
|
111
111
|
forceSelectAll?: SelectAllOption;
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
/** Allow user to clear current selection */
|
|
114
114
|
allowClearSelection?: boolean;
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
/** Number of items to retrieve in fetch request (it is possible
|
|
117
117
|
* to fetch more items at once if several pages are requested) */
|
|
118
118
|
pageSize?: number;
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
/** Select the first available option */
|
|
121
121
|
autoSelect?: boolean;
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
/** Disable the select if only one option is given and must be selected. */
|
|
124
124
|
autoDisabled?: boolean;
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
/** Accept only values which are in options */
|
|
127
127
|
strictValue?: boolean;
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
/** Define how the component should behave when selected items are too
|
|
130
130
|
* large for the container.
|
|
131
131
|
* collapsed (default): Items are reduced in width and an ellipsis
|
|
132
132
|
* is displayed in their name.
|
|
@@ -135,69 +135,80 @@ export interface SelecticStoreStateParams {
|
|
|
135
135
|
*/
|
|
136
136
|
selectionOverflow?: SelectionOverflow;
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
/** Called when item is displayed in the list. */
|
|
139
139
|
formatOption?: FormatCallback;
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
/** Called when item is displayed in the selection area. */
|
|
142
142
|
formatSelection?: FormatCallback;
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
/** Described behavior when options from several sources are set (static, dynamic, slots)
|
|
145
145
|
* It describe what to do (sort or force)
|
|
146
146
|
* and the order (O → static options, D → dynamic options, E → slot elements)
|
|
147
147
|
* Example: "sort-ODE"
|
|
148
148
|
*/
|
|
149
149
|
optionBehavior?: string;
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
/** Indicate where the list should be deployed */
|
|
152
152
|
listPosition?: ListPosition;
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
/** If true, the component is open at start */
|
|
155
155
|
isOpen?: boolean;
|
|
156
|
+
|
|
157
|
+
/** Avoid selecting all items when clicking on group's header */
|
|
158
|
+
disableGroupSelection?: boolean;
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
export interface Props {
|
|
159
|
-
|
|
162
|
+
/** Selected value */
|
|
160
163
|
value?: SelectedValue | null;
|
|
161
164
|
|
|
162
|
-
|
|
165
|
+
/** If true, the value represents the ones we don't want to select */
|
|
163
166
|
selectionIsExcluded?: boolean;
|
|
164
167
|
|
|
165
|
-
|
|
168
|
+
/** Equivalent of "disabled" Select's attribute */
|
|
166
169
|
disabled?: boolean;
|
|
167
170
|
|
|
168
|
-
|
|
171
|
+
/** List of options to display */
|
|
169
172
|
options?: OptionProp[] | null;
|
|
170
173
|
|
|
171
|
-
|
|
174
|
+
/** List of options to display from child elements */
|
|
172
175
|
childOptions?: OptionValue[];
|
|
173
176
|
|
|
174
|
-
|
|
177
|
+
/** Define groups which will be used by items */
|
|
175
178
|
groups?: GroupValue[];
|
|
176
179
|
|
|
177
|
-
|
|
180
|
+
/** Overwrite default texts */
|
|
178
181
|
texts?: PartialMessages | null;
|
|
179
182
|
|
|
180
|
-
|
|
183
|
+
/** Overwrite default icons */
|
|
184
|
+
icons?: PartialIcons | null;
|
|
185
|
+
|
|
186
|
+
/** Overwrite default icon family */
|
|
187
|
+
iconFamily?: IconFamily | null;
|
|
188
|
+
|
|
189
|
+
/** Keep this component open if another Selectic component opens */
|
|
181
190
|
keepOpenWithOtherSelectic?: boolean;
|
|
182
191
|
|
|
183
|
-
|
|
192
|
+
/** Selectic configuration */
|
|
184
193
|
params?: SelecticStoreStateParams;
|
|
185
194
|
|
|
186
|
-
|
|
195
|
+
/** Method to call to fetch extra data */
|
|
187
196
|
fetchCallback?: FetchCallback | null;
|
|
188
197
|
|
|
189
|
-
|
|
198
|
+
/** Method to call to get specific item */
|
|
190
199
|
getItemsCallback?: GetCallback | null;
|
|
191
200
|
}
|
|
192
201
|
|
|
193
202
|
type InternalProps = MandateProps<Props>;
|
|
194
203
|
|
|
195
204
|
export interface Data {
|
|
196
|
-
|
|
205
|
+
/** Number of items displayed in a page (before scrolling) */
|
|
197
206
|
itemsPerPage: number;
|
|
198
207
|
|
|
199
208
|
labels: Messages;
|
|
200
|
-
|
|
209
|
+
icons: PartialIcons;
|
|
210
|
+
iconFamily: IconFamily;
|
|
211
|
+
/** used to avoid checking and updating table while doing batch stuff */
|
|
201
212
|
doNotUpdate: boolean;
|
|
202
213
|
cacheItem: Map<OptionId, OptionValue>;
|
|
203
214
|
activeOrder: OptionBehaviorOrder;
|
|
@@ -205,28 +216,28 @@ export interface Data {
|
|
|
205
216
|
}
|
|
206
217
|
|
|
207
218
|
export interface SelecticStoreState {
|
|
208
|
-
|
|
219
|
+
/** The current selected values */
|
|
209
220
|
internalValue: SelectedValue;
|
|
210
221
|
|
|
211
|
-
|
|
222
|
+
/** If true, user wants to choose the opposite selection */
|
|
212
223
|
selectionIsExcluded: boolean;
|
|
213
224
|
|
|
214
|
-
|
|
225
|
+
/** If true, several value can be selected */
|
|
215
226
|
multiple: boolean;
|
|
216
227
|
|
|
217
|
-
|
|
228
|
+
/** If true, no change can be done by user */
|
|
218
229
|
disabled: boolean;
|
|
219
230
|
|
|
220
|
-
|
|
231
|
+
/** Define the default text to display when there is no selection */
|
|
221
232
|
placeholder: string;
|
|
222
233
|
|
|
223
|
-
|
|
234
|
+
/** If true, filters and controls are hidden */
|
|
224
235
|
hideFilter: boolean;
|
|
225
236
|
|
|
226
|
-
|
|
237
|
+
/** If true, the filter panel is always open */
|
|
227
238
|
keepFilterOpen: boolean;
|
|
228
239
|
|
|
229
|
-
|
|
240
|
+
/** Allow to reverse selection.
|
|
230
241
|
* If true, parent should support the selectionIsExcluded property.
|
|
231
242
|
* If false, the action is never available.
|
|
232
243
|
* If undefined, the action is available only when it is not needed to
|
|
@@ -234,101 +245,134 @@ export interface SelecticStoreState {
|
|
|
234
245
|
*/
|
|
235
246
|
allowRevert?: boolean;
|
|
236
247
|
|
|
237
|
-
|
|
248
|
+
/** If true, user can clear current selection
|
|
238
249
|
* (if false, it is still possible to clear it programmatically) */
|
|
239
250
|
allowClearSelection: boolean;
|
|
240
251
|
|
|
241
|
-
|
|
252
|
+
/** If false, do not select the first available option even if value is mandatory */
|
|
242
253
|
autoSelect: boolean;
|
|
243
254
|
|
|
244
|
-
|
|
255
|
+
/** If true, Selectic is disabled if there is only one mandatory option. */
|
|
245
256
|
autoDisabled: boolean;
|
|
246
257
|
|
|
247
|
-
|
|
258
|
+
/** If true, only values which are in options are accepted. */
|
|
248
259
|
strictValue: boolean;
|
|
249
260
|
|
|
250
|
-
|
|
261
|
+
/** Define how to behave when selected items are too large for container. */
|
|
251
262
|
selectionOverflow: SelectionOverflow;
|
|
252
263
|
|
|
253
|
-
|
|
264
|
+
/** If true, the list is displayed */
|
|
254
265
|
isOpen: boolean;
|
|
255
266
|
|
|
256
|
-
|
|
267
|
+
/** Text entered by user to look for options */
|
|
257
268
|
searchText: string;
|
|
258
269
|
|
|
259
|
-
|
|
270
|
+
/** Contains all known options */
|
|
260
271
|
allOptions: OptionValue[];
|
|
261
272
|
|
|
262
|
-
|
|
273
|
+
/** Contains all fetched dynamic options */
|
|
263
274
|
dynOptions: OptionValue[];
|
|
264
275
|
|
|
265
|
-
|
|
276
|
+
/** Contains options which should be displayed */
|
|
266
277
|
filteredOptions: OptionItem[];
|
|
267
278
|
|
|
268
|
-
|
|
279
|
+
/** Contains options which are selected */
|
|
269
280
|
selectedOptions: OptionItem | OptionItem[] | null;
|
|
270
281
|
|
|
271
|
-
|
|
282
|
+
/** The total number of all options (static + dynamic + elements) without any filter */
|
|
272
283
|
totalAllOptions: number;
|
|
273
284
|
|
|
274
|
-
|
|
285
|
+
/** The total number of options which can be fetched (without any filter) */
|
|
275
286
|
totalDynOptions: number;
|
|
276
287
|
|
|
277
|
-
|
|
288
|
+
/** The total number of options which should be displayed (filter is applied) */
|
|
278
289
|
totalFilteredOptions: number;
|
|
279
290
|
|
|
280
|
-
|
|
291
|
+
/** Description of groups (optGroup) */
|
|
281
292
|
groups: Map<OptionId, string>;
|
|
282
293
|
|
|
283
|
-
|
|
294
|
+
/** Starting index of options which are displayed */
|
|
284
295
|
offsetItem: number;
|
|
285
296
|
|
|
286
|
-
|
|
297
|
+
/** Index of active item */
|
|
287
298
|
activeItemIdx: number;
|
|
288
299
|
|
|
289
|
-
|
|
300
|
+
/** Number of items to fetch per page */
|
|
290
301
|
pageSize: number;
|
|
291
302
|
|
|
292
|
-
|
|
303
|
+
/** Called when item is displayed in the list. */
|
|
293
304
|
formatOption?: FormatCallback;
|
|
294
305
|
|
|
295
|
-
|
|
306
|
+
/** Called when item is displayed in the selection area. */
|
|
296
307
|
formatSelection?: FormatCallback;
|
|
297
308
|
|
|
298
|
-
|
|
309
|
+
/** Operation to apply when there are several sources */
|
|
299
310
|
optionBehaviorOperation: OptionBehaviorOperation;
|
|
300
311
|
|
|
301
|
-
|
|
312
|
+
/** Order of sources options */
|
|
302
313
|
optionBehaviorOrder: OptionBehaviorOrder[];
|
|
303
314
|
|
|
304
|
-
|
|
315
|
+
/** Indicate where the list should be deployed */
|
|
305
316
|
listPosition: ListPosition;
|
|
306
317
|
|
|
307
|
-
|
|
318
|
+
/** If true, the "select All" is still available even if all data are not fetched yet. */
|
|
308
319
|
forceSelectAll: SelectAllOption;
|
|
309
320
|
|
|
310
|
-
|
|
321
|
+
/** Avoid selecting all items when clicking on group's header */
|
|
322
|
+
disableGroupSelection: boolean;
|
|
323
|
+
|
|
324
|
+
/** Inner status which should be modified only by store */
|
|
311
325
|
status: {
|
|
312
|
-
|
|
326
|
+
/** If true, a search is currently done */
|
|
313
327
|
searching: boolean;
|
|
314
328
|
|
|
315
|
-
|
|
329
|
+
/** If not empty, an error happens */
|
|
316
330
|
errorMessage: string;
|
|
317
331
|
|
|
318
|
-
|
|
332
|
+
/** If true it means that all options are selected */
|
|
319
333
|
areAllSelected: boolean;
|
|
320
334
|
|
|
321
|
-
|
|
335
|
+
/** If true, a change has been done by user */
|
|
322
336
|
hasChanged: boolean;
|
|
323
337
|
|
|
324
|
-
|
|
338
|
+
/** If true, it means the current change has been done automatically by Selectic */
|
|
325
339
|
automaticChange: boolean;
|
|
326
340
|
|
|
327
|
-
|
|
341
|
+
/** If true, it means the current close has been done automatically by Selectic */
|
|
328
342
|
automaticClose: boolean;
|
|
329
343
|
};
|
|
330
344
|
}
|
|
331
345
|
|
|
346
|
+
export type IconFamily = ''
|
|
347
|
+
| 'selectic'
|
|
348
|
+
| 'font-awesome-4'
|
|
349
|
+
| 'font-awesome-5'
|
|
350
|
+
| 'font-awesome-6'
|
|
351
|
+
| 'raw'
|
|
352
|
+
| `prefix:${string}`
|
|
353
|
+
;
|
|
354
|
+
|
|
355
|
+
export type IconKey =
|
|
356
|
+
| 'caret-down'
|
|
357
|
+
| 'caret-up'
|
|
358
|
+
| 'check'
|
|
359
|
+
| 'search'
|
|
360
|
+
| 'spinner'
|
|
361
|
+
| 'strikethrough'
|
|
362
|
+
| 'times'
|
|
363
|
+
| 'question'
|
|
364
|
+
| 'spin'
|
|
365
|
+
;
|
|
366
|
+
|
|
367
|
+
export type IconValue =
|
|
368
|
+
| `selectic:${IconKey}${'' | ':spin'}`
|
|
369
|
+
| `raw:${string}`
|
|
370
|
+
| `current:${IconKey}${'' | ':spin'}`
|
|
371
|
+
| string
|
|
372
|
+
;
|
|
373
|
+
export type Icons = Record<IconKey, IconValue>;
|
|
374
|
+
export type PartialIcons = { [K in IconKey]?: Icons[K] };
|
|
375
|
+
|
|
332
376
|
interface Messages {
|
|
333
377
|
noFetchMethod: string;
|
|
334
378
|
searchPlaceholder: string;
|
|
@@ -358,6 +402,14 @@ export function changeTexts(texts: PartialMessages) {
|
|
|
358
402
|
messages = Object.assign(messages, texts);
|
|
359
403
|
}
|
|
360
404
|
|
|
405
|
+
export function changeIcons(newIcons: PartialIcons, newFamilyIcon?: IconFamily) {
|
|
406
|
+
icons = Object.assign(icons, newIcons);
|
|
407
|
+
|
|
408
|
+
if (newFamilyIcon) {
|
|
409
|
+
defaultFamilyIcon = newFamilyIcon;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
361
413
|
/* }}} */
|
|
362
414
|
|
|
363
415
|
let messages: Messages = {
|
|
@@ -380,6 +432,10 @@ let messages: Messages = {
|
|
|
380
432
|
wrongQueryResult: 'Query did not return all results.',
|
|
381
433
|
};
|
|
382
434
|
|
|
435
|
+
let defaultFamilyIcon: IconFamily = 'selectic';
|
|
436
|
+
let icons: PartialIcons = {
|
|
437
|
+
};
|
|
438
|
+
|
|
383
439
|
let closePreviousSelectic: undefined | voidCaller;
|
|
384
440
|
|
|
385
441
|
/* }}} */
|
|
@@ -402,9 +458,12 @@ export default class SelecticStore {
|
|
|
402
458
|
/* }}} */
|
|
403
459
|
/* {{{ computed */
|
|
404
460
|
|
|
405
|
-
|
|
461
|
+
/** Number of item to pre-display */
|
|
406
462
|
public marginSize: ComputedRef<number>;
|
|
407
463
|
|
|
464
|
+
/** If true, it is possible to click on group to select all items inside */
|
|
465
|
+
public allowGroupSelection: ComputedRef<boolean>;
|
|
466
|
+
|
|
408
467
|
public isPartial: ComputedRef<boolean>;
|
|
409
468
|
public hasAllItems: ComputedRef<boolean>;
|
|
410
469
|
public hasFetchedAllItems: ComputedRef<boolean>;
|
|
@@ -428,6 +487,8 @@ export default class SelecticStore {
|
|
|
428
487
|
childOptions: [],
|
|
429
488
|
groups: [],
|
|
430
489
|
texts: null,
|
|
490
|
+
icons: null,
|
|
491
|
+
iconFamily: null,
|
|
431
492
|
params: {},
|
|
432
493
|
fetchCallback: null,
|
|
433
494
|
getItemsCallback: null,
|
|
@@ -440,51 +501,52 @@ export default class SelecticStore {
|
|
|
440
501
|
/* {{{ data */
|
|
441
502
|
|
|
442
503
|
this.state = reactive<SelecticStoreState>({
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
placeholder: '',
|
|
446
|
-
hideFilter: false,
|
|
447
|
-
keepFilterOpen: false,
|
|
448
|
-
allowRevert: undefined,
|
|
504
|
+
activeItemIdx: -1,
|
|
505
|
+
allOptions: [],
|
|
449
506
|
allowClearSelection: false,
|
|
450
|
-
|
|
507
|
+
allowRevert: undefined,
|
|
451
508
|
autoDisabled: true,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
509
|
+
autoSelect: true,
|
|
510
|
+
disabled: false,
|
|
511
|
+
disableGroupSelection: false,
|
|
512
|
+
dynOptions: [],
|
|
513
|
+
filteredOptions: [],
|
|
514
|
+
forceSelectAll: 'auto',
|
|
515
|
+
groups: new Map(),
|
|
516
|
+
hideFilter: false,
|
|
455
517
|
internalValue: null,
|
|
456
518
|
isOpen: false,
|
|
519
|
+
keepFilterOpen: false,
|
|
520
|
+
listPosition: 'auto',
|
|
521
|
+
multiple: false,
|
|
522
|
+
offsetItem: 0,
|
|
523
|
+
optionBehaviorOperation: 'sort',
|
|
524
|
+
optionBehaviorOrder: ['O', 'D', 'E'],
|
|
525
|
+
pageSize: 100,
|
|
526
|
+
placeholder: '',
|
|
457
527
|
searchText: '',
|
|
458
|
-
selectionIsExcluded: false,
|
|
459
|
-
forceSelectAll: 'auto',
|
|
460
|
-
allOptions: [],
|
|
461
|
-
dynOptions: [],
|
|
462
|
-
filteredOptions: [],
|
|
463
528
|
selectedOptions: null,
|
|
529
|
+
selectionIsExcluded: false,
|
|
530
|
+
selectionOverflow: 'collapsed',
|
|
531
|
+
strictValue: false,
|
|
464
532
|
totalAllOptions: Infinity,
|
|
465
533
|
totalDynOptions: Infinity,
|
|
466
534
|
totalFilteredOptions: Infinity,
|
|
467
|
-
groups: new Map(),
|
|
468
|
-
offsetItem: 0,
|
|
469
|
-
activeItemIdx: -1,
|
|
470
|
-
pageSize: 100,
|
|
471
|
-
listPosition: 'auto',
|
|
472
|
-
|
|
473
|
-
optionBehaviorOperation: 'sort',
|
|
474
|
-
optionBehaviorOrder: ['O', 'D', 'E'],
|
|
475
535
|
|
|
476
536
|
status: {
|
|
477
|
-
searching: false,
|
|
478
|
-
errorMessage: '',
|
|
479
537
|
areAllSelected: false,
|
|
480
|
-
hasChanged: false,
|
|
481
538
|
automaticChange: false,
|
|
482
539
|
automaticClose: false,
|
|
540
|
+
errorMessage: '',
|
|
541
|
+
hasChanged: false,
|
|
542
|
+
searching: false,
|
|
483
543
|
},
|
|
484
544
|
});
|
|
485
545
|
|
|
486
546
|
this.data = reactive({
|
|
487
547
|
labels: Object.assign({}, messages),
|
|
548
|
+
icons: Object.assign({}, icons),
|
|
549
|
+
iconFamily: defaultFamilyIcon,
|
|
488
550
|
itemsPerPage: 10,
|
|
489
551
|
doNotUpdate: false,
|
|
490
552
|
cacheItem: new Map(),
|
|
@@ -539,6 +601,10 @@ export default class SelecticStore {
|
|
|
539
601
|
return this.getElementOptions();
|
|
540
602
|
});
|
|
541
603
|
|
|
604
|
+
this.allowGroupSelection = computed(() => {
|
|
605
|
+
return this.state.multiple && !this.isPartial.value && !this.state.disableGroupSelection;
|
|
606
|
+
});
|
|
607
|
+
|
|
542
608
|
/* }}} */
|
|
543
609
|
/* {{{ watch */
|
|
544
610
|
|
|
@@ -585,6 +651,11 @@ export default class SelecticStore {
|
|
|
585
651
|
|
|
586
652
|
watch(() => this.state.internalValue, () => {
|
|
587
653
|
this.buildSelectedOptions();
|
|
654
|
+
/* If there is only one item, and the previous selected value was
|
|
655
|
+
* different, then if we change it to the only available item we
|
|
656
|
+
* should disable Selectic (user has no more choice).
|
|
657
|
+
* This is why it is needed to check autoDisabled here. */
|
|
658
|
+
this.checkAutoDisabled();
|
|
588
659
|
}, { deep: true });
|
|
589
660
|
|
|
590
661
|
watch(() => this.state.allOptions, () => {
|
|
@@ -641,6 +712,9 @@ export default class SelecticStore {
|
|
|
641
712
|
if (this.props.texts) {
|
|
642
713
|
this.changeTexts(this.props.texts);
|
|
643
714
|
}
|
|
715
|
+
if (this.props.icons || this.props.iconFamily) {
|
|
716
|
+
this.changeIcons(this.props.icons, this.props.iconFamily);
|
|
717
|
+
}
|
|
644
718
|
|
|
645
719
|
this.addGroups(this.props.groups);
|
|
646
720
|
this.assertValueType();
|
|
@@ -779,19 +853,47 @@ export default class SelecticStore {
|
|
|
779
853
|
return this.buildSelectedItems(ids);
|
|
780
854
|
}
|
|
781
855
|
|
|
782
|
-
public
|
|
856
|
+
public selectGroup(id: OptionId, itemsSelected: boolean) {
|
|
857
|
+
const state = this.state;
|
|
858
|
+
|
|
859
|
+
if (!unref(this.allowGroupSelection)) {
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const selectItem = this.selectItem.bind(this);
|
|
864
|
+
let hasChanged = false;
|
|
865
|
+
this.data.doNotUpdate = true;
|
|
866
|
+
const items = state.filteredOptions.filter((item) => {
|
|
867
|
+
const isInGroup = item.group === id && !item.exclusive && !item.disabled;
|
|
868
|
+
|
|
869
|
+
if (isInGroup) {
|
|
870
|
+
hasChanged = selectItem(item.id, itemsSelected, true) || hasChanged;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
return isInGroup;
|
|
874
|
+
});
|
|
875
|
+
this.data.doNotUpdate = false;
|
|
876
|
+
|
|
877
|
+
if (hasChanged && items.length) {
|
|
878
|
+
this.updateFilteredOptions();
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
public selectItem(id: OptionId, selected?: boolean, keepOpen = false): boolean {
|
|
783
885
|
const state = this.state;
|
|
784
886
|
let hasChanged = false;
|
|
785
887
|
const item = state.allOptions.find((opt) => opt.id === id);
|
|
786
888
|
|
|
787
889
|
/* Check that item is not disabled */
|
|
788
890
|
if (item?.disabled) {
|
|
789
|
-
return;
|
|
891
|
+
return hasChanged;
|
|
790
892
|
}
|
|
791
893
|
|
|
792
894
|
if (state.strictValue && !this.hasValue(id)) {
|
|
793
895
|
/* reject invalid values */
|
|
794
|
-
return;
|
|
896
|
+
return hasChanged;
|
|
795
897
|
}
|
|
796
898
|
|
|
797
899
|
if (state.multiple) {
|
|
@@ -844,12 +946,12 @@ export default class SelecticStore {
|
|
|
844
946
|
|
|
845
947
|
if (!selected) {
|
|
846
948
|
if (id !== oldValue) {
|
|
847
|
-
return;
|
|
949
|
+
return hasChanged;
|
|
848
950
|
}
|
|
849
951
|
id = null;
|
|
850
952
|
} else
|
|
851
953
|
if (id === oldValue) {
|
|
852
|
-
return;
|
|
954
|
+
return hasChanged;
|
|
853
955
|
}
|
|
854
956
|
|
|
855
957
|
if (keepOpen) {
|
|
@@ -863,6 +965,8 @@ export default class SelecticStore {
|
|
|
863
965
|
if (hasChanged) {
|
|
864
966
|
state.status.hasChanged = true;
|
|
865
967
|
}
|
|
968
|
+
|
|
969
|
+
return hasChanged;
|
|
866
970
|
}
|
|
867
971
|
|
|
868
972
|
public toggleSelectAll() {
|
|
@@ -950,6 +1054,16 @@ export default class SelecticStore {
|
|
|
950
1054
|
this.data.labels = Object.assign({}, this.data.labels, texts);
|
|
951
1055
|
}
|
|
952
1056
|
|
|
1057
|
+
public changeIcons(icons: PartialIcons | null, family?: IconFamily | null) {
|
|
1058
|
+
if (icons) {
|
|
1059
|
+
this.data.icons = Object.assign({}, this.data.icons, icons);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
if (typeof family === 'string') {
|
|
1063
|
+
this.data.iconFamily = family;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
953
1067
|
/* }}} */
|
|
954
1068
|
/* {{{ private methods */
|
|
955
1069
|
|
|
@@ -1073,6 +1187,7 @@ export default class SelecticStore {
|
|
|
1073
1187
|
if (!this.data.doNotUpdate) {
|
|
1074
1188
|
this.state.filteredOptions = this.buildItems(this.state.filteredOptions);
|
|
1075
1189
|
this.buildSelectedOptions();
|
|
1190
|
+
this.updateGroupSelection();
|
|
1076
1191
|
}
|
|
1077
1192
|
}
|
|
1078
1193
|
|
|
@@ -1082,7 +1197,7 @@ export default class SelecticStore {
|
|
|
1082
1197
|
});
|
|
1083
1198
|
}
|
|
1084
1199
|
|
|
1085
|
-
|
|
1200
|
+
/** This method is for the computed property listOptions */
|
|
1086
1201
|
private getListOptions(): OptionValue[] {
|
|
1087
1202
|
const options = deepClone(this.props.options, ['data']);
|
|
1088
1203
|
const listOptions: OptionValue[] = [];
|
|
@@ -1256,13 +1371,11 @@ export default class SelecticStore {
|
|
|
1256
1371
|
/* Do not fetch again just build filteredOptions */
|
|
1257
1372
|
const search = this.state.searchText;
|
|
1258
1373
|
if (!search) {
|
|
1259
|
-
this.
|
|
1260
|
-
this.state.totalFilteredOptions = this.state.filteredOptions.length;
|
|
1374
|
+
this.setFilteredOptions(this.buildGroupItems(allOptions));
|
|
1261
1375
|
return;
|
|
1262
1376
|
}
|
|
1263
1377
|
const options = this.filterOptions(allOptions, search);
|
|
1264
|
-
this.
|
|
1265
|
-
this.state.totalFilteredOptions = this.state.filteredOptions.length;
|
|
1378
|
+
this.setFilteredOptions(options);
|
|
1266
1379
|
}
|
|
1267
1380
|
}
|
|
1268
1381
|
|
|
@@ -1288,14 +1401,12 @@ export default class SelecticStore {
|
|
|
1288
1401
|
/* Check if all options have been fetched */
|
|
1289
1402
|
if (hasFetchedAllItems) {
|
|
1290
1403
|
if (!search) {
|
|
1291
|
-
this.
|
|
1292
|
-
this.state.totalFilteredOptions = this.state.filteredOptions.length;
|
|
1404
|
+
this.setFilteredOptions(this.buildGroupItems(allOptions));
|
|
1293
1405
|
return;
|
|
1294
1406
|
}
|
|
1295
1407
|
|
|
1296
1408
|
const options = this.filterOptions(allOptions, search);
|
|
1297
|
-
this.
|
|
1298
|
-
this.state.totalFilteredOptions = this.state.filteredOptions.length;
|
|
1409
|
+
this.setFilteredOptions(options);
|
|
1299
1410
|
return;
|
|
1300
1411
|
}
|
|
1301
1412
|
|
|
@@ -1310,8 +1421,7 @@ export default class SelecticStore {
|
|
|
1310
1421
|
}
|
|
1311
1422
|
|
|
1312
1423
|
if (!search && endIndex <= allOptionsLength) {
|
|
1313
|
-
this.
|
|
1314
|
-
this.state.totalFilteredOptions = totalAllOptions + this.state.groups.size;
|
|
1424
|
+
this.setFilteredOptions(this.buildGroupItems(allOptions), false, totalAllOptions + this.state.groups.size);
|
|
1315
1425
|
const isPartial = unref(this.isPartial);
|
|
1316
1426
|
if (isPartial && this.state.totalDynOptions === Infinity) {
|
|
1317
1427
|
this.fetchData();
|
|
@@ -1447,7 +1557,7 @@ export default class SelecticStore {
|
|
|
1447
1557
|
/* Added options are the same as previous ones.
|
|
1448
1558
|
* Stop fetching to avoid infinite loop
|
|
1449
1559
|
*/
|
|
1450
|
-
if (!this.hasFetchedAllItems) {
|
|
1560
|
+
if (!unref(this.hasFetchedAllItems)) {
|
|
1451
1561
|
/* Display error if all items are not fetch
|
|
1452
1562
|
* We can have the case where old value and result
|
|
1453
1563
|
* are the same but total is correct when the
|
|
@@ -1472,6 +1582,10 @@ export default class SelecticStore {
|
|
|
1472
1582
|
const options = this.buildGroupItems(result, previousItem);
|
|
1473
1583
|
const nbGroups1 = this.nbGroups(options);
|
|
1474
1584
|
|
|
1585
|
+
/* replace existing options by what have been received
|
|
1586
|
+
* or add received options.
|
|
1587
|
+
* This allow to manage requests received in different orders.
|
|
1588
|
+
*/
|
|
1475
1589
|
state.filteredOptions.splice(offset + dynOffset, limit + nbGroups1, ...options);
|
|
1476
1590
|
}
|
|
1477
1591
|
|
|
@@ -1481,6 +1595,7 @@ export default class SelecticStore {
|
|
|
1481
1595
|
}
|
|
1482
1596
|
|
|
1483
1597
|
state.totalFilteredOptions = total + nbGroups + dynOffset;
|
|
1598
|
+
this.updateGroupSelection();
|
|
1484
1599
|
|
|
1485
1600
|
if (search && state.totalFilteredOptions <= state.filteredOptions.length) {
|
|
1486
1601
|
this.addStaticFilteredOptions(true);
|
|
@@ -1538,8 +1653,7 @@ export default class SelecticStore {
|
|
|
1538
1653
|
options = this.filterOptions(unref(this.elementOptions), search);
|
|
1539
1654
|
break;
|
|
1540
1655
|
}
|
|
1541
|
-
this.
|
|
1542
|
-
this.state.totalFilteredOptions += options.length;
|
|
1656
|
+
this.setFilteredOptions(options, true);
|
|
1543
1657
|
}
|
|
1544
1658
|
}
|
|
1545
1659
|
|
|
@@ -1715,6 +1829,59 @@ export default class SelecticStore {
|
|
|
1715
1829
|
}
|
|
1716
1830
|
}
|
|
1717
1831
|
|
|
1832
|
+
/** update group item, to mark them as selected if needed */
|
|
1833
|
+
private updateGroupSelection() {
|
|
1834
|
+
const state = this.state;
|
|
1835
|
+
|
|
1836
|
+
if (!unref(this.allowGroupSelection)) {
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
const filteredOptions = state.filteredOptions;;
|
|
1841
|
+
const groupIdx = new Map<OptionId, number>();
|
|
1842
|
+
const groupAllSelected = new Map<OptionId, boolean>();
|
|
1843
|
+
const groupNbItem = new Map<OptionId, number>();
|
|
1844
|
+
|
|
1845
|
+
filteredOptions.forEach((option, idx) => {
|
|
1846
|
+
const groupId = option.group;
|
|
1847
|
+
|
|
1848
|
+
if (option.isGroup) {
|
|
1849
|
+
const id = option.id;
|
|
1850
|
+
groupIdx.set(id, idx);
|
|
1851
|
+
groupAllSelected.set(id, true);
|
|
1852
|
+
} else
|
|
1853
|
+
if (groupId !== undefined) {
|
|
1854
|
+
if (option.disabled || option.exclusive) {
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
groupNbItem.set(groupId, (groupNbItem.get(groupId) || 0) + 1)
|
|
1859
|
+
|
|
1860
|
+
if (!option.selected) {
|
|
1861
|
+
groupAllSelected.set(groupId, false);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1865
|
+
|
|
1866
|
+
for (const [id, idx] of groupIdx.entries()) {
|
|
1867
|
+
const group = filteredOptions[idx];
|
|
1868
|
+
group.selected = !!(groupAllSelected.get(id) && groupNbItem.get(id));
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
/** assign new value to the filteredOptions and apply change depending on it */
|
|
1873
|
+
private setFilteredOptions(options: OptionItem[], add = false, length = 0) {
|
|
1874
|
+
if (!add) {
|
|
1875
|
+
this.state.filteredOptions = options;
|
|
1876
|
+
this.state.totalFilteredOptions = length || options.length;
|
|
1877
|
+
} else {
|
|
1878
|
+
this.state.filteredOptions.push(...options);
|
|
1879
|
+
this.state.totalFilteredOptions += length || options.length;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
this.updateGroupSelection();
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1718
1885
|
/* }}} */
|
|
1719
1886
|
/* }}} */
|
|
1720
1887
|
|