selectic 3.0.1 → 3.0.5

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/index.tsx CHANGED
@@ -18,7 +18,7 @@
18
18
  * close [component]: triggered when the list closes.
19
19
  */
20
20
 
21
- import {Vue, Component, Prop, Watch, h} from 'vtyx';
21
+ import {Vue, Component, Emit, Prop, Watch, h} from 'vtyx';
22
22
  import './css/selectic.css';
23
23
 
24
24
  import Store, {
@@ -36,6 +36,7 @@ import Store, {
36
36
  FormatCallback,
37
37
  SelectionOverflow,
38
38
  ListPosition,
39
+ HideFilter,
39
40
  } from './Store';
40
41
  import MainInput from './MainInput';
41
42
  import ExtendedList from './ExtendedList';
@@ -55,8 +56,21 @@ export {
55
56
  FormatCallback,
56
57
  SelectionOverflow,
57
58
  ListPosition,
59
+ HideFilter,
58
60
  };
59
61
 
62
+ type EventType = 'input' | 'change' | 'open' | 'close' | 'focus' | 'blur' | 'item:click';
63
+
64
+ export interface EventOptions {
65
+ instance: Selectic;
66
+ eventType: EventType;
67
+ automatic: boolean;
68
+ }
69
+
70
+ export interface EventChangeOptions extends EventOptions {
71
+ isExcluded: boolean;
72
+ }
73
+
60
74
  export interface ParamProps {
61
75
  /* Method to call to fetch extra data */
62
76
  fetchCallback?: FetchCallback;
@@ -71,7 +85,7 @@ export interface ParamProps {
71
85
  pageSize?: number;
72
86
 
73
87
  /* Hide the search control */
74
- hideFilter?: boolean | 'auto';
88
+ hideFilter?: HideFilter;
75
89
 
76
90
  /* Allow to reverse selection.
77
91
  * If true, parent should support the selectionIsExcluded property.
@@ -295,8 +309,6 @@ export default class Selectic extends Vue<Props> {
295
309
 
296
310
  get outsideListener() {
297
311
  return (evt: MouseEvent) => {
298
- const target = evt.target as Node;
299
-
300
312
  if (!this.$refs) {
301
313
  /* this component should have been destroyed */
302
314
  this.removeListeners();
@@ -304,8 +316,23 @@ export default class Selectic extends Vue<Props> {
304
316
  return;
305
317
  }
306
318
 
307
- if (!this.$refs.extendedList.$el.contains(target) && !this.$el.contains(target)) {
308
- this.store.commit('isOpen', false);
319
+ const store = this.store;
320
+ const keepOpenWithOtherSelectic = this.params.keepOpenWithOtherSelectic;
321
+ const extendedList = this.$refs.extendedList;
322
+
323
+ if (!extendedList) {
324
+ /* this component is not focused anymore */
325
+ if (!keepOpenWithOtherSelectic) {
326
+ this.removeListeners();
327
+ this.store.commit('isOpen', false);
328
+ }
329
+ return;
330
+ }
331
+
332
+ const target = evt.target as Node;
333
+
334
+ if (!extendedList.$el.contains(target) && !this.$el.contains(target)) {
335
+ store.commit('isOpen', false);
309
336
  }
310
337
  };
311
338
  }
@@ -483,14 +510,14 @@ export default class Selectic extends Vue<Props> {
483
510
  window.addEventListener('resize', this.windowResize, false);
484
511
  document.addEventListener('click', this.outsideListener, true);
485
512
  this.computeOffset();
486
- this.$emit('open', this);
513
+ this.emit('open');
487
514
  } else {
488
515
  this.removeListeners();
489
516
  if (state.status.hasChanged) {
490
517
  this.$emit('change', this.getValue(), state.selectionIsExcluded, this);
491
518
  this.store.resetChange();
492
519
  }
493
- this.$emit('close', this);
520
+ this.emit('close');
494
521
  }
495
522
  }
496
523
 
@@ -580,10 +607,10 @@ export default class Selectic extends Vue<Props> {
580
607
  if (canTrigger) {
581
608
  const selectionIsExcluded = this.store.state.selectionIsExcluded;
582
609
 
583
- this.$emit('input', value, selectionIsExcluded, this);
610
+ this.emit('input', value, selectionIsExcluded);
584
611
 
585
612
  if (!this.isFocused) {
586
- this.$emit('change', value, selectionIsExcluded, this);
613
+ this.emit('change', value, selectionIsExcluded);
587
614
  this.store.resetChange();
588
615
  }
589
616
  }
@@ -613,7 +640,11 @@ export default class Selectic extends Vue<Props> {
613
640
  }, 0);
614
641
  }
615
642
 
616
- private emit(event: string, ...args: any[]) {
643
+ /* This method is only to emit the events and to replicate them */
644
+ private _emit(event: 'input' | 'change', value: SelectedValue, options: EventChangeOptions): void;
645
+ private _emit(event: 'open' | 'close' | 'focus' | 'blur', options: EventOptions): void;
646
+ private _emit(event: 'item:click', value: OptionId, options: EventOptions): void;
647
+ private _emit(event: EventType, ...args: any[]) {
617
648
  this.$emit(event, ...args);
618
649
 
619
650
  if (typeof this._on === 'function') {
@@ -621,6 +652,40 @@ export default class Selectic extends Vue<Props> {
621
652
  }
622
653
  }
623
654
 
655
+ private emit(event: 'input' | 'change', value: SelectedValue, isExcluded: boolean): void;
656
+ private emit(event: 'open' | 'close' | 'focus' | 'blur'): void;
657
+ private emit(event: 'item:click', value: OptionId): void;
658
+ private emit(event: EventType, value?: SelectedValue | OptionId, isExcluded?: boolean) {
659
+ const automatic = this.store.state.status.automaticChange;
660
+ const options: EventOptions = {
661
+ instance: this,
662
+ eventType: event,
663
+ automatic,
664
+ };
665
+ switch (event) {
666
+ case 'input':
667
+ case 'change':
668
+ const changeOptions: EventChangeOptions = Object.assign({
669
+ isExcluded: isExcluded!,
670
+ }, options);
671
+ this._emit(event, value as SelectedValue, changeOptions);
672
+ break;
673
+ case 'open':
674
+ case 'focus':
675
+ this._emit('open', options);
676
+ this._emit('focus', options);
677
+ break;
678
+ case 'close':
679
+ case 'blur':
680
+ this._emit('close', options);
681
+ this._emit('blur', options);
682
+ break;
683
+ case 'item:click':
684
+ this._emit(event, value as OptionId, options);
685
+ break;
686
+ }
687
+ }
688
+
624
689
  // private extractFromNode(node: Vue.VNode, text = ''): OptionValue {
625
690
  // function styleToString(staticStyle?: {[key: string]: string}): string | undefined {
626
691
  // if (!staticStyle) {
@@ -724,8 +789,7 @@ export default class Selectic extends Vue<Props> {
724
789
  params: {
725
790
  multiple: (this.multiple ?? false) !== false,
726
791
  pageSize: this.params.pageSize || 100,
727
- hideFilter: this.params.hideFilter !== undefined
728
- ? this.params.hideFilter : 'auto',
792
+ hideFilter: this.params.hideFilter ?? 'auto',
729
793
  allowRevert: this.params.allowRevert, /* it can be undefined */
730
794
  allowClearSelection: this.params.allowClearSelection || false,
731
795
  autoSelect: this.params.autoSelect === undefined
@@ -793,6 +857,13 @@ export default class Selectic extends Vue<Props> {
793
857
 
794
858
  /* }}} */
795
859
 
860
+ @Emit('input')
861
+ @Emit('change')
862
+ @Emit('open')
863
+ @Emit('focus')
864
+ @Emit('close')
865
+ @Emit('blur')
866
+ @Emit('item:click')
796
867
  public render() {
797
868
  const id = this.id || undefined;
798
869
  const store = this.store;
@@ -825,7 +896,7 @@ export default class Selectic extends Vue<Props> {
825
896
  store={store}
826
897
  id={id}
827
898
  on={{
828
- 'item:click': (id: OptionId) => this.emit('item:click', id, this),
899
+ 'item:click': (id: OptionId) => this.emit('item:click', id),
829
900
  }}
830
901
  ref="mainInput"
831
902
  />
@@ -33,6 +33,9 @@ tape.test('Store creation', (subT) => {
33
33
 
34
34
  t.deepEqual(defaultStore.state, getInitialState({
35
35
  disabled: true,
36
+ status: {
37
+ automaticClose: true,
38
+ },
36
39
  }));
37
40
 
38
41
  const store = new Store({
@@ -62,6 +65,9 @@ tape.test('Store creation', (subT) => {
62
65
  }],
63
66
  autoSelect: false,
64
67
  disabled: true,
68
+ status: {
69
+ automaticClose: true,
70
+ },
65
71
  }));
66
72
 
67
73
  t.end();
@@ -937,6 +943,7 @@ tape.test('Store creation', (subT) => {
937
943
  await sleep(0);
938
944
 
939
945
  t.is(store.state.hideFilter, true);
946
+ t.is(store.state.keepFilterOpen, false);
940
947
  t.end();
941
948
  });
942
949
 
@@ -950,6 +957,7 @@ tape.test('Store creation', (subT) => {
950
957
  await sleep(0);
951
958
 
952
959
  t.is(store.state.hideFilter, false);
960
+ t.is(store.state.keepFilterOpen, false);
953
961
  t.end();
954
962
  });
955
963
 
@@ -988,6 +996,7 @@ tape.test('Store creation', (subT) => {
988
996
  await sleep(0);
989
997
 
990
998
  t.is(store.state.hideFilter, false);
999
+ t.is(store.state.keepFilterOpen, false);
991
1000
  t.end();
992
1001
  });
993
1002
  });
@@ -1003,6 +1012,7 @@ tape.test('Store creation', (subT) => {
1003
1012
  await sleep(0);
1004
1013
 
1005
1014
  t.is(store.state.hideFilter, false);
1015
+ t.is(store.state.keepFilterOpen, false);
1006
1016
  t.end();
1007
1017
  });
1008
1018
 
@@ -1016,17 +1026,20 @@ tape.test('Store creation', (subT) => {
1016
1026
  await sleep(0);
1017
1027
 
1018
1028
  t.is(store.state.hideFilter, false);
1029
+ t.is(store.state.keepFilterOpen, false);
1019
1030
 
1020
1031
  /* Assert it doesn't change after fetching data */
1021
1032
  store.commit('isOpen', true);
1022
1033
  await sleep(0);
1023
1034
 
1024
1035
  t.is(store.state.hideFilter, false);
1036
+ t.is(store.state.keepFilterOpen, false);
1025
1037
 
1026
1038
  store.commit('isOpen', false);
1027
1039
  await sleep(0);
1028
1040
 
1029
1041
  t.is(store.state.hideFilter, false);
1042
+ t.is(store.state.keepFilterOpen, false);
1030
1043
  t.end();
1031
1044
  });
1032
1045
  });
@@ -1083,6 +1096,49 @@ tape.test('Store creation', (subT) => {
1083
1096
  t.end();
1084
1097
  });
1085
1098
  });
1099
+
1100
+ st.test('having value "open"', (sTest) => {
1101
+ sTest.test('should show filter with few options', async (t) => {
1102
+ const store = new Store({
1103
+ options: getOptions(3),
1104
+ params: {
1105
+ hideFilter: 'open',
1106
+ },
1107
+ });
1108
+ await sleep(0);
1109
+
1110
+ t.is(store.state.hideFilter, false);
1111
+ t.is(store.state.keepFilterOpen, true);
1112
+ t.end();
1113
+ });
1114
+
1115
+ sTest.test('should show filter with dynamic options', async (t) => {
1116
+ const store = new Store({
1117
+ fetchCallback: buildFetchCb({ total: 5 }),
1118
+ params: {
1119
+ hideFilter: 'open',
1120
+ },
1121
+ });
1122
+ await sleep(0);
1123
+
1124
+ t.is(store.state.hideFilter, false);
1125
+ t.is(store.state.keepFilterOpen, true);
1126
+
1127
+ /* Assert it doesn't change after fetching data */
1128
+ store.commit('isOpen', true);
1129
+ await sleep(0);
1130
+
1131
+ t.is(store.state.hideFilter, false);
1132
+ t.is(store.state.keepFilterOpen, true);
1133
+
1134
+ store.commit('isOpen', false);
1135
+ await sleep(0);
1136
+
1137
+ t.is(store.state.hideFilter, false);
1138
+ t.is(store.state.keepFilterOpen, true);
1139
+ t.end();
1140
+ });
1141
+ });
1086
1142
  });
1087
1143
 
1088
1144
  subT.test('"optionBehavior" property', (st) => {
@@ -45,6 +45,9 @@ tape.test('commit()', (st) => {
45
45
  hideFilter: true,
46
46
  allowClearSelection: true,
47
47
  disabled: true,
48
+ status: {
49
+ automaticClose: true,
50
+ },
48
51
  }));
49
52
 
50
53
  store.commit('searchText', 'hello2');
@@ -945,15 +948,22 @@ tape.test('commit()', (st) => {
945
948
  t.end();
946
949
  });
947
950
 
948
- sTest.test('should close the select list', (t) => {
951
+ sTest.test('should close the select list', async (t) => {
949
952
  const store = new Store({
950
953
  options: getOptions(5),
951
954
  });
952
955
 
956
+
953
957
  store.commit('isOpen', true);
958
+ await sleep(0);
954
959
  store.commit('disabled', true);
955
960
 
956
961
  t.deepEqual(store.state.isOpen, false);
962
+ t.deepEqual(store.state.status.automaticClose, true);
963
+
964
+ await sleep(10);
965
+ t.deepEqual(store.state.status.automaticClose, false);
966
+
957
967
  t.end();
958
968
  });
959
969
  });
@@ -1080,26 +1090,32 @@ tape.test('commit()', (st) => {
1080
1090
  },
1081
1091
  });
1082
1092
 
1093
+ await sleep(0);
1083
1094
  store1.commit('internalValue', 3);
1084
1095
  store2.commit('internalValue', [1, 2]);
1085
- await Promise.all([_.nextVueTick(store1), _.nextVueTick(store2)]);
1086
1096
 
1087
1097
  t.is(store1.state.internalValue, 3);
1088
1098
  t.deepEqual(store2.state.internalValue, [1, 2]);
1099
+ t.is(store1.state.status.automaticChange, false);
1100
+ t.is(store2.state.status.automaticChange, false);
1089
1101
 
1102
+ await sleep(0);
1090
1103
  store1.commit('internalValue', 1);
1091
1104
  store2.commit('internalValue', [3, 4, 5]);
1092
- await Promise.all([_.nextVueTick(store1), _.nextVueTick(store2)]);
1093
1105
 
1094
1106
  t.is(store1.state.internalValue, 1);
1095
1107
  t.deepEqual(store2.state.internalValue, [3, 4, 5]);
1108
+ t.is(store1.state.status.automaticChange, false);
1109
+ t.is(store2.state.status.automaticChange, false);
1096
1110
 
1111
+ await sleep(0);
1097
1112
  store1.commit('internalValue', null);
1098
1113
  store2.commit('internalValue', []);
1099
- await Promise.all([_.nextVueTick(store1), _.nextVueTick(store2)]);
1100
1114
 
1101
1115
  t.is(store1.state.internalValue, null);
1102
1116
  t.deepEqual(store2.state.internalValue, []);
1117
+ t.is(store1.state.status.automaticChange, false);
1118
+ t.is(store2.state.status.automaticChange, false);
1103
1119
 
1104
1120
  t.end();
1105
1121
  });
@@ -1118,27 +1134,39 @@ tape.test('commit()', (st) => {
1118
1134
  multiple: true,
1119
1135
  },
1120
1136
  });
1137
+ await sleep(0);
1121
1138
 
1122
1139
  store1.commit('internalValue', [3]);
1123
1140
  store2.commit('internalValue', 1);
1124
- await Promise.all([_.nextVueTick(store1), _.nextVueTick(store2)]);
1125
1141
 
1126
1142
  t.is(store1.state.internalValue, 3);
1127
1143
  t.deepEqual(store2.state.internalValue, [1]);
1144
+ t.is(store1.state.status.automaticChange, true);
1145
+ t.is(store2.state.status.automaticChange, true);
1146
+
1147
+ await sleep(0);
1128
1148
 
1129
1149
  store1.commit('internalValue', [1, 2, 3]);
1130
1150
  store2.commit('internalValue', 3);
1131
- await Promise.all([_.nextVueTick(store1), _.nextVueTick(store2)]);
1132
1151
 
1133
1152
  t.is(store1.state.internalValue, 1);
1134
1153
  t.deepEqual(store2.state.internalValue, [3]);
1154
+ t.is(store1.state.status.automaticChange, true);
1155
+ t.is(store2.state.status.automaticChange, true);
1156
+
1157
+ await sleep(0);
1135
1158
 
1136
1159
  store1.commit('internalValue', []);
1137
1160
  store2.commit('internalValue', null);
1138
- await Promise.all([_.nextVueTick(store1), _.nextVueTick(store2)]);
1139
1161
 
1140
1162
  t.is(store1.state.internalValue, null, 'should not select anything');
1141
1163
  t.deepEqual(store2.state.internalValue, [], 'should have no selection');
1164
+ t.is(store1.state.status.automaticChange, true);
1165
+ t.is(store2.state.status.automaticChange, true);
1166
+
1167
+ await sleep(0);
1168
+ t.is(store1.state.status.automaticChange, false);
1169
+ t.is(store2.state.status.automaticChange, false);
1142
1170
 
1143
1171
  t.end();
1144
1172
  });
@@ -1153,14 +1181,19 @@ tape.test('commit()', (st) => {
1153
1181
  });
1154
1182
 
1155
1183
  store1.commit('internalValue', 3);
1156
- await _.nextVueTick(store1);
1157
1184
 
1158
1185
  t.is(store1.state.internalValue, 3);
1186
+ t.is(store1.state.status.automaticChange, false);
1159
1187
 
1188
+ await sleep(0);
1160
1189
  store1.commit('internalValue', null);
1161
- await _.nextVueTick(store1);
1162
1190
 
1163
1191
  t.is(store1.state.internalValue, 0);
1192
+ t.is(store1.state.status.automaticChange, true);
1193
+
1194
+ await sleep(0);
1195
+
1196
+ t.is(store1.state.status.automaticChange, false);
1164
1197
 
1165
1198
  t.end();
1166
1199
  });
package/test/helper.js CHANGED
@@ -8,6 +8,7 @@ function getInitialState(replacedAttributes) {
8
8
  disabled: false,
9
9
  placeholder: '',
10
10
  hideFilter: false,
11
+ keepFilterOpen: false,
11
12
  allowRevert: undefined,
12
13
  allowClearSelection: false,
13
14
  autoSelect: true,
@@ -40,6 +41,8 @@ function getInitialState(replacedAttributes) {
40
41
  errorMessage: '',
41
42
  areAllSelected: false,
42
43
  hasChanged: false,
44
+ automaticChange: false,
45
+ automaticClose: false,
43
46
  },
44
47
  }, replacedAttributes);
45
48
  }
package/types/Store.d.ts CHANGED
@@ -37,10 +37,11 @@ export declare type GetCallback = (_ids: OptionId[]) => Promise<OptionValue[]>;
37
37
  export declare type FormatCallback = (_option: OptionItem) => OptionItem;
38
38
  export declare type SelectionOverflow = 'collapsed' | 'multiline';
39
39
  export declare type ListPosition = 'bottom' | 'top' | 'auto';
40
+ export declare type HideFilter = boolean | 'auto' | 'open';
40
41
  export interface SelecticStoreStateParams {
41
42
  multiple?: boolean;
42
43
  placeholder?: string;
43
- hideFilter?: boolean | 'auto';
44
+ hideFilter?: HideFilter;
44
45
  allowRevert?: boolean;
45
46
  allowClearSelection?: boolean;
46
47
  pageSize?: number;
@@ -83,6 +84,7 @@ export interface SelecticStoreState {
83
84
  disabled: boolean;
84
85
  placeholder: string;
85
86
  hideFilter: boolean;
87
+ keepFilterOpen: boolean;
86
88
  allowRevert?: boolean;
87
89
  allowClearSelection: boolean;
88
90
  autoSelect: boolean;
@@ -112,6 +114,8 @@ export interface SelecticStoreState {
112
114
  errorMessage: string;
113
115
  areAllSelected: boolean;
114
116
  hasChanged: boolean;
117
+ automaticChange: boolean;
118
+ automaticClose: boolean;
115
119
  };
116
120
  }
117
121
  interface Messages {
@@ -145,6 +149,7 @@ export default class SelecticStore {
145
149
  disabled: boolean;
146
150
  placeholder: string;
147
151
  hideFilter: boolean;
152
+ keepFilterOpen: boolean;
148
153
  allowRevert?: boolean | undefined;
149
154
  allowClearSelection: boolean;
150
155
  autoSelect: boolean;
@@ -268,6 +273,8 @@ export default class SelecticStore {
268
273
  errorMessage: string;
269
274
  areAllSelected: boolean;
270
275
  hasChanged: boolean;
276
+ automaticChange: boolean;
277
+ automaticClose: boolean;
271
278
  };
272
279
  };
273
280
  data: Data;
@@ -283,6 +290,8 @@ export default class SelecticStore {
283
290
  _uid: number;
284
291
  constructor(props?: Props);
285
292
  commit<N extends keyof SelecticStoreState, V extends SelecticStoreState[N]>(name: N, value: V): void;
293
+ setAutomaticChange(): void;
294
+ setAutomaticClose(): void;
286
295
  getItem(id: OptionId): OptionValue;
287
296
  getItems(ids: OptionId[]): Promise<OptionItem[]>;
288
297
  selectItem(id: OptionId, selected?: boolean, keepOpen?: boolean): void;
package/types/index.d.ts CHANGED
@@ -1,14 +1,23 @@
1
1
  import { Vue, h } from 'vtyx';
2
2
  import './css/selectic.css';
3
- import { OptionProp, OptionId, StrictOptionId, GroupValue, SelectedValue, FetchCallback, GetCallback, PartialMessages, OptionValue, OptionItem, FormatCallback, SelectionOverflow, ListPosition } from './Store';
3
+ import { OptionProp, OptionId, StrictOptionId, GroupValue, SelectedValue, FetchCallback, GetCallback, PartialMessages, OptionValue, OptionItem, FormatCallback, SelectionOverflow, ListPosition, HideFilter } from './Store';
4
4
  import MainInput from './MainInput';
5
5
  import ExtendedList from './ExtendedList';
6
- export { GroupValue, OptionValue, OptionItem, OptionProp, OptionId, StrictOptionId, SelectedValue, PartialMessages, GetCallback, FetchCallback, FormatCallback, SelectionOverflow, ListPosition, };
6
+ export { GroupValue, OptionValue, OptionItem, OptionProp, OptionId, StrictOptionId, SelectedValue, PartialMessages, GetCallback, FetchCallback, FormatCallback, SelectionOverflow, ListPosition, HideFilter, };
7
+ declare type EventType = 'input' | 'change' | 'open' | 'close' | 'focus' | 'blur' | 'item:click';
8
+ export interface EventOptions {
9
+ instance: Selectic;
10
+ eventType: EventType;
11
+ automatic: boolean;
12
+ }
13
+ export interface EventChangeOptions extends EventOptions {
14
+ isExcluded: boolean;
15
+ }
7
16
  export interface ParamProps {
8
17
  fetchCallback?: FetchCallback;
9
18
  getItemsCallback?: GetCallback;
10
19
  pageSize?: number;
11
- hideFilter?: boolean | 'auto';
20
+ hideFilter?: HideFilter;
12
21
  allowRevert?: boolean;
13
22
  allowClearSelection?: boolean;
14
23
  autoSelect?: boolean;
@@ -122,6 +131,7 @@ export default class Selectic extends Vue<Props> {
122
131
  onFocusChanged(): void;
123
132
  onInternalValueChange(): void;
124
133
  private checkFocus;
134
+ private _emit;
125
135
  private emit;
126
136
  created(): void;
127
137
  mounted(): void;