selectic 3.2.1 → 3.3.1
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/.tool-versions +2 -2
- package/dist/selectic.common.js +17 -4
- package/dist/selectic.esm.js +17 -4
- package/package.json +5 -5
- package/src/Store.tsx +25 -2
- package/test/Store/Store_props.spec.js +56 -0
- package/types/Store.d.ts +22 -22
- package/types/index.d.ts +3 -3
package/.tool-versions
CHANGED
package/dist/selectic.common.js
CHANGED
|
@@ -1456,13 +1456,26 @@ class SelecticStore {
|
|
|
1456
1456
|
: false;
|
|
1457
1457
|
const hasOnlyValidValue = hasValue && !hasDisabledSelected && (Array.isArray(value) ? value.every((val) => this.hasValue(val)) :
|
|
1458
1458
|
this.hasValue(value));
|
|
1459
|
+
/* Whether at least one option, that is not disabled, is not selected. */
|
|
1460
|
+
const hasAvailableNonSelectedOptions = state.allOptions.some((opt) => {
|
|
1461
|
+
const isOptionSelected = (Array.isArray(selectedOptions)
|
|
1462
|
+
? selectedOptions.some((selectedOpt) => selectedOpt.id === opt.id)
|
|
1463
|
+
: selectedOptions && selectedOptions.id === opt.id);
|
|
1464
|
+
return !isOptionSelected && !opt.disabled;
|
|
1465
|
+
});
|
|
1459
1466
|
const isEmpty = nbEnabled === 0;
|
|
1460
|
-
const hasOnlyOneOption = nbEnabled === 1 && hasOnlyValidValue && !state.allowClearSelection;
|
|
1467
|
+
const hasOnlyOneOption = (nbEnabled === 1 && hasOnlyValidValue && !state.allowClearSelection);
|
|
1468
|
+
/* In most cases if `hasOnlyOneOption` is true, we disable selection.
|
|
1469
|
+
* However, if we are not in multi-select mode and if the only option available is not the selected one then
|
|
1470
|
+
* we do not disable selection (to let the user switch from its current disabled selection,
|
|
1471
|
+
* and a valid one).
|
|
1472
|
+
*/
|
|
1473
|
+
const cannotSelectAnyOption = hasOnlyOneOption && (Array.isArray(selectedOptions) || !hasAvailableNonSelectedOptions);
|
|
1461
1474
|
const isExclusiveDisabledItem = Array.isArray(selectedOptions) /* which means "multiple" mode */
|
|
1462
1475
|
&& selectedOptions.length === 1
|
|
1463
1476
|
&& selectedOptions[0].exclusive
|
|
1464
1477
|
&& selectedOptions[0].disabled;
|
|
1465
|
-
if (
|
|
1478
|
+
if (cannotSelectAnyOption || isEmpty || isExclusiveDisabledItem) {
|
|
1466
1479
|
if (state.isOpen) {
|
|
1467
1480
|
this.setAutomaticClose();
|
|
1468
1481
|
this.commit('isOpen', false);
|
|
@@ -3317,7 +3330,7 @@ let Selectic = class Selectic extends vtyx.Vue {
|
|
|
3317
3330
|
multiple: ((_a = this.multiple) !== null && _a !== void 0 ? _a : false) !== false,
|
|
3318
3331
|
pageSize: this.params.pageSize || 100,
|
|
3319
3332
|
hideFilter: (_b = this.params.hideFilter) !== null && _b !== void 0 ? _b : 'auto',
|
|
3320
|
-
allowRevert: this.params.allowRevert,
|
|
3333
|
+
allowRevert: this.params.allowRevert, /* it can be undefined */
|
|
3321
3334
|
forceSelectAll: this.params.forceSelectAll || 'auto',
|
|
3322
3335
|
allowClearSelection: this.params.allowClearSelection || false,
|
|
3323
3336
|
autoSelect: this.params.autoSelect === undefined
|
|
@@ -3331,7 +3344,7 @@ let Selectic = class Selectic extends vtyx.Vue {
|
|
|
3331
3344
|
formatOption: this.params.formatOption,
|
|
3332
3345
|
formatSelection: this.params.formatSelection,
|
|
3333
3346
|
listPosition: this.params.listPosition || 'auto',
|
|
3334
|
-
optionBehavior: this.params.optionBehavior,
|
|
3347
|
+
optionBehavior: this.params.optionBehavior, /* it can be undefined */
|
|
3335
3348
|
isOpen: ((_c = this.open) !== null && _c !== void 0 ? _c : false) !== false,
|
|
3336
3349
|
disableGroupSelection: this.params.disableGroupSelection,
|
|
3337
3350
|
},
|
package/dist/selectic.esm.js
CHANGED
|
@@ -1452,13 +1452,26 @@ class SelecticStore {
|
|
|
1452
1452
|
: false;
|
|
1453
1453
|
const hasOnlyValidValue = hasValue && !hasDisabledSelected && (Array.isArray(value) ? value.every((val) => this.hasValue(val)) :
|
|
1454
1454
|
this.hasValue(value));
|
|
1455
|
+
/* Whether at least one option, that is not disabled, is not selected. */
|
|
1456
|
+
const hasAvailableNonSelectedOptions = state.allOptions.some((opt) => {
|
|
1457
|
+
const isOptionSelected = (Array.isArray(selectedOptions)
|
|
1458
|
+
? selectedOptions.some((selectedOpt) => selectedOpt.id === opt.id)
|
|
1459
|
+
: selectedOptions && selectedOptions.id === opt.id);
|
|
1460
|
+
return !isOptionSelected && !opt.disabled;
|
|
1461
|
+
});
|
|
1455
1462
|
const isEmpty = nbEnabled === 0;
|
|
1456
|
-
const hasOnlyOneOption = nbEnabled === 1 && hasOnlyValidValue && !state.allowClearSelection;
|
|
1463
|
+
const hasOnlyOneOption = (nbEnabled === 1 && hasOnlyValidValue && !state.allowClearSelection);
|
|
1464
|
+
/* In most cases if `hasOnlyOneOption` is true, we disable selection.
|
|
1465
|
+
* However, if we are not in multi-select mode and if the only option available is not the selected one then
|
|
1466
|
+
* we do not disable selection (to let the user switch from its current disabled selection,
|
|
1467
|
+
* and a valid one).
|
|
1468
|
+
*/
|
|
1469
|
+
const cannotSelectAnyOption = hasOnlyOneOption && (Array.isArray(selectedOptions) || !hasAvailableNonSelectedOptions);
|
|
1457
1470
|
const isExclusiveDisabledItem = Array.isArray(selectedOptions) /* which means "multiple" mode */
|
|
1458
1471
|
&& selectedOptions.length === 1
|
|
1459
1472
|
&& selectedOptions[0].exclusive
|
|
1460
1473
|
&& selectedOptions[0].disabled;
|
|
1461
|
-
if (
|
|
1474
|
+
if (cannotSelectAnyOption || isEmpty || isExclusiveDisabledItem) {
|
|
1462
1475
|
if (state.isOpen) {
|
|
1463
1476
|
this.setAutomaticClose();
|
|
1464
1477
|
this.commit('isOpen', false);
|
|
@@ -3313,7 +3326,7 @@ let Selectic = class Selectic extends Vue {
|
|
|
3313
3326
|
multiple: ((_a = this.multiple) !== null && _a !== void 0 ? _a : false) !== false,
|
|
3314
3327
|
pageSize: this.params.pageSize || 100,
|
|
3315
3328
|
hideFilter: (_b = this.params.hideFilter) !== null && _b !== void 0 ? _b : 'auto',
|
|
3316
|
-
allowRevert: this.params.allowRevert,
|
|
3329
|
+
allowRevert: this.params.allowRevert, /* it can be undefined */
|
|
3317
3330
|
forceSelectAll: this.params.forceSelectAll || 'auto',
|
|
3318
3331
|
allowClearSelection: this.params.allowClearSelection || false,
|
|
3319
3332
|
autoSelect: this.params.autoSelect === undefined
|
|
@@ -3327,7 +3340,7 @@ let Selectic = class Selectic extends Vue {
|
|
|
3327
3340
|
formatOption: this.params.formatOption,
|
|
3328
3341
|
formatSelection: this.params.formatSelection,
|
|
3329
3342
|
listPosition: this.params.listPosition || 'auto',
|
|
3330
|
-
optionBehavior: this.params.optionBehavior,
|
|
3343
|
+
optionBehavior: this.params.optionBehavior, /* it can be undefined */
|
|
3331
3344
|
isOpen: ((_c = this.open) !== null && _c !== void 0 ? _c : false) !== false,
|
|
3332
3345
|
disableGroupSelection: this.params.disableGroupSelection,
|
|
3333
3346
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "selectic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"description": "Smart Select for VueJS 3.x",
|
|
5
5
|
"main": "dist/selectic.common.js",
|
|
6
6
|
"module": "dist/selectic.esm.js",
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
"test": "npm run build && tape test/**/*.spec.js"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"vtyx": "4.
|
|
42
|
+
"vtyx": "4.4.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@babel/types": "^7.
|
|
46
|
-
"rollup": "^2.
|
|
45
|
+
"@babel/types": "^7.29.0",
|
|
46
|
+
"rollup": "^2.80.0",
|
|
47
47
|
"rollup-plugin-postcss": "^4.0.2",
|
|
48
48
|
"tape": "^4.17.0",
|
|
49
|
-
"typescript": "~
|
|
49
|
+
"typescript": "~5.9"
|
|
50
50
|
}
|
|
51
51
|
}
|
package/src/Store.tsx
CHANGED
|
@@ -1988,14 +1988,37 @@ export default class SelecticStore {
|
|
|
1988
1988
|
this.hasValue(value)
|
|
1989
1989
|
);
|
|
1990
1990
|
|
|
1991
|
+
/* Whether at least one option, that is not disabled, is not selected. */
|
|
1992
|
+
const hasAvailableNonSelectedOptions = state.allOptions.some((opt) => {
|
|
1993
|
+
const isOptionSelected = (
|
|
1994
|
+
Array.isArray(selectedOptions)
|
|
1995
|
+
? selectedOptions.some((selectedOpt) => selectedOpt.id === opt.id)
|
|
1996
|
+
: selectedOptions && selectedOptions.id === opt.id
|
|
1997
|
+
);
|
|
1998
|
+
|
|
1999
|
+
return !isOptionSelected && !opt.disabled;
|
|
2000
|
+
});
|
|
2001
|
+
|
|
1991
2002
|
const isEmpty = nbEnabled === 0;
|
|
1992
|
-
const hasOnlyOneOption =
|
|
2003
|
+
const hasOnlyOneOption = (
|
|
2004
|
+
nbEnabled === 1 && hasOnlyValidValue && !state.allowClearSelection
|
|
2005
|
+
);
|
|
2006
|
+
|
|
2007
|
+
/* In most cases if `hasOnlyOneOption` is true, we disable selection.
|
|
2008
|
+
* However, if we are not in multi-select mode and if the only option available is not the selected one then
|
|
2009
|
+
* we do not disable selection (to let the user switch from its current disabled selection,
|
|
2010
|
+
* and a valid one).
|
|
2011
|
+
*/
|
|
2012
|
+
const cannotSelectAnyOption = hasOnlyOneOption && (
|
|
2013
|
+
Array.isArray(selectedOptions) || !hasAvailableNonSelectedOptions
|
|
2014
|
+
);
|
|
2015
|
+
|
|
1993
2016
|
const isExclusiveDisabledItem = Array.isArray(selectedOptions) /* which means "multiple" mode */
|
|
1994
2017
|
&& selectedOptions.length === 1
|
|
1995
2018
|
&& selectedOptions[0].exclusive
|
|
1996
2019
|
&& selectedOptions[0].disabled;
|
|
1997
2020
|
|
|
1998
|
-
if (
|
|
2021
|
+
if (cannotSelectAnyOption || isEmpty || isExclusiveDisabledItem) {
|
|
1999
2022
|
if (state.isOpen) {
|
|
2000
2023
|
this.setAutomaticClose();
|
|
2001
2024
|
this.commit('isOpen', false);
|
|
@@ -229,6 +229,62 @@ tape.test('change props', (subT) => {
|
|
|
229
229
|
t.end();
|
|
230
230
|
});
|
|
231
231
|
|
|
232
|
+
sTest.test('should not disable the select, if selected item is disabled but there is another option',
|
|
233
|
+
async (t) => {
|
|
234
|
+
const options = getOptions(2, 'alpha');
|
|
235
|
+
options[1].disabled = true;
|
|
236
|
+
|
|
237
|
+
const store = new Store({
|
|
238
|
+
value: 1,
|
|
239
|
+
options: options,
|
|
240
|
+
disabled: false,
|
|
241
|
+
params: {
|
|
242
|
+
autoDisabled: true,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
store.commit('isOpen', true);
|
|
247
|
+
await _.nextVueTick(store);
|
|
248
|
+
|
|
249
|
+
t.is(store.state.selectedOptions.text, 'alpha1');
|
|
250
|
+
t.is(store.state.internalValue, 1);
|
|
251
|
+
t.is(store.state.disabled, false);
|
|
252
|
+
|
|
253
|
+
/* Once we choose the only valid option, disable the selection */
|
|
254
|
+
store.props.value = 0;
|
|
255
|
+
await _.nextVueTick(store);
|
|
256
|
+
|
|
257
|
+
t.is(store.state.selectedOptions.text, 'alpha0');
|
|
258
|
+
t.is(store.state.internalValue, 0);
|
|
259
|
+
t.is(store.state.disabled, true);
|
|
260
|
+
|
|
261
|
+
t.end();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
sTest.test('multi select should not be disabled, even if only disabled options are left',
|
|
265
|
+
async (t) => {
|
|
266
|
+
const options = getOptions(3, 'alpha');
|
|
267
|
+
options[1].disabled = true;
|
|
268
|
+
options[2].disabled = true;
|
|
269
|
+
|
|
270
|
+
const store = new Store({
|
|
271
|
+
value: [0, 1],
|
|
272
|
+
options: options,
|
|
273
|
+
disabled: false,
|
|
274
|
+
params: {
|
|
275
|
+
autoDisabled: true,
|
|
276
|
+
multiple: true,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
store.commit('isOpen', true);
|
|
281
|
+
await _.nextVueTick(store);
|
|
282
|
+
|
|
283
|
+
t.deepEqual(store.state.internalValue, [0, 1]);
|
|
284
|
+
t.is(store.state.disabled, false);
|
|
285
|
+
t.end();
|
|
286
|
+
});
|
|
287
|
+
|
|
232
288
|
sTest.test('should not disable the select with an invalid value', async (t) => {
|
|
233
289
|
const store = new Store({
|
|
234
290
|
value: 2,
|
package/types/Store.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ComputedRef } from 'vue';
|
|
2
|
-
|
|
2
|
+
type MandateProps<T extends {}> = {
|
|
3
3
|
[TK in keyof T]-?: T[TK];
|
|
4
4
|
};
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
5
|
+
export type StrictOptionId = string | number;
|
|
6
|
+
export type OptionId = StrictOptionId | null;
|
|
7
|
+
export type SelectedValue = OptionId | StrictOptionId[];
|
|
8
8
|
export interface OptionValue {
|
|
9
9
|
id: OptionId;
|
|
10
10
|
text: string;
|
|
@@ -18,19 +18,19 @@ export interface OptionValue {
|
|
|
18
18
|
exclusive?: boolean;
|
|
19
19
|
data?: any;
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
type OptionBehaviorOperation = 'sort' | 'force';
|
|
22
|
+
type OptionBehaviorOrder = 'O' | 'D' | 'E';
|
|
23
23
|
export interface OptionItem extends OptionValue {
|
|
24
24
|
selected: boolean;
|
|
25
25
|
disabled: boolean;
|
|
26
26
|
isGroup: boolean;
|
|
27
27
|
}
|
|
28
|
-
export
|
|
28
|
+
export type OptionProp = OptionValue | string;
|
|
29
29
|
export interface GroupValue {
|
|
30
30
|
id: StrictOptionId;
|
|
31
31
|
text: string;
|
|
32
32
|
}
|
|
33
|
-
export
|
|
33
|
+
export type RequestResult = {
|
|
34
34
|
/** The total number of expecting options.
|
|
35
35
|
* Needed to know if there are more items to fetch, and to size the scrollbar.
|
|
36
36
|
*/
|
|
@@ -38,20 +38,20 @@ export declare type RequestResult = {
|
|
|
38
38
|
/** The list of the options. */
|
|
39
39
|
result: OptionValue[];
|
|
40
40
|
};
|
|
41
|
-
export
|
|
42
|
-
export
|
|
43
|
-
export
|
|
44
|
-
export
|
|
41
|
+
export type FetchCallback = (_search: string, _offsetItem: number, _pageSize: number) => Promise<RequestResult>;
|
|
42
|
+
export type GetCallback = (_ids: OptionId[]) => Promise<OptionValue[]>;
|
|
43
|
+
export type FormatCallback = (_option: OptionItem) => OptionItem;
|
|
44
|
+
export type SelectionOverflow =
|
|
45
45
|
/** Items are reduced in width and an ellipsis is displayed in their name. */
|
|
46
46
|
'collapsed' | 'multiline';
|
|
47
|
-
export
|
|
47
|
+
export type ListPosition =
|
|
48
48
|
/** Display the list at bottom */
|
|
49
49
|
'bottom'
|
|
50
50
|
/** Display the list at bottom */
|
|
51
51
|
| 'top'
|
|
52
52
|
/** Display the list at bottom but if there is not enough space, display it at top */
|
|
53
53
|
| 'auto';
|
|
54
|
-
export
|
|
54
|
+
export type HideFilter =
|
|
55
55
|
/** Display or hide the filter panel */
|
|
56
56
|
boolean
|
|
57
57
|
/** The handler to open the filter panel is hidden only if there is less
|
|
@@ -59,7 +59,7 @@ boolean
|
|
|
59
59
|
| 'auto'
|
|
60
60
|
/** The panel filter is always open */
|
|
61
61
|
| 'open';
|
|
62
|
-
export
|
|
62
|
+
export type SelectAllOption =
|
|
63
63
|
/** Display the "select all" only when data are all fetched or allowRevert */
|
|
64
64
|
'auto'
|
|
65
65
|
/** Always display the "select all" in mulitple mode. */
|
|
@@ -151,7 +151,7 @@ export interface Props {
|
|
|
151
151
|
/** Method to call to get specific item */
|
|
152
152
|
getItemsCallback?: GetCallback | null;
|
|
153
153
|
}
|
|
154
|
-
|
|
154
|
+
type InternalProps = MandateProps<Props>;
|
|
155
155
|
export interface Data {
|
|
156
156
|
/** Number of items displayed in a page (before scrolling) */
|
|
157
157
|
itemsPerPage: number;
|
|
@@ -253,11 +253,11 @@ export interface SelecticStoreState {
|
|
|
253
253
|
automaticClose: boolean;
|
|
254
254
|
};
|
|
255
255
|
}
|
|
256
|
-
export
|
|
257
|
-
export
|
|
258
|
-
export
|
|
259
|
-
export
|
|
260
|
-
export
|
|
256
|
+
export type IconFamily = '' | 'selectic' | 'font-awesome-4' | 'font-awesome-5' | 'font-awesome-6' | 'raw' | `prefix:${string}`;
|
|
257
|
+
export type IconKey = 'caret-down' | 'caret-up' | 'check' | 'dot' | 'search' | 'spinner' | 'strikethrough' | 'times' | 'question' | 'spin';
|
|
258
|
+
export type IconValue = `selectic:${IconKey}${'' | ':spin'}` | `raw:${string}` | `current:${IconKey}${'' | ':spin'}` | string;
|
|
259
|
+
export type Icons = Record<IconKey, IconValue>;
|
|
260
|
+
export type PartialIcons = {
|
|
261
261
|
[K in IconKey]?: Icons[K];
|
|
262
262
|
};
|
|
263
263
|
interface Messages {
|
|
@@ -279,7 +279,7 @@ interface Messages {
|
|
|
279
279
|
unknownPropertyValue: string;
|
|
280
280
|
wrongQueryResult: string;
|
|
281
281
|
}
|
|
282
|
-
export
|
|
282
|
+
export type PartialMessages = {
|
|
283
283
|
[K in keyof Messages]?: Messages[K];
|
|
284
284
|
};
|
|
285
285
|
export declare function changeTexts(texts: PartialMessages): void;
|
package/types/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { OptionProp, OptionId, StrictOptionId, GroupValue, SelectedValue, FetchC
|
|
|
4
4
|
import MainInput from './MainInput';
|
|
5
5
|
import ExtendedList from './ExtendedList';
|
|
6
6
|
export { GroupValue, OptionValue, OptionItem, OptionProp, OptionId, StrictOptionId, SelectedValue, PartialMessages, GetCallback, FetchCallback, FormatCallback, SelectionOverflow, ListPosition, HideFilter, };
|
|
7
|
-
|
|
7
|
+
type EventType = 'input' | 'change' | 'open' | 'close' | 'focus' | 'blur' | 'item:click';
|
|
8
8
|
export interface EventOptions {
|
|
9
9
|
instance: Selectic;
|
|
10
10
|
eventType: EventType;
|
|
@@ -77,8 +77,8 @@ export interface ParamProps {
|
|
|
77
77
|
/** Avoid click on group name to select all items in this group. */
|
|
78
78
|
disableGroupSelection?: boolean;
|
|
79
79
|
}
|
|
80
|
-
export
|
|
81
|
-
export
|
|
80
|
+
export type OnCallback = (event: string, ...args: any[]) => void;
|
|
81
|
+
export type GetMethodsCallback = (methods: {
|
|
82
82
|
clearCache: Selectic['clearCache'];
|
|
83
83
|
changeTexts: Selectic['changeTexts'];
|
|
84
84
|
changeIcons: Selectic['changeIcons'];
|