selectic 1.3.11 → 3.0.3

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/src/Store.tsx CHANGED
@@ -1,13 +1,17 @@
1
1
  /* File Purpose:
2
2
  * It keeps and computes all states at a single place.
3
- * Every inner components of Selectic should comunicate with this file to
3
+ * Every inner components of Selectic should communicate with this file to
4
4
  * change or to get states.
5
5
  */
6
6
 
7
- import {Vue, Component, Prop, Watch} from 'vtyx';
7
+ import { reactive, watch, unref, computed, ComputedRef } from 'vue';
8
8
 
9
9
  /* {{{ Types definitions */
10
10
 
11
+ type MandateProps<T extends {}> = {
12
+ [TK in keyof T]-?: T[TK];
13
+ }
14
+
11
15
  type voidCaller = () => void;
12
16
 
13
17
  export type StrictOptionId = string | number;
@@ -65,9 +69,18 @@ export type ListPosition =
65
69
  'bottom'
66
70
  /* Display the list at bottom */
67
71
  | 'top'
68
- /* Display the list at bootom but if there is not enough space, display it at top */
72
+ /* Display the list at bottom but if there is not enough space, display it at top */
69
73
  | 'auto';
70
74
 
75
+ export type HideFilter =
76
+ /* Display or hide the filter panel */
77
+ boolean
78
+ /* The handler to open the filter panel is hidden only if there is less
79
+ * than 10 options */
80
+ | 'auto'
81
+ /* The panel filter is always open */
82
+ | 'open';
83
+
71
84
  export interface SelecticStoreStateParams {
72
85
  /* Equivalent of <select>'s "multiple" attribute */
73
86
  multiple?: boolean;
@@ -76,7 +89,7 @@ export interface SelecticStoreStateParams {
76
89
  placeholder?: string;
77
90
 
78
91
  /* Hide filter component when enabled */
79
- hideFilter?: boolean | 'auto';
92
+ hideFilter?: HideFilter;
80
93
 
81
94
  /* Allow to reverse selection.
82
95
  * If true, parent should support the selectionIsExcluded property.
@@ -124,31 +137,34 @@ export interface SelecticStoreStateParams {
124
137
  */
125
138
  optionBehavior?: string;
126
139
 
140
+ /* Indicate where the list should be deployed */
141
+ listPosition?: ListPosition;
142
+
127
143
  /* If true, the component is open at start */
128
144
  isOpen?: boolean;
129
145
  }
130
146
 
131
147
  export interface Props {
132
148
  /* Selected value */
133
- value?: SelectedValue;
149
+ value?: SelectedValue | null;
134
150
 
135
151
  /* If true, the value represents the ones we don't want to select */
136
152
  selectionIsExcluded?: boolean;
137
153
 
138
- /* Equivalent of "disabled" select's attribute */
154
+ /* Equivalent of "disabled" Select's attribute */
139
155
  disabled?: boolean;
140
156
 
141
157
  /* List of options to display */
142
- options?: OptionProp[];
158
+ options?: OptionProp[] | null;
143
159
 
144
160
  /* List of options to display from child elements */
145
- childOptions?: OptionProp[];
161
+ childOptions?: OptionValue[];
146
162
 
147
163
  /* Define groups which will be used by items */
148
164
  groups?: GroupValue[];
149
165
 
150
166
  /* Overwrite default texts */
151
- texts?: PartialMessages;
167
+ texts?: PartialMessages | null;
152
168
 
153
169
  /* Keep this component open if another Selectic component opens */
154
170
  keepOpenWithOtherSelectic?: boolean;
@@ -157,10 +173,24 @@ export interface Props {
157
173
  params?: SelecticStoreStateParams;
158
174
 
159
175
  /* Method to call to fetch extra data */
160
- fetchCallback?: FetchCallback;
176
+ fetchCallback?: FetchCallback | null;
161
177
 
162
178
  /* Method to call to get specific item */
163
- getItemsCallback?: GetCallback;
179
+ getItemsCallback?: GetCallback | null;
180
+ }
181
+
182
+ type InternalProps = MandateProps<Props>;
183
+
184
+ export interface Data {
185
+ /* Number of items displayed in a page (before scrolling) */
186
+ itemsPerPage: number;
187
+
188
+ labels: Messages;
189
+ /* used to avoid checking and updating table while doing batch stuff */
190
+ doNotUpdate: boolean;
191
+ cacheItem: Map<OptionId, OptionValue>;
192
+ activeOrder: OptionBehaviorOrder;
193
+ dynOffset: number;
164
194
  }
165
195
 
166
196
  export interface SelecticStoreState {
@@ -182,6 +212,9 @@ export interface SelecticStoreState {
182
212
  /* If true, filters and controls are hidden */
183
213
  hideFilter: boolean;
184
214
 
215
+ /* If true, the filter panel is always open */
216
+ keepFilterOpen: boolean;
217
+
185
218
  /* Allow to reverse selection.
186
219
  * If true, parent should support the selectionIsExcluded property.
187
220
  * If false, the action is never available.
@@ -191,7 +224,7 @@ export interface SelecticStoreState {
191
224
  allowRevert?: boolean;
192
225
 
193
226
  /* If true, user can clear current selection
194
- * (if false, it is still possible to clear it programatically) */
227
+ * (if false, it is still possible to clear it programmatically) */
195
228
  allowClearSelection: boolean;
196
229
 
197
230
  /* If false, do not select the first available option even if value is mandatory */
@@ -273,6 +306,12 @@ export interface SelecticStoreState {
273
306
 
274
307
  /* If true, a change has been done by user */
275
308
  hasChanged: boolean;
309
+
310
+ /* If true, it means the current change has been done automatically by Selectic */
311
+ automaticChange: boolean;
312
+
313
+ /* If true, it means the current close has been done automatically by Selectic */
314
+ automaticClose: boolean;
276
315
  };
277
316
  }
278
317
 
@@ -298,6 +337,7 @@ interface Messages {
298
337
  export type PartialMessages = { [K in keyof Messages]?: Messages[K] };
299
338
 
300
339
  /* }}} */
340
+ /* {{{ Helper */
301
341
 
302
342
  /**
303
343
  * Escape search string to consider regexp special characters as they
@@ -317,6 +357,30 @@ function convertToRegExp(name: string, flag = 'i'): RegExp {
317
357
  return new RegExp(pattern, flag);
318
358
  }
319
359
 
360
+ /** Does the same as Object.assign but does not replace if value is undefined */
361
+ function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial<T>>): T {
362
+ const result = obj;
363
+ for (const source of sourceObjects) {
364
+ for (const key of Object.keys(source)) {
365
+ const value = source[key as keyof T];
366
+ if (value === undefined) {
367
+ continue;
368
+ }
369
+ result[key as keyof T] = value;
370
+ }
371
+ }
372
+ return result as T;
373
+ }
374
+
375
+ /* }}} */
376
+ /* {{{ Static */
377
+
378
+ export function changeTexts(texts: PartialMessages) {
379
+ messages = Object.assign(messages, texts);
380
+ }
381
+
382
+ /* }}} */
383
+
320
384
  let messages: Messages = {
321
385
  noFetchMethod: 'Fetch callback is missing: it is not possible to retrieve data.',
322
386
  searchPlaceholder: 'Search',
@@ -338,62 +402,21 @@ let messages: Messages = {
338
402
 
339
403
  let closePreviousSelectic: undefined | voidCaller;
340
404
 
341
- /* {{{ Static */
342
-
343
- export function changeTexts(texts: PartialMessages) {
344
- messages = Object.assign(messages, texts);
345
- }
346
-
347
405
  /* }}} */
348
406
 
349
- @Component
350
- export default class SelecticStore extends Vue<Props> {
351
- /* {{{ props */
352
-
353
- @Prop()
354
- public value?: SelectedValue;
355
-
356
- @Prop({default: false})
357
- public selectionIsExcluded: boolean;
358
-
359
- @Prop({default: false})
360
- public disabled: boolean;
361
-
362
- @Prop()
363
- public options?: OptionProp[];
364
-
365
- @Prop()
366
- public childOptions?: OptionValue[];
367
-
368
- @Prop({default: () => []})
369
- public groups: GroupValue[];
370
-
371
- @Prop()
372
- public texts?: PartialMessages;
407
+ let uid = 0;
373
408
 
374
- @Prop()
375
- private params?: SelecticStoreStateParams;
409
+ export default class SelecticStore {
410
+ public props: InternalProps;
376
411
 
377
- @Prop()
378
- private fetchCallback?: FetchCallback;
379
-
380
- @Prop()
381
- private getItemsCallback?: GetCallback;
382
-
383
- @Prop({ default: false })
384
- private keepOpenWithOtherSelectic: boolean;
385
-
386
- /* }}} */
387
412
  /* {{{ data */
388
413
 
389
- /* Number of items displayed in a page (before scrolling) */
390
- public itemsPerPage = 10;
391
-
392
- public state: SelecticStoreState = {
414
+ public state = reactive<SelecticStoreState>({
393
415
  multiple: false,
394
416
  disabled: false,
395
417
  placeholder: '',
396
418
  hideFilter: false,
419
+ keepFilterOpen: false,
397
420
  allowRevert: undefined,
398
421
  allowClearSelection: false,
399
422
  autoSelect: true,
@@ -426,59 +449,224 @@ export default class SelecticStore extends Vue<Props> {
426
449
  errorMessage: '',
427
450
  areAllSelected: false,
428
451
  hasChanged: false,
452
+ automaticChange: false,
453
+ automaticClose: false,
429
454
  },
430
- };
431
- public labels = Object.assign({}, messages);
432
- /* used to avoid checking and updating table while doing batch stuff */
433
- private doNotUpdate = false;
434
- private cacheItem: Map<OptionId, OptionValue> = new Map();
435
- private activeOrder: OptionBehaviorOrder = 'D';
436
- private dynOffset: number = 0;
455
+ });
456
+ public data: Data;
437
457
 
438
458
  /* Do not need reactivity */
439
- private requestId: number;
459
+ private requestId: number = 0;
440
460
  private cacheRequest: Map<string, Promise<OptionValue[]>>;
461
+ private closeSelectic: () => void;
441
462
 
442
463
  /* }}} */
443
464
  /* {{{ computed */
444
465
 
445
466
  /* Number of item to pre-display */
446
- get marginSize() {
447
- return this.state.pageSize / 2;
448
- }
467
+ public marginSize: ComputedRef<number>;
449
468
 
450
- get isPartial(): boolean {
451
- const state = this.state;
452
- let isPartial = typeof this.fetchCallback === 'function';
469
+ public isPartial: ComputedRef<boolean>;
470
+ public hasAllItems: ComputedRef<boolean>;
471
+ public hasFetchedAllItems: ComputedRef<boolean>;
472
+ private listOptions: ComputedRef<OptionValue[]>;
473
+ private elementOptions: ComputedRef<OptionValue[]>;
453
474
 
454
- if (isPartial && state.optionBehaviorOperation === 'force' && this.activeOrder !== 'D') {
455
- isPartial = false;
475
+ /* }}} */
476
+
477
+ public _uid: number; /* Mainly for debugging */
478
+
479
+ constructor(props: Props = {}) {
480
+ this._uid = ++uid;
481
+
482
+ /* {{{ Props */
483
+
484
+ const defaultProps: InternalProps = {
485
+ value: null,
486
+ selectionIsExcluded: false,
487
+ disabled: false,
488
+ options: null,
489
+ childOptions: [],
490
+ groups: [],
491
+ texts: null,
492
+ params: {},
493
+ fetchCallback: null,
494
+ getItemsCallback: null,
495
+ keepOpenWithOtherSelectic: false,
496
+ };
497
+ const propsVal: InternalProps = assignObject(defaultProps, props);
498
+ this.props = reactive(propsVal);
499
+
500
+ /* }}} */
501
+ /* {{{ data */
502
+
503
+ this.data = reactive({
504
+ labels: Object.assign({}, messages),
505
+ itemsPerPage: 10,
506
+ doNotUpdate: false,
507
+ cacheItem: new Map(),
508
+ activeOrder: 'D',
509
+ dynOffset: 0,
510
+ });
511
+
512
+ /* }}} */
513
+ /* {{{ computed */
514
+
515
+ this.marginSize = computed(() => {
516
+ return this.state.pageSize / 2;
517
+ });
518
+
519
+ this.isPartial = computed(() => {
520
+ const state = this.state;
521
+ let isPartial = typeof this.props.fetchCallback === 'function';
522
+
523
+ if (isPartial &&
524
+ state.optionBehaviorOperation === 'force' &&
525
+ this.data.activeOrder !== 'D'
526
+ ) {
527
+ isPartial = false;
528
+ }
529
+
530
+ return isPartial;
531
+ });
532
+
533
+ this.hasAllItems = computed(() => {
534
+ const state = this.state;
535
+ const nbItems = state.totalFilteredOptions + state.groups.size;
536
+
537
+ return this.state.filteredOptions.length >= nbItems;
538
+ });
539
+
540
+ this.hasFetchedAllItems = computed(() => {
541
+ const isPartial = unref(this.isPartial);
542
+
543
+ if (!isPartial) {
544
+ return true;
545
+ }
546
+ const state = this.state;
547
+
548
+ return state.dynOptions.length === state.totalDynOptions;
549
+ });
550
+
551
+ this.listOptions = computed(() => {
552
+ return this.getListOptions();
553
+ });
554
+
555
+ this.elementOptions = computed(() => {
556
+ return this.getElementOptions();
557
+ });
558
+
559
+ /* }}} */
560
+ /* {{{ watch */
561
+
562
+ watch(() => [this.props.options, this.props.childOptions], () => {
563
+ this.data.cacheItem.clear();
564
+ this.setAutomaticClose();
565
+ this.commit('isOpen', false);
566
+ this.buildAllOptions(true);
567
+ this.buildSelectedOptions();
568
+ });
569
+
570
+ watch(() => [this.listOptions, this.elementOptions], () => {
571
+ /* TODO: transform allOptions as a computed properties and this
572
+ * watcher become useless */
573
+ this.buildAllOptions(true);
574
+ });
575
+
576
+ watch(() => this.props.value, () => {
577
+ const value = this.props.value ?? null;
578
+ this.commit('internalValue', value);
579
+ });
580
+
581
+ watch(() => this.props.selectionIsExcluded, () => {
582
+ this.commit('selectionIsExcluded', this.props.selectionIsExcluded);
583
+ });
584
+
585
+ watch(() => this.props.disabled, () => {
586
+ this.commit('disabled', this.props.disabled);
587
+ });
588
+
589
+ watch(() => this.state.filteredOptions, () => {
590
+ let areAllSelected = false;
591
+ const hasAllItems = unref(this.hasAllItems);
592
+
593
+ if (hasAllItems) {
594
+ const selectionIsExcluded = +this.state.selectionIsExcluded;
595
+ /* eslint-disable-next-line no-bitwise */
596
+ areAllSelected = this.state.filteredOptions.every((item) =>
597
+ !!(+item.selected ^ selectionIsExcluded));
598
+ }
599
+
600
+ this.state.status.areAllSelected = areAllSelected;
601
+ });
602
+
603
+ watch(() => this.state.internalValue, () => {
604
+ this.buildSelectedOptions();
605
+ });
606
+
607
+ watch(() => this.state.allOptions, () => {
608
+ this.checkAutoSelect();
609
+ this.checkAutoDisabled();
610
+ });
611
+
612
+ watch(() => this.state.totalAllOptions, () => {
613
+ this.checkHideFilter();
614
+ });
615
+
616
+ /* }}} */
617
+
618
+ this.closeSelectic = () => {
619
+ this.setAutomaticClose();
620
+ this.commit('isOpen', false);
456
621
  }
457
622
 
458
- return isPartial;
459
- }
623
+ const value = this.props.value;
460
624
 
461
- get hasAllItems() {
462
- const nbItems = this.state.totalFilteredOptions + this.state.groups.size;
625
+ /* set initial value for non reactive attribute */
626
+ this.cacheRequest = new Map();
463
627
 
464
- return this.state.filteredOptions.length >= nbItems;
465
- }
628
+ const stateParam: SelecticStoreStateParams | SelecticStoreState =
629
+ Object.assign({}, this.props.params);
466
630
 
467
- get hasFetchedAllItems() {
468
- const state = this.state;
631
+ if (stateParam.optionBehavior) {
632
+ this.buildOptionBehavior(
633
+ stateParam.optionBehavior,
634
+ stateParam as SelecticStoreState
635
+ );
636
+ delete stateParam.optionBehavior;
637
+ }
469
638
 
470
- if (!this.isPartial) {
471
- return true;
639
+ if (stateParam.hideFilter === 'auto') {
640
+ delete stateParam.hideFilter;
641
+ } else if (stateParam.hideFilter === 'open') {
642
+ this.state.keepFilterOpen = true;
643
+ delete stateParam.hideFilter;
472
644
  }
473
645
 
474
- return state.dynOptions.length === state.totalDynOptions;
475
- }
646
+ /* Update state */
647
+ assignObject(this.state, stateParam as SelecticStoreState);
648
+ /* XXX: should be done in 2 lines, in order to set the multiple state
649
+ * and ensure convertValue run with correct state */
650
+ assignObject(this.state, {
651
+ internalValue: this.convertTypeValue(value),
652
+ selectionIsExcluded: props.selectionIsExcluded,
653
+ disabled: props.disabled,
654
+ });
655
+
656
+ this.checkHideFilter();
476
657
 
477
- get closeSelectic() {
478
- return () => this.commit('isOpen', false);
658
+ if (this.props.texts) {
659
+ this.changeTexts(this.props.texts);
660
+ }
661
+
662
+ this.addGroups(this.props.groups);
663
+ this.assertValueType();
664
+ this.buildAllOptions();
665
+
666
+ this.buildSelectedOptions();
667
+ this.checkAutoDisabled();
479
668
  }
480
669
 
481
- /* }}} */
482
670
  /* {{{ methods */
483
671
  /* {{{ public methods */
484
672
 
@@ -524,7 +712,7 @@ export default class SelecticStore extends Vue<Props> {
524
712
  if (typeof closePreviousSelectic === 'function') {
525
713
  closePreviousSelectic();
526
714
  }
527
- if (!this.keepOpenWithOtherSelectic) {
715
+ if (!this.props.keepOpenWithOtherSelectic) {
528
716
  closePreviousSelectic = this.closeSelectic;
529
717
  }
530
718
  }
@@ -543,17 +731,28 @@ export default class SelecticStore extends Vue<Props> {
543
731
  break;
544
732
  case 'disabled':
545
733
  if (value) {
734
+ this.setAutomaticClose();
546
735
  this.commit('isOpen', false);
547
736
  }
548
737
  break;
549
738
  }
550
739
  }
551
740
 
741
+ public setAutomaticChange() {
742
+ this.state.status.automaticChange = true;
743
+ setTimeout(() => this.state.status.automaticChange = false, 0);
744
+ }
745
+
746
+ public setAutomaticClose() {
747
+ this.state.status.automaticClose = true;
748
+ setTimeout(() => this.state.status.automaticClose = false, 0);
749
+ }
750
+
552
751
  public getItem(id: OptionId): OptionValue {
553
752
  let item: OptionValue;
554
753
 
555
754
  if (this.hasItemInStore(id)) {
556
- item = this.cacheItem.get(id) as OptionValue;
755
+ item = this.data.cacheItem.get(id) as OptionValue;
557
756
  } else {
558
757
  this.getItems([id]);
559
758
  item = {
@@ -567,8 +766,9 @@ export default class SelecticStore extends Vue<Props> {
567
766
 
568
767
  public async getItems(ids: OptionId[]): Promise<OptionItem[]> {
569
768
  const itemsToFetch: OptionId[] = ids.filter((id) => !this.hasItemInStore(id));
769
+ const getItemsCallback = this.props.getItemsCallback;
570
770
 
571
- if (itemsToFetch.length && typeof this.getItemsCallback === 'function') {
771
+ if (itemsToFetch.length && typeof getItemsCallback === 'function') {
572
772
  const cacheRequest = this.cacheRequest;
573
773
  const requestId = itemsToFetch.toString();
574
774
  let promise: Promise<OptionValue[]>;
@@ -576,7 +776,7 @@ export default class SelecticStore extends Vue<Props> {
576
776
  if (cacheRequest.has(requestId)) {
577
777
  promise = cacheRequest.get(requestId)!;
578
778
  } else {
579
- promise = this.getItemsCallback(itemsToFetch);
779
+ promise = getItemsCallback(itemsToFetch);
580
780
  cacheRequest.set(requestId, promise);
581
781
  promise.then(() => {
582
782
  cacheRequest.delete(requestId);
@@ -584,10 +784,11 @@ export default class SelecticStore extends Vue<Props> {
584
784
  }
585
785
 
586
786
  const items = await promise;
787
+ const cacheItem = this.data.cacheItem;
587
788
 
588
789
  for (const item of items) {
589
790
  if (item) {
590
- this.cacheItem.set(item.id, item);
791
+ cacheItem.set(item.id, item);
591
792
  }
592
793
  }
593
794
  }
@@ -598,9 +799,10 @@ export default class SelecticStore extends Vue<Props> {
598
799
  public selectItem(id: OptionId, selected?: boolean, keepOpen = false) {
599
800
  const state = this.state;
600
801
  let hasChanged = false;
802
+ const isPartial = unref(this.isPartial);
601
803
 
602
804
  /* Check that item is not disabled */
603
- if (!this.isPartial) {
805
+ if (!isPartial) {
604
806
  const item = state.allOptions.find((opt) => opt.id === id);
605
807
  if (item && item.disabled) {
606
808
  return;
@@ -659,6 +861,10 @@ export default class SelecticStore extends Vue<Props> {
659
861
  return;
660
862
  }
661
863
 
864
+ if (keepOpen) {
865
+ /* if keepOpen is true it means that it is an automatic change */
866
+ this.setAutomaticChange();
867
+ }
662
868
  this.commit('internalValue', id);
663
869
  hasChanged = true;
664
870
  }
@@ -672,15 +878,18 @@ export default class SelecticStore extends Vue<Props> {
672
878
  if (!this.state.multiple) {
673
879
  return;
674
880
  }
881
+ const hasAllItems = unref(this.hasAllItems);
882
+
883
+ if (!hasAllItems) {
884
+ const labels = this.data.labels;
675
885
 
676
- if (!this.hasAllItems) {
677
886
  if (this.state.searchText) {
678
- this.state.status.errorMessage = this.labels.cannotSelectAllSearchedItems;
887
+ this.state.status.errorMessage = labels.cannotSelectAllSearchedItems;
679
888
  return;
680
889
  }
681
890
 
682
891
  if (!this.state.allowRevert) {
683
- this.state.status.errorMessage = this.labels.cannotSelectAllRevertItems;
892
+ this.state.status.errorMessage = labels.cannotSelectAllRevertItems;
684
893
  return;
685
894
  }
686
895
 
@@ -695,9 +904,9 @@ export default class SelecticStore extends Vue<Props> {
695
904
 
696
905
  const selectAll = !this.state.status.areAllSelected;
697
906
  this.state.status.areAllSelected = selectAll;
698
- this.doNotUpdate = true;
907
+ this.data.doNotUpdate = true;
699
908
  this.state.filteredOptions.forEach((item) => this.selectItem(item.id, selectAll));
700
- this.doNotUpdate = false;
909
+ this.data.doNotUpdate = false;
701
910
  this.updateFilteredOptions();
702
911
  }
703
912
 
@@ -710,9 +919,10 @@ export default class SelecticStore extends Vue<Props> {
710
919
  }
711
920
 
712
921
  public clearCache(forceReset = false) {
713
- const total = this.isPartial ? Infinity : 0;
922
+ const isPartial = unref(this.isPartial);
923
+ const total = isPartial ? Infinity : 0;
714
924
 
715
- this.cacheItem.clear();
925
+ this.data.cacheItem.clear();
716
926
 
717
927
  this.state.allOptions = [];
718
928
  this.state.totalAllOptions = total;
@@ -746,7 +956,7 @@ export default class SelecticStore extends Vue<Props> {
746
956
  }
747
957
 
748
958
  public changeTexts(texts: PartialMessages) {
749
- this.labels = Object.assign({}, this.labels, texts);
959
+ this.data.labels = Object.assign({}, this.data.labels, texts);
750
960
  }
751
961
 
752
962
  /* }}} */
@@ -767,25 +977,53 @@ export default class SelecticStore extends Vue<Props> {
767
977
 
768
978
  return this.state.filteredOptions.find(findId) ||
769
979
  this.state.dynOptions.find(findId) ||
770
- this.getListOptions().find(findId) ||
771
- this.getElementOptions().find(findId);
980
+ unref(this.listOptions).find(findId) ||
981
+ unref(this.elementOptions).find(findId);
772
982
  }
773
983
 
774
- private assertCorrectValue(forceStrict = false) {
984
+ private convertTypeValue(oldValue: OptionId | StrictOptionId[]) {
775
985
  const state = this.state;
986
+ const isMultiple = state.multiple;
987
+ let newValue = oldValue;
988
+
989
+ if (isMultiple) {
990
+ if (!Array.isArray(oldValue)) {
991
+ newValue = oldValue === null ? [] : [oldValue];
992
+ }
993
+ } else {
994
+ if (Array.isArray(oldValue)) {
995
+ const value = oldValue[0];
996
+ newValue = typeof value === 'undefined' ? null : value;
997
+ }
998
+ }
999
+ return newValue;
1000
+ }
1001
+
1002
+ private assertValueType() {
1003
+ const state = this.state;
1004
+ const internalValue = state.internalValue;
1005
+ const newValue = this.convertTypeValue(internalValue);
1006
+
1007
+ if (newValue !== internalValue) {
1008
+ this.setAutomaticChange();
1009
+ state.internalValue = newValue;
1010
+ }
1011
+ }
1012
+
1013
+ private assertCorrectValue(applyStrict = false) {
1014
+ const state = this.state;
1015
+ this.assertValueType();
776
1016
  const internalValue = state.internalValue;
777
1017
  const selectionIsExcluded = state.selectionIsExcluded;
778
1018
  const isMultiple = state.multiple;
779
1019
  const checkStrict = state.strictValue;
780
1020
  let newValue = internalValue;
781
- const isPartial = this.isPartial;
1021
+ const isPartial = unref(this.isPartial);
782
1022
 
783
1023
  if (isMultiple) {
784
- if (!Array.isArray(internalValue)) {
785
- newValue = internalValue === null ? [] : [internalValue];
786
- }
1024
+ const hasFetchedAllItems = unref(this.hasFetchedAllItems);
787
1025
 
788
- if (selectionIsExcluded && this.hasFetchedAllItems) {
1026
+ if (selectionIsExcluded && hasFetchedAllItems) {
789
1027
  newValue = state.allOptions.reduce((values, option) => {
790
1028
  const id = option.id as StrictOptionId;
791
1029
 
@@ -798,11 +1036,6 @@ export default class SelecticStore extends Vue<Props> {
798
1036
  state.selectionIsExcluded = false;
799
1037
  }
800
1038
  } else {
801
- if (Array.isArray(internalValue)) {
802
- const value = internalValue[0];
803
- newValue = typeof value === 'undefined' ? null : value;
804
- }
805
-
806
1039
  state.selectionIsExcluded = false;
807
1040
  }
808
1041
 
@@ -815,22 +1048,25 @@ export default class SelecticStore extends Vue<Props> {
815
1048
  .filter((value) => this.hasItemInStore(value));
816
1049
  isDifferent = filteredValue.length !== (newValue as StrictOptionId[]).length;
817
1050
 
818
- if (isDifferent && isPartial && !forceStrict) {
819
- this.getItems(newValue as StrictOptionId[]).then(() => this.assertCorrectValue(true));
1051
+ if (isDifferent && isPartial && !applyStrict) {
1052
+ this.getItems(newValue as StrictOptionId[])
1053
+ .then(() => this.assertCorrectValue(true));
820
1054
  return;
821
1055
  }
822
1056
  } else
823
- if (!this.hasItemInStore(newValue as OptionId)) {
1057
+ if (newValue !== null && !this.hasItemInStore(newValue as OptionId)) {
824
1058
  filteredValue = null;
825
1059
  isDifferent = true;
826
1060
 
827
- if (isPartial && !forceStrict) {
828
- this.getItems([newValue as OptionId]).then(() => this.assertCorrectValue(true));
1061
+ if (isPartial && !applyStrict) {
1062
+ this.getItems([newValue as OptionId])
1063
+ .then(() => this.assertCorrectValue(true));
829
1064
  return;
830
1065
  }
831
1066
  }
832
1067
 
833
1068
  if (isDifferent) {
1069
+ this.setAutomaticChange();
834
1070
  newValue = filteredValue!;
835
1071
  }
836
1072
  }
@@ -843,8 +1079,9 @@ export default class SelecticStore extends Vue<Props> {
843
1079
  }
844
1080
 
845
1081
  private updateFilteredOptions() {
846
- if (!this.doNotUpdate) {
1082
+ if (!this.data.doNotUpdate) {
847
1083
  this.state.filteredOptions = this.buildItems(this.state.filteredOptions);
1084
+ this.buildSelectedOptions();
848
1085
  }
849
1086
  }
850
1087
 
@@ -854,14 +1091,15 @@ export default class SelecticStore extends Vue<Props> {
854
1091
  });
855
1092
  }
856
1093
 
857
- /* XXX: This is not a computed property to avoid consuming more memory */
1094
+ /* This method is for the computed property listOptions */
858
1095
  private getListOptions(): OptionValue[] {
859
- const options = this.options;
1096
+ const options = this.props.options;
860
1097
  const listOptions: OptionValue[] = [];
861
1098
 
862
1099
  if (!Array.isArray(options)) {
863
1100
  return listOptions;
864
1101
  }
1102
+ const state = this.state;
865
1103
 
866
1104
  options.forEach((option) => {
867
1105
  /* manage simple string */
@@ -877,14 +1115,14 @@ export default class SelecticStore extends Vue<Props> {
877
1115
  const subOptions = option.options;
878
1116
 
879
1117
  /* check for groups */
880
- if (group && !this.state.groups.has(group)) {
881
- this.state.groups.set(group, String(group));
1118
+ if (group && !state.groups.has(group)) {
1119
+ state.groups.set(group, String(group));
882
1120
  }
883
1121
 
884
1122
  /* check for sub options */
885
1123
  if (subOptions) {
886
1124
  const groupId = option.id as StrictOptionId;
887
- this.state.groups.set(groupId, option.text);
1125
+ state.groups.set(groupId, option.text);
888
1126
 
889
1127
  subOptions.forEach((subOpt) => {
890
1128
  subOpt.group = groupId;
@@ -899,28 +1137,29 @@ export default class SelecticStore extends Vue<Props> {
899
1137
  return listOptions;
900
1138
  }
901
1139
 
902
- /* XXX: This is not a computed property to avoid consuming more memory */
1140
+ /* This method is for the computed property elementOptions */
903
1141
  private getElementOptions(): OptionValue[] {
904
- const options = this.childOptions;
1142
+ const options = this.props.childOptions;
905
1143
  const childOptions: OptionValue[] = [];
906
1144
 
907
- if (!Array.isArray(options)) {
1145
+ if (!Array.isArray(options) || options.length === 0) {
908
1146
  return childOptions;
909
1147
  }
1148
+ const state = this.state;
910
1149
 
911
1150
  options.forEach((option) => {
912
1151
  const group = option.group;
913
1152
  const subOptions = option.options;
914
1153
 
915
1154
  /* check for groups */
916
- if (group && !this.state.groups.has(group)) {
917
- this.state.groups.set(group, String(group));
1155
+ if (group && !state.groups.has(group)) {
1156
+ state.groups.set(group, String(group));
918
1157
  }
919
1158
 
920
1159
  /* check for sub options */
921
1160
  if (subOptions) {
922
1161
  const groupId = option.id as StrictOptionId;
923
- this.state.groups.set(groupId, option.text);
1162
+ state.groups.set(groupId, option.text);
924
1163
 
925
1164
  const sOpts: OptionValue[] = subOptions.map((subOpt) => {
926
1165
  return Object.assign({}, subOpt, {
@@ -943,6 +1182,7 @@ export default class SelecticStore extends Vue<Props> {
943
1182
  let elementOptions: OptionValue[] = [];
944
1183
  const optionBehaviorOrder = this.state.optionBehaviorOrder;
945
1184
  let length: number = Infinity;
1185
+ const isPartial = unref(this.isPartial);
946
1186
 
947
1187
  const arrayFromOrder = (orderValue: OptionBehaviorOrder): OptionValue[] => {
948
1188
  switch(orderValue) {
@@ -963,7 +1203,7 @@ export default class SelecticStore extends Vue<Props> {
963
1203
  };
964
1204
 
965
1205
  if (!keepFetched) {
966
- if (this.isPartial) {
1206
+ if (isPartial) {
967
1207
  this.state.totalAllOptions = Infinity;
968
1208
  this.state.totalDynOptions = Infinity;
969
1209
  } else {
@@ -971,15 +1211,15 @@ export default class SelecticStore extends Vue<Props> {
971
1211
  }
972
1212
  }
973
1213
 
974
- listOptions = this.getListOptions();
975
- elementOptions = this.getElementOptions();
1214
+ listOptions = unref(this.listOptions);
1215
+ elementOptions = unref(this.elementOptions);
976
1216
 
977
1217
  if (this.state.optionBehaviorOperation === 'force') {
978
1218
  const orderValue = optionBehaviorOrder.find((value) => lengthFromOrder(value) > 0)!;
979
1219
  allOptions.push(...arrayFromOrder(orderValue));
980
1220
  length = lengthFromOrder(orderValue);
981
- this.activeOrder = orderValue;
982
- this.dynOffset = 0;
1221
+ this.data.activeOrder = orderValue;
1222
+ this.data.dynOffset = 0;
983
1223
  } else {
984
1224
  /* sort */
985
1225
  let offset = 0;
@@ -988,7 +1228,7 @@ export default class SelecticStore extends Vue<Props> {
988
1228
  const lngth = lengthFromOrder(orderValue);
989
1229
 
990
1230
  if (orderValue === 'D') {
991
- this.dynOffset = offset;
1231
+ this.data.dynOffset = offset;
992
1232
  } else {
993
1233
  offset += lngth;
994
1234
  }
@@ -999,7 +1239,7 @@ export default class SelecticStore extends Vue<Props> {
999
1239
  break;
1000
1240
  }
1001
1241
  }
1002
- this.activeOrder = 'D';
1242
+ this.data.activeOrder = 'D';
1003
1243
  length = optionBehaviorOrder.reduce((total, orderValue) => total + lengthFromOrder(orderValue), 0);
1004
1244
  }
1005
1245
 
@@ -1008,7 +1248,7 @@ export default class SelecticStore extends Vue<Props> {
1008
1248
  if (keepFetched) {
1009
1249
  this.state.totalAllOptions = length;
1010
1250
  } else {
1011
- if (!this.isPartial) {
1251
+ if (!isPartial) {
1012
1252
  this.state.totalAllOptions = allOptions.length;
1013
1253
  }
1014
1254
  }
@@ -1016,7 +1256,10 @@ export default class SelecticStore extends Vue<Props> {
1016
1256
  this.state.filteredOptions = [];
1017
1257
  this.state.totalFilteredOptions = Infinity;
1018
1258
 
1019
- this.buildFilteredOptions();
1259
+ this.buildFilteredOptions().then(() => {
1260
+ /* XXX: To recompute for strict mode and auto-select */
1261
+ this.assertCorrectValue();
1262
+ });
1020
1263
  }
1021
1264
 
1022
1265
  private async buildFilteredOptions() {
@@ -1030,14 +1273,16 @@ export default class SelecticStore extends Vue<Props> {
1030
1273
  const totalAllOptions = this.state.totalAllOptions;
1031
1274
  const allOptionsLength = allOptions.length;
1032
1275
  let filteredOptionsLength = this.state.filteredOptions.length;
1276
+ const hasAllItems = unref(this.hasAllItems);
1033
1277
 
1034
- if (this.hasAllItems) {
1278
+ if (hasAllItems) {
1035
1279
  /* Everything has already been fetched and stored in filteredOptions */
1036
1280
  return;
1037
1281
  }
1038
1282
 
1283
+ const hasFetchedAllItems = unref(this.hasFetchedAllItems);
1039
1284
  /* Check if all options have been fetched */
1040
- if (this.hasFetchedAllItems) {
1285
+ if (hasFetchedAllItems) {
1041
1286
  if (!search) {
1042
1287
  this.state.filteredOptions = this.buildGroupItems(allOptions);
1043
1288
  this.state.totalFilteredOptions = this.state.filteredOptions.length;
@@ -1053,7 +1298,7 @@ export default class SelecticStore extends Vue<Props> {
1053
1298
  /* When we only have partial options */
1054
1299
 
1055
1300
  const offsetItem = this.state.offsetItem;
1056
- const marginSize = this.marginSize;
1301
+ const marginSize = unref(this.marginSize);
1057
1302
  const endIndex = offsetItem + marginSize;
1058
1303
 
1059
1304
  if (endIndex <= filteredOptionsLength) {
@@ -1063,7 +1308,8 @@ export default class SelecticStore extends Vue<Props> {
1063
1308
  if (!search && endIndex <= allOptionsLength) {
1064
1309
  this.state.filteredOptions = this.buildGroupItems(allOptions);
1065
1310
  this.state.totalFilteredOptions = totalAllOptions + this.state.groups.size;
1066
- if (this.isPartial && this.state.totalDynOptions === Infinity) {
1311
+ const isPartial = unref(this.isPartial);
1312
+ if (isPartial && this.state.totalDynOptions === Infinity) {
1067
1313
  this.fetchData();
1068
1314
  }
1069
1315
  return;
@@ -1073,7 +1319,7 @@ export default class SelecticStore extends Vue<Props> {
1073
1319
  this.addStaticFilteredOptions();
1074
1320
 
1075
1321
  filteredOptionsLength = this.state.filteredOptions.length;
1076
- this.dynOffset = filteredOptionsLength;
1322
+ this.data.dynOffset = filteredOptionsLength;
1077
1323
  if (endIndex <= filteredOptionsLength) {
1078
1324
  return;
1079
1325
  }
@@ -1084,66 +1330,71 @@ export default class SelecticStore extends Vue<Props> {
1084
1330
 
1085
1331
  private async buildSelectedOptions() {
1086
1332
  const internalValue = this.state.internalValue;
1333
+ const state = this.state;
1087
1334
 
1088
- if (this.state.multiple) {
1335
+ if (state.multiple) {
1089
1336
  /* display partial information about selected items */
1090
- this.state.selectedOptions = this.buildSelectedItems(internalValue as StrictOptionId[]);
1337
+ state.selectedOptions = this.buildSelectedItems(internalValue as StrictOptionId[]);
1091
1338
 
1092
1339
  const items: OptionItem[] = await this.getItems(internalValue as StrictOptionId[]).catch(() => []);
1093
- if (internalValue !== this.state.internalValue) {
1340
+ if (internalValue !== state.internalValue) {
1094
1341
  /* Values have been deprecated */
1095
1342
  return;
1096
1343
  }
1097
1344
 
1098
1345
  if (items.length !== (internalValue as StrictOptionId[]).length) {
1099
- if (!this.state.strictValue) {
1100
- const updatedItems = this.state.selectedOptions.map((option) => {
1346
+ if (!state.strictValue) {
1347
+ const updatedItems = state.selectedOptions.map((option) => {
1101
1348
  const foundItem = items.find((item) => item.id === option.id);
1102
1349
 
1103
1350
  return foundItem || option;
1104
1351
  });
1105
1352
 
1106
- this.state.selectedOptions = updatedItems;
1353
+ state.selectedOptions = updatedItems;
1107
1354
  } else {
1108
1355
  const itemIds = items.map((item) => item.id as StrictOptionId) ;
1109
1356
 
1357
+ this.setAutomaticChange();
1110
1358
  this.commit('internalValue', itemIds);
1111
1359
  }
1112
1360
  return;
1113
1361
  }
1114
1362
 
1115
1363
  /* display full information about selected items */
1116
- this.state.selectedOptions = items;
1364
+ state.selectedOptions = items;
1117
1365
  } else
1118
1366
  if (internalValue === null) {
1119
- this.state.selectedOptions = null;
1367
+ state.selectedOptions = null;
1120
1368
  } else {
1121
1369
  /* display partial information about selected items */
1122
- this.state.selectedOptions = this.buildSelectedItems([internalValue as OptionId])[0];
1370
+ state.selectedOptions = this.buildSelectedItems([internalValue as OptionId])[0];
1123
1371
 
1124
1372
  const items = await this.getItems([internalValue as OptionId]).catch(() => []);
1125
- if (internalValue !== this.state.internalValue) {
1373
+ if (internalValue !== state.internalValue) {
1126
1374
  /* Values have been deprecated */
1127
1375
  return;
1128
1376
  }
1129
1377
 
1130
1378
  if (!items.length) {
1131
- if (this.state.strictValue) {
1379
+ if (state.strictValue) {
1380
+ this.setAutomaticChange();
1132
1381
  this.commit('internalValue', null);
1133
1382
  }
1134
1383
  return;
1135
1384
  }
1136
1385
 
1137
1386
  /* display full information about selected items */
1138
- this.state.selectedOptions = items[0];
1387
+ state.selectedOptions = items[0];
1139
1388
  }
1140
1389
  }
1141
1390
 
1142
1391
  private async fetchData() {
1143
1392
  const state = this.state;
1393
+ const labels = this.data.labels;
1394
+ const fetchCallback = this.props.fetchCallback;
1144
1395
 
1145
- if (!this.fetchCallback) {
1146
- state.status.errorMessage = this.labels.noFetchMethod;
1396
+ if (!fetchCallback) {
1397
+ state.status.errorMessage = labels.noFetchMethod;
1147
1398
  return;
1148
1399
  }
1149
1400
 
@@ -1151,25 +1402,26 @@ export default class SelecticStore extends Vue<Props> {
1151
1402
  const filteredOptionsLength = state.filteredOptions.length;
1152
1403
  const offsetItem = state.offsetItem;
1153
1404
  const pageSize = state.pageSize;
1154
- const marginSize = this.marginSize;
1405
+ const marginSize = unref(this.marginSize);
1155
1406
  const endIndex = offsetItem + marginSize;
1407
+ const dynOffset = this.data.dynOffset;
1156
1408
 
1157
1409
  /* Run the query */
1158
1410
  this.state.status.searching = true;
1159
1411
 
1160
1412
  /* Manage cases where offsetItem is not equal to the last item received */
1161
- const offset = filteredOptionsLength - this.nbGroups(state.filteredOptions) - this.dynOffset;
1413
+ const offset = filteredOptionsLength - this.nbGroups(state.filteredOptions) - dynOffset;
1162
1414
  const nbItems = endIndex - offset;
1163
1415
  const limit = Math.ceil(nbItems / pageSize) * pageSize;
1164
1416
 
1165
1417
  try {
1166
1418
  const requestId = ++this.requestId;
1167
- const {total: rTotal, result} = await this.fetchCallback(search, offset, limit);
1419
+ const {total: rTotal, result} = await fetchCallback(search, offset, limit);
1168
1420
  let total = rTotal;
1169
1421
 
1170
1422
  /* Assert result is correctly formatted */
1171
1423
  if (!Array.isArray(result)) {
1172
- throw new Error(this.labels.wrongFormattedData);
1424
+ throw new Error(labels.wrongFormattedData);
1173
1425
  }
1174
1426
 
1175
1427
  /* Handle case where total is not returned */
@@ -1186,7 +1438,7 @@ export default class SelecticStore extends Vue<Props> {
1186
1438
  /* update cache */
1187
1439
  state.totalDynOptions = total;
1188
1440
  state.dynOptions.splice(offset, limit, ...result);
1189
- this.$nextTick(() => this.buildAllOptions(true));
1441
+ setTimeout(() => this.buildAllOptions(true), 0);
1190
1442
  }
1191
1443
 
1192
1444
  /* Check request is not obsolete */
@@ -1201,7 +1453,7 @@ export default class SelecticStore extends Vue<Props> {
1201
1453
  const options = this.buildGroupItems(result, previousItem);
1202
1454
  const nbGroups1 = this.nbGroups(options);
1203
1455
 
1204
- state.filteredOptions.splice(offset + this.dynOffset, limit + nbGroups1, ...options);
1456
+ state.filteredOptions.splice(offset + dynOffset, limit + nbGroups1, ...options);
1205
1457
  }
1206
1458
 
1207
1459
  let nbGroups = state.groups.size;
@@ -1209,7 +1461,7 @@ export default class SelecticStore extends Vue<Props> {
1209
1461
  nbGroups = this.nbGroups(state.filteredOptions);
1210
1462
  }
1211
1463
 
1212
- state.totalFilteredOptions = total + nbGroups + this.dynOffset;
1464
+ state.totalFilteredOptions = total + nbGroups + dynOffset;
1213
1465
 
1214
1466
  if (search && state.totalFilteredOptions <= state.filteredOptions.length) {
1215
1467
  this.addStaticFilteredOptions(true);
@@ -1217,7 +1469,7 @@ export default class SelecticStore extends Vue<Props> {
1217
1469
 
1218
1470
  state.status.errorMessage = '';
1219
1471
  } catch (e) {
1220
- state.status.errorMessage = e.message;
1472
+ state.status.errorMessage = (e as Error).message;
1221
1473
  if (!search) {
1222
1474
  state.totalDynOptions = 0;
1223
1475
  this.buildAllOptions(true);
@@ -1261,10 +1513,10 @@ export default class SelecticStore extends Vue<Props> {
1261
1513
 
1262
1514
  switch (order) {
1263
1515
  case 'O':
1264
- options = this.filterOptions(this.getListOptions(), search);
1516
+ options = this.filterOptions(unref(this.listOptions), search);
1265
1517
  break;
1266
1518
  case 'E':
1267
- options = this.filterOptions(this.getElementOptions(), search);
1519
+ options = this.filterOptions(unref(this.elementOptions), search);
1268
1520
  break;
1269
1521
  }
1270
1522
  this.state.filteredOptions.push(...options);
@@ -1274,7 +1526,8 @@ export default class SelecticStore extends Vue<Props> {
1274
1526
 
1275
1527
  private buildSelectedItems(ids: OptionId[]): OptionItem[] {
1276
1528
  return this.buildItems(ids.map((id) => {
1277
- const item = this.cacheItem.get(id);
1529
+ const cacheItem = this.data.cacheItem;
1530
+ const item = cacheItem.get(id);
1278
1531
 
1279
1532
  return item || {
1280
1533
  id,
@@ -1284,13 +1537,14 @@ export default class SelecticStore extends Vue<Props> {
1284
1537
  }
1285
1538
 
1286
1539
  private hasItemInStore(id: OptionId): boolean {
1287
- let item: OptionValue | undefined = this.cacheItem.get(id);
1540
+ const cacheItem = this.data.cacheItem;
1541
+ let item: OptionValue | undefined = cacheItem.get(id);
1288
1542
 
1289
1543
  if (!item) {
1290
1544
  item = this.getValue(id);
1291
1545
 
1292
1546
  if (item) {
1293
- this.cacheItem.set(item.id, item);
1547
+ cacheItem.set(item.id, item);
1294
1548
  }
1295
1549
  }
1296
1550
 
@@ -1311,7 +1565,7 @@ export default class SelecticStore extends Vue<Props> {
1311
1565
  disabled: false,
1312
1566
  isGroup: false,
1313
1567
  }, option, {
1314
- // tslint:disable-next-line:no-bitwise
1568
+ /* eslint-disable-next-line no-bitwise */
1315
1569
  selected: !!(+selected.includes(id) ^ selectionIsExcluded),
1316
1570
  });
1317
1571
  });
@@ -1355,7 +1609,8 @@ export default class SelecticStore extends Vue<Props> {
1355
1609
  isValid = isValid && /^[ODE]+$/.test(order);
1356
1610
 
1357
1611
  if (!isValid) {
1358
- this.state.status.errorMessage = this.labels.unknownPropertyValue.replace(/%s/, 'optionBehavior');
1612
+ const labels = this.data.labels;
1613
+ this.state.status.errorMessage = labels.unknownPropertyValue.replace(/%s/, 'optionBehavior');
1359
1614
  operation = 'sort';
1360
1615
  orderArray = ['O', 'D', 'E'];
1361
1616
  } else {
@@ -1387,6 +1642,7 @@ export default class SelecticStore extends Vue<Props> {
1387
1642
  for (const option of options) {
1388
1643
  if (!option.disabled) {
1389
1644
  this.selectItem(option.id, true, true);
1645
+ this.checkAutoDisabled();
1390
1646
  return;
1391
1647
  }
1392
1648
  }
@@ -1394,9 +1650,11 @@ export default class SelecticStore extends Vue<Props> {
1394
1650
 
1395
1651
  private checkAutoDisabled() {
1396
1652
  const state = this.state;
1397
- const doNotCheck = this.disabled || this.isPartial || !state.autoDisabled;
1653
+ const isPartial = unref(this.isPartial);
1654
+ const doNotCheck = isPartial || this.props.disabled || !state.autoDisabled;
1655
+ const hasFetchedAllItems = unref(this.hasFetchedAllItems);
1398
1656
 
1399
- if (doNotCheck || !this.hasFetchedAllItems) {
1657
+ if (doNotCheck || !hasFetchedAllItems) {
1400
1658
  return;
1401
1659
  }
1402
1660
 
@@ -1412,7 +1670,10 @@ export default class SelecticStore extends Vue<Props> {
1412
1670
  const hasOnlyOneOption = nb === 1 && hasValidValue && !state.allowClearSelection;
1413
1671
 
1414
1672
  if (hasOnlyOneOption || isEmpty) {
1415
- this.commit('isOpen', false);
1673
+ if (state.isOpen) {
1674
+ this.setAutomaticClose();
1675
+ this.commit('isOpen', false);
1676
+ }
1416
1677
  this.commit('disabled', true);
1417
1678
  } else {
1418
1679
  this.commit('disabled', false);
@@ -1420,128 +1681,22 @@ export default class SelecticStore extends Vue<Props> {
1420
1681
  }
1421
1682
 
1422
1683
  private checkHideFilter() {
1423
- if (this.params && this.params.hideFilter !== 'auto') {
1684
+ const params = this.props.params;
1685
+ if (params && params.hideFilter !== 'auto') {
1424
1686
  return;
1425
1687
  }
1426
1688
 
1427
1689
  const state = this.state;
1690
+ const isPartial = unref(this.isPartial);
1428
1691
 
1429
- if (state.multiple || this.isPartial) {
1692
+ if (state.multiple || isPartial) {
1430
1693
  state.hideFilter = false;
1431
1694
  } else {
1432
- state.hideFilter = state.totalAllOptions <= this.itemsPerPage;
1695
+ state.hideFilter = state.totalAllOptions <= this.data.itemsPerPage;
1433
1696
  }
1434
1697
  }
1435
1698
 
1436
1699
  /* }}} */
1437
1700
  /* }}} */
1438
- /* {{{ watch */
1439
-
1440
- @Watch('options')
1441
- protected onOptionsChange(options: OptionValue[] = [], oldOptions: OptionValue[] = []) {
1442
- if (JSON.stringify(options) === JSON.stringify(oldOptions)) {
1443
- /* There is no real difference, only a change of reference */
1444
- return;
1445
- }
1446
- this.cacheItem.clear();
1447
- this.commit('isOpen', false);
1448
- this.buildAllOptions(true);
1449
- this.assertCorrectValue();
1450
- this.buildSelectedOptions();
1451
- }
1452
-
1453
- @Watch('childOptions')
1454
- protected onChildOptionsChange(childOptions: OptionValue[] = [], oldChildOptions: OptionValue[] = []) {
1455
- if (JSON.stringify(childOptions) === JSON.stringify(oldChildOptions)) {
1456
- /* There is no real difference, only a change of reference */
1457
- return;
1458
- }
1459
- this.cacheItem.clear();
1460
- this.commit('isOpen', false);
1461
- this.buildAllOptions(true);
1462
- this.assertCorrectValue();
1463
- this.buildSelectedOptions();
1464
- }
1465
-
1466
- @Watch('value')
1467
- protected onValueChange() {
1468
- const value = typeof this.value === 'undefined' ? null : this.value;
1469
- this.commit('internalValue', value);
1470
- }
1471
-
1472
- @Watch('selectionIsExcluded')
1473
- protected onSelectionExcludedChange() {
1474
- this.commit('selectionIsExcluded', this.selectionIsExcluded);
1475
- }
1476
-
1477
- @Watch('disabled')
1478
- protected onDisabledChange() {
1479
- this.commit('disabled', this.disabled);
1480
- }
1481
-
1482
- @Watch('state.filteredOptions')
1483
- protected onFilteredChange() {
1484
- let areAllSelected = false;
1485
-
1486
- if (this.hasAllItems) {
1487
- const selectionIsExcluded = +this.state.selectionIsExcluded;
1488
- // tslint:disable-next-line:no-bitwise
1489
- areAllSelected = this.state.filteredOptions.every((item) => !!(+item.selected ^ selectionIsExcluded));
1490
- }
1491
-
1492
- this.state.status.areAllSelected = areAllSelected;
1493
- }
1494
-
1495
- @Watch('state.internalValue')
1496
- protected onInternalValueChange() {
1497
- this.buildSelectedOptions();
1498
- }
1499
-
1500
- @Watch('state.allOptions')
1501
- protected onAllOptionChange() {
1502
- this.checkAutoSelect();
1503
- this.checkAutoDisabled();
1504
- }
1505
-
1506
- @Watch('state.totalAllOptions')
1507
- protected onTotalAllOptionsChange() {
1508
- this.checkHideFilter();
1509
- }
1510
-
1511
- /* }}} */
1512
- /* {{{ life cycles methods */
1513
-
1514
- protected created() {
1515
- const value = typeof this.value === 'undefined' ? null : this.value;
1516
-
1517
- /* set initial value for non reactive attribute */
1518
- this.requestId = 0;
1519
- this.cacheRequest = new Map();
1520
-
1521
- const stateParam = Object.assign({}, this.params);
1522
1701
 
1523
- if (stateParam.optionBehavior) {
1524
- this.buildOptionBehavior(stateParam.optionBehavior, stateParam as SelecticStoreState);
1525
- delete stateParam.optionBehavior;
1526
- }
1527
-
1528
- this.state = Object.assign(this.state, stateParam, {
1529
- internalValue: value,
1530
- selectionIsExcluded: this.selectionIsExcluded,
1531
- disabled: this.disabled,
1532
- });
1533
-
1534
- this.checkHideFilter();
1535
-
1536
- if (this.texts) {
1537
- this.changeTexts(this.texts);
1538
- }
1539
-
1540
- this.addGroups(this.groups);
1541
- this.buildAllOptions();
1542
- this.assertCorrectValue();
1543
- this.buildSelectedOptions();
1544
- }
1545
-
1546
- /* }}} */
1547
- }
1702
+ };