selectic 3.1.4 → 3.1.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/dist/selectic.common.js +72 -26
- package/dist/selectic.esm.js +72 -26
- package/package.json +1 -1
- package/src/List.tsx +6 -3
- package/src/Store.tsx +2 -2
- package/src/index.tsx +1 -1
- package/src/tools.ts +85 -23
- package/test/Tools/tools.spec.js +151 -0
- package/types/List.d.ts +1 -1
- package/types/tools.d.ts +10 -4
package/dist/selectic.common.js
CHANGED
|
@@ -111,32 +111,76 @@ function assignObject(obj, ...sourceObjects) {
|
|
|
111
111
|
}
|
|
112
112
|
return result;
|
|
113
113
|
}
|
|
114
|
-
/**
|
|
115
|
-
*
|
|
114
|
+
/**
|
|
115
|
+
* Ckeck whether a value is primitive.
|
|
116
|
+
* @returns true if val is primitive and false otherwise.
|
|
117
|
+
*/
|
|
118
|
+
function isPrimitive(val) {
|
|
119
|
+
/* The value null is treated explicitly because in JavaScript
|
|
120
|
+
* `typeof null === 'object'` is evaluated to `true`.
|
|
121
|
+
*/
|
|
122
|
+
return val === null || (typeof val !== 'object' && typeof val !== 'function');
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Performs a deep comparison between two objects to determine if they
|
|
126
|
+
* should be considered equal.
|
|
127
|
+
*
|
|
128
|
+
* @param objA object to compare to objB.
|
|
129
|
+
* @param objB object to compare to objA.
|
|
130
|
+
* @param attributes list of attributes to not compare.
|
|
131
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
132
|
+
* @returns true if objA should be considered equal to objB.
|
|
116
133
|
*/
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
function isDeepEqual(objA, objB, ignoreAttributes = [], refs = new WeakMap()) {
|
|
135
|
+
objA = vue.unref(objA);
|
|
136
|
+
objB = vue.unref(objB);
|
|
137
|
+
/* For primitive types */
|
|
138
|
+
if (isPrimitive(objA)) {
|
|
139
|
+
return isPrimitive(objB) && Object.is(objA, objB);
|
|
140
|
+
}
|
|
141
|
+
/* For functions (follow the behavior of _.isEqual and compare functions
|
|
142
|
+
* by reference). */
|
|
143
|
+
if (typeof objA === 'function') {
|
|
144
|
+
return typeof objB === 'function' && objA === objB;
|
|
145
|
+
}
|
|
146
|
+
/* For circular references */
|
|
147
|
+
if (refs.has(objA)) {
|
|
148
|
+
return refs.get(objA) === objB;
|
|
149
|
+
}
|
|
150
|
+
refs.set(objA, objB);
|
|
151
|
+
/* For objects */
|
|
152
|
+
if (typeof objA === 'object') {
|
|
153
|
+
if (typeof objB !== 'object') {
|
|
125
154
|
return false;
|
|
126
155
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
156
|
+
/* For arrays */
|
|
157
|
+
if (Array.isArray(objA)) {
|
|
158
|
+
return Array.isArray(objB) &&
|
|
159
|
+
objA.length === objB.length &&
|
|
160
|
+
!objA.some((val, idx) => !isDeepEqual(val, objB[idx], ignoreAttributes, refs));
|
|
161
|
+
}
|
|
162
|
+
/* For RegExp */
|
|
163
|
+
if (objA instanceof RegExp) {
|
|
164
|
+
return objB instanceof RegExp &&
|
|
165
|
+
objA.source === objB.source &&
|
|
166
|
+
objA.flags === objB.flags;
|
|
167
|
+
}
|
|
168
|
+
/* For Date */
|
|
169
|
+
if (objA instanceof Date) {
|
|
170
|
+
return objB instanceof Date && objA.getTime() === objB.getTime();
|
|
171
|
+
}
|
|
172
|
+
/* This should be an object */
|
|
173
|
+
const aRec = objA;
|
|
174
|
+
const bRec = objB;
|
|
175
|
+
const aKeys = Object.keys(aRec).filter((key) => !ignoreAttributes.includes(key));
|
|
176
|
+
const bKeys = Object.keys(bRec).filter((key) => !ignoreAttributes.includes(key));
|
|
177
|
+
const differentKeyFound = aKeys.some((key) => {
|
|
178
|
+
return !bKeys.includes(key) ||
|
|
179
|
+
!isDeepEqual(aRec[key], bRec[key], ignoreAttributes, refs);
|
|
138
180
|
});
|
|
139
|
-
|
|
181
|
+
return aKeys.length === bKeys.length && !differentKeyFound;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
140
184
|
}
|
|
141
185
|
let displayLog = false;
|
|
142
186
|
function debug(fName, step, ...args) {
|
|
@@ -1190,7 +1234,7 @@ class SelecticStore {
|
|
|
1190
1234
|
/* update cache */
|
|
1191
1235
|
state.totalDynOptions = total;
|
|
1192
1236
|
const old = state.dynOptions.splice(offset, result.length, ...result);
|
|
1193
|
-
if (
|
|
1237
|
+
if (isDeepEqual(old, result)) {
|
|
1194
1238
|
/* Added options are the same as previous ones.
|
|
1195
1239
|
* Stop fetching to avoid infinite loop
|
|
1196
1240
|
*/
|
|
@@ -2452,8 +2496,10 @@ let List = class List extends vtyx.Vue {
|
|
|
2452
2496
|
onOffsetChange() {
|
|
2453
2497
|
this.checkOffset();
|
|
2454
2498
|
}
|
|
2455
|
-
onFilteredOptionsChange() {
|
|
2456
|
-
|
|
2499
|
+
onFilteredOptionsChange(oldVal, newVal) {
|
|
2500
|
+
if (!isDeepEqual(oldVal, newVal)) {
|
|
2501
|
+
this.checkOffset();
|
|
2502
|
+
}
|
|
2457
2503
|
}
|
|
2458
2504
|
onGroupIdChange() {
|
|
2459
2505
|
this.$emit('groupId', this.groupId);
|
|
@@ -3013,7 +3059,7 @@ let Selectic = class Selectic extends vtyx.Vue {
|
|
|
3013
3059
|
else {
|
|
3014
3060
|
this.removeListeners();
|
|
3015
3061
|
if (state.status.hasChanged) {
|
|
3016
|
-
this
|
|
3062
|
+
this.emit('change', this.getValue(), state.selectionIsExcluded);
|
|
3017
3063
|
this.store.resetChange();
|
|
3018
3064
|
}
|
|
3019
3065
|
this.emit('close');
|
package/dist/selectic.esm.js
CHANGED
|
@@ -107,32 +107,76 @@ function assignObject(obj, ...sourceObjects) {
|
|
|
107
107
|
}
|
|
108
108
|
return result;
|
|
109
109
|
}
|
|
110
|
-
/**
|
|
111
|
-
*
|
|
110
|
+
/**
|
|
111
|
+
* Ckeck whether a value is primitive.
|
|
112
|
+
* @returns true if val is primitive and false otherwise.
|
|
113
|
+
*/
|
|
114
|
+
function isPrimitive(val) {
|
|
115
|
+
/* The value null is treated explicitly because in JavaScript
|
|
116
|
+
* `typeof null === 'object'` is evaluated to `true`.
|
|
117
|
+
*/
|
|
118
|
+
return val === null || (typeof val !== 'object' && typeof val !== 'function');
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Performs a deep comparison between two objects to determine if they
|
|
122
|
+
* should be considered equal.
|
|
123
|
+
*
|
|
124
|
+
* @param objA object to compare to objB.
|
|
125
|
+
* @param objB object to compare to objA.
|
|
126
|
+
* @param attributes list of attributes to not compare.
|
|
127
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
128
|
+
* @returns true if objA should be considered equal to objB.
|
|
112
129
|
*/
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
function isDeepEqual(objA, objB, ignoreAttributes = [], refs = new WeakMap()) {
|
|
131
|
+
objA = unref(objA);
|
|
132
|
+
objB = unref(objB);
|
|
133
|
+
/* For primitive types */
|
|
134
|
+
if (isPrimitive(objA)) {
|
|
135
|
+
return isPrimitive(objB) && Object.is(objA, objB);
|
|
136
|
+
}
|
|
137
|
+
/* For functions (follow the behavior of _.isEqual and compare functions
|
|
138
|
+
* by reference). */
|
|
139
|
+
if (typeof objA === 'function') {
|
|
140
|
+
return typeof objB === 'function' && objA === objB;
|
|
141
|
+
}
|
|
142
|
+
/* For circular references */
|
|
143
|
+
if (refs.has(objA)) {
|
|
144
|
+
return refs.get(objA) === objB;
|
|
145
|
+
}
|
|
146
|
+
refs.set(objA, objB);
|
|
147
|
+
/* For objects */
|
|
148
|
+
if (typeof objA === 'object') {
|
|
149
|
+
if (typeof objB !== 'object') {
|
|
121
150
|
return false;
|
|
122
151
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
152
|
+
/* For arrays */
|
|
153
|
+
if (Array.isArray(objA)) {
|
|
154
|
+
return Array.isArray(objB) &&
|
|
155
|
+
objA.length === objB.length &&
|
|
156
|
+
!objA.some((val, idx) => !isDeepEqual(val, objB[idx], ignoreAttributes, refs));
|
|
157
|
+
}
|
|
158
|
+
/* For RegExp */
|
|
159
|
+
if (objA instanceof RegExp) {
|
|
160
|
+
return objB instanceof RegExp &&
|
|
161
|
+
objA.source === objB.source &&
|
|
162
|
+
objA.flags === objB.flags;
|
|
163
|
+
}
|
|
164
|
+
/* For Date */
|
|
165
|
+
if (objA instanceof Date) {
|
|
166
|
+
return objB instanceof Date && objA.getTime() === objB.getTime();
|
|
167
|
+
}
|
|
168
|
+
/* This should be an object */
|
|
169
|
+
const aRec = objA;
|
|
170
|
+
const bRec = objB;
|
|
171
|
+
const aKeys = Object.keys(aRec).filter((key) => !ignoreAttributes.includes(key));
|
|
172
|
+
const bKeys = Object.keys(bRec).filter((key) => !ignoreAttributes.includes(key));
|
|
173
|
+
const differentKeyFound = aKeys.some((key) => {
|
|
174
|
+
return !bKeys.includes(key) ||
|
|
175
|
+
!isDeepEqual(aRec[key], bRec[key], ignoreAttributes, refs);
|
|
134
176
|
});
|
|
135
|
-
|
|
177
|
+
return aKeys.length === bKeys.length && !differentKeyFound;
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
136
180
|
}
|
|
137
181
|
let displayLog = false;
|
|
138
182
|
function debug(fName, step, ...args) {
|
|
@@ -1186,7 +1230,7 @@ class SelecticStore {
|
|
|
1186
1230
|
/* update cache */
|
|
1187
1231
|
state.totalDynOptions = total;
|
|
1188
1232
|
const old = state.dynOptions.splice(offset, result.length, ...result);
|
|
1189
|
-
if (
|
|
1233
|
+
if (isDeepEqual(old, result)) {
|
|
1190
1234
|
/* Added options are the same as previous ones.
|
|
1191
1235
|
* Stop fetching to avoid infinite loop
|
|
1192
1236
|
*/
|
|
@@ -2448,8 +2492,10 @@ let List = class List extends Vue {
|
|
|
2448
2492
|
onOffsetChange() {
|
|
2449
2493
|
this.checkOffset();
|
|
2450
2494
|
}
|
|
2451
|
-
onFilteredOptionsChange() {
|
|
2452
|
-
|
|
2495
|
+
onFilteredOptionsChange(oldVal, newVal) {
|
|
2496
|
+
if (!isDeepEqual(oldVal, newVal)) {
|
|
2497
|
+
this.checkOffset();
|
|
2498
|
+
}
|
|
2453
2499
|
}
|
|
2454
2500
|
onGroupIdChange() {
|
|
2455
2501
|
this.$emit('groupId', this.groupId);
|
|
@@ -3009,7 +3055,7 @@ let Selectic = class Selectic extends Vue {
|
|
|
3009
3055
|
else {
|
|
3010
3056
|
this.removeListeners();
|
|
3011
3057
|
if (state.status.hasChanged) {
|
|
3012
|
-
this
|
|
3058
|
+
this.emit('change', this.getValue(), state.selectionIsExcluded);
|
|
3013
3059
|
this.store.resetChange();
|
|
3014
3060
|
}
|
|
3015
3061
|
this.emit('close');
|
package/package.json
CHANGED
package/src/List.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* It handles interactions with these items.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {Vue, Component, Prop, Watch, h} from 'vtyx';
|
|
6
|
+
import { Vue, Component, Prop, Watch, h } from 'vtyx';
|
|
7
7
|
import { unref } from 'vue';
|
|
8
8
|
|
|
9
9
|
import Store, {
|
|
@@ -11,6 +11,7 @@ import Store, {
|
|
|
11
11
|
OptionId,
|
|
12
12
|
} from './Store';
|
|
13
13
|
import Icon from './Icon';
|
|
14
|
+
import { isDeepEqual } from './tools';
|
|
14
15
|
|
|
15
16
|
export interface Props {
|
|
16
17
|
store: Store;
|
|
@@ -222,8 +223,10 @@ export default class List extends Vue<Props> {
|
|
|
222
223
|
}
|
|
223
224
|
|
|
224
225
|
@Watch('filteredOptions', { deep: true })
|
|
225
|
-
public onFilteredOptionsChange() {
|
|
226
|
-
|
|
226
|
+
public onFilteredOptionsChange(oldVal: OptionItem[], newVal: OptionItem[]) {
|
|
227
|
+
if (!isDeepEqual(oldVal, newVal)) {
|
|
228
|
+
this.checkOffset();
|
|
229
|
+
}
|
|
227
230
|
}
|
|
228
231
|
|
|
229
232
|
@Watch('groupId')
|
package/src/Store.tsx
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { reactive, watch, unref, computed, ComputedRef } from 'vue';
|
|
8
8
|
import {
|
|
9
9
|
assignObject,
|
|
10
|
-
|
|
10
|
+
isDeepEqual,
|
|
11
11
|
convertToRegExp,
|
|
12
12
|
debug,
|
|
13
13
|
deepClone,
|
|
@@ -1704,7 +1704,7 @@ export default class SelecticStore {
|
|
|
1704
1704
|
state.totalDynOptions = total;
|
|
1705
1705
|
const old = state.dynOptions.splice(offset, result.length, ...result);
|
|
1706
1706
|
|
|
1707
|
-
if (
|
|
1707
|
+
if (isDeepEqual(old, result)) {
|
|
1708
1708
|
/* Added options are the same as previous ones.
|
|
1709
1709
|
* Stop fetching to avoid infinite loop
|
|
1710
1710
|
*/
|
package/src/index.tsx
CHANGED
|
@@ -560,7 +560,7 @@ export default class Selectic extends Vue<Props> {
|
|
|
560
560
|
} else {
|
|
561
561
|
this.removeListeners();
|
|
562
562
|
if (state.status.hasChanged) {
|
|
563
|
-
this
|
|
563
|
+
this.emit('change', this.getValue(), state.selectionIsExcluded);
|
|
564
564
|
this.store.resetChange();
|
|
565
565
|
}
|
|
566
566
|
this.emit('close');
|
package/src/tools.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { unref } from 'vue';
|
|
2
|
-
import { OptionValue } from './Store';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Clone the object and its inner properties.
|
|
@@ -8,7 +7,11 @@ import { OptionValue } from './Store';
|
|
|
8
7
|
* @param refs internal reference to object to avoid cyclic references
|
|
9
8
|
* @returns a copy of obj
|
|
10
9
|
*/
|
|
11
|
-
export function deepClone<T = any>(
|
|
10
|
+
export function deepClone<T = any>(
|
|
11
|
+
origObject: T,
|
|
12
|
+
ignoreAttributes: string[] = [],
|
|
13
|
+
refs: WeakMap<any, any> = new WeakMap()
|
|
14
|
+
): T {
|
|
12
15
|
const obj = unref(origObject);
|
|
13
16
|
|
|
14
17
|
/* For circular references */
|
|
@@ -88,34 +91,93 @@ export function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial
|
|
|
88
91
|
return result as T;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
/**
|
|
92
|
-
*
|
|
94
|
+
/**
|
|
95
|
+
* Ckeck whether a value is primitive.
|
|
96
|
+
* @returns true if val is primitive and false otherwise.
|
|
97
|
+
*/
|
|
98
|
+
function isPrimitive<T = any>(val: T): boolean {
|
|
99
|
+
/* The value null is treated explicitly because in JavaScript
|
|
100
|
+
* `typeof null === 'object'` is evaluated to `true`.
|
|
101
|
+
*/
|
|
102
|
+
return val === null || (typeof val !== 'object' && typeof val !== 'function');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Performs a deep comparison between two objects to determine if they
|
|
107
|
+
* should be considered equal.
|
|
108
|
+
*
|
|
109
|
+
* @param objA object to compare to objB.
|
|
110
|
+
* @param objB object to compare to objA.
|
|
111
|
+
* @param attributes list of attributes to not compare.
|
|
112
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
113
|
+
* @returns true if objA should be considered equal to objB.
|
|
93
114
|
*/
|
|
94
|
-
export function
|
|
95
|
-
|
|
96
|
-
|
|
115
|
+
export function isDeepEqual<T = any>(
|
|
116
|
+
objA: T,
|
|
117
|
+
objB: T,
|
|
118
|
+
ignoreAttributes: string[] = [],
|
|
119
|
+
refs: WeakMap<any, any> = new WeakMap()
|
|
120
|
+
): boolean {
|
|
121
|
+
objA = unref(objA);
|
|
122
|
+
objB = unref(objB);
|
|
123
|
+
|
|
124
|
+
/* For primitive types */
|
|
125
|
+
if (isPrimitive(objA)) {
|
|
126
|
+
return isPrimitive(objB) && Object.is(objA, objB);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* For functions (follow the behavior of _.isEqual and compare functions
|
|
130
|
+
* by reference). */
|
|
131
|
+
if (typeof objA === 'function') {
|
|
132
|
+
return typeof objB === 'function' && objA === objB;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* For circular references */
|
|
136
|
+
if (refs.has(objA)) {
|
|
137
|
+
return refs.get(objA) === objB;
|
|
97
138
|
}
|
|
139
|
+
refs.set(objA, objB);
|
|
98
140
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (keys.length !== Object.keys(newOption).length) {
|
|
141
|
+
/* For objects */
|
|
142
|
+
if (typeof objA === 'object') {
|
|
143
|
+
if (typeof objB !== 'object') {
|
|
103
144
|
return false;
|
|
104
145
|
}
|
|
105
|
-
return keys.every((optionKey) => {
|
|
106
|
-
const key = optionKey as keyof OptionValue;
|
|
107
|
-
const oldValue = oldOption[key];
|
|
108
|
-
const newValue = newOption[key];
|
|
109
146
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
147
|
+
/* For arrays */
|
|
148
|
+
if (Array.isArray(objA)) {
|
|
149
|
+
return Array.isArray(objB) &&
|
|
150
|
+
objA.length === objB.length &&
|
|
151
|
+
!objA.some((val, idx) => !isDeepEqual(val, (objB as unknown[])[idx], ignoreAttributes, refs));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* For RegExp */
|
|
155
|
+
if (objA instanceof RegExp) {
|
|
156
|
+
return objB instanceof RegExp &&
|
|
157
|
+
objA.source === objB.source &&
|
|
158
|
+
objA.flags === objB.flags;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* For Date */
|
|
162
|
+
if (objA instanceof Date) {
|
|
163
|
+
return objB instanceof Date && objA.getTime() === objB.getTime();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* This should be an object */
|
|
167
|
+
const aRec = objA as Record<string, any>;
|
|
168
|
+
const bRec = objB as Record<string, any>;
|
|
169
|
+
const aKeys = Object.keys(aRec).filter((key) => !ignoreAttributes.includes(key));
|
|
170
|
+
const bKeys = Object.keys(bRec).filter((key) => !ignoreAttributes.includes(key));
|
|
171
|
+
|
|
172
|
+
const differentKeyFound = aKeys.some((key) => {
|
|
173
|
+
return !bKeys.includes(key) ||
|
|
174
|
+
!isDeepEqual(aRec[key], bRec[key], ignoreAttributes, refs);
|
|
117
175
|
});
|
|
118
|
-
|
|
176
|
+
|
|
177
|
+
return aKeys.length === bKeys.length && !differentKeyFound;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return true;
|
|
119
181
|
}
|
|
120
182
|
|
|
121
183
|
let displayLog = false;
|
package/test/Tools/tools.spec.js
CHANGED
|
@@ -5,6 +5,7 @@ const {
|
|
|
5
5
|
assignObject,
|
|
6
6
|
convertToRegExp,
|
|
7
7
|
deepClone,
|
|
8
|
+
isDeepEqual,
|
|
8
9
|
} = toolFile;
|
|
9
10
|
|
|
10
11
|
tape.test('assignObject()', (st) => {
|
|
@@ -402,3 +403,153 @@ tape.test('deepClone()', (st) => {
|
|
|
402
403
|
tst.end();
|
|
403
404
|
});
|
|
404
405
|
});
|
|
406
|
+
|
|
407
|
+
tape.test('isDeepEqual()', (st) => {
|
|
408
|
+
const fn = () => {};
|
|
409
|
+
const obj = {
|
|
410
|
+
a: 1,
|
|
411
|
+
b: 'b',
|
|
412
|
+
c: false,
|
|
413
|
+
d: undefined,
|
|
414
|
+
e: null,
|
|
415
|
+
f: {},
|
|
416
|
+
g: fn,
|
|
417
|
+
spe1: NaN,
|
|
418
|
+
spe2: Infinity,
|
|
419
|
+
spe3: '',
|
|
420
|
+
spe4: [],
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
st.test('should compare simple objects', (tst) => {
|
|
424
|
+
const objA = obj;
|
|
425
|
+
const objB = deepClone(obj);
|
|
426
|
+
|
|
427
|
+
tst.is(isDeepEqual(objB, objA), true, 'should be equal');
|
|
428
|
+
|
|
429
|
+
objB.a = objB.a.toString();
|
|
430
|
+
tst.is(isDeepEqual(objA, objB), false, 'should not be equal');
|
|
431
|
+
tst.end();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
st.test('should compare nested objects', (tst) => {
|
|
435
|
+
const deep1 = obj;
|
|
436
|
+
const deep2 = {
|
|
437
|
+
deep: deep1,
|
|
438
|
+
added: 'a value',
|
|
439
|
+
};
|
|
440
|
+
const objA = {
|
|
441
|
+
d: deep2,
|
|
442
|
+
}
|
|
443
|
+
const objB = deepClone(objA);
|
|
444
|
+
|
|
445
|
+
tst.is(isDeepEqual(objA, objB), true, 'should be equal');
|
|
446
|
+
tst.end();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
st.test('with arrays', (tst) => {
|
|
450
|
+
const nestedArray1 = [
|
|
451
|
+
obj,
|
|
452
|
+
{ a: 'value' },
|
|
453
|
+
null,
|
|
454
|
+
undefined,
|
|
455
|
+
42,
|
|
456
|
+
'value',
|
|
457
|
+
];
|
|
458
|
+
const nestedArray2 = deepClone(nestedArray1);
|
|
459
|
+
const arrayA = [
|
|
460
|
+
{
|
|
461
|
+
deep: nestedArray1,
|
|
462
|
+
},
|
|
463
|
+
nestedArray2,
|
|
464
|
+
42, 'value', null, undefined, [[]],
|
|
465
|
+
];
|
|
466
|
+
const arrayB = deepClone(arrayA);
|
|
467
|
+
|
|
468
|
+
tst.is(isDeepEqual(arrayA, arrayB), true, 'should be equal');
|
|
469
|
+
tst.end();
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
st.test('with RegExp', (tst) => {
|
|
473
|
+
const regA1 = /hello?/gi;
|
|
474
|
+
const regA2 = /.* [aA]+?/;
|
|
475
|
+
|
|
476
|
+
const regB1 = deepClone(regA1);
|
|
477
|
+
const regB2 = {rgx: regA2};
|
|
478
|
+
|
|
479
|
+
tst.is(isDeepEqual(regA1, regB1), true, 'should be equal');
|
|
480
|
+
tst.is(isDeepEqual(regA2, regB2), false, 'should not be equal');
|
|
481
|
+
tst.end();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
st.test('with Date', (tst) => {
|
|
485
|
+
const dateA = new Date('1996-06-27');
|
|
486
|
+
const dateB = new Date('1996-06-27');
|
|
487
|
+
|
|
488
|
+
tst.is(isDeepEqual(dateA, dateB), true, 'should be equal');
|
|
489
|
+
tst.end();
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
st.test('with functions', (tst) => {
|
|
493
|
+
const fn1 = fn;
|
|
494
|
+
const fn2 = () => {};
|
|
495
|
+
|
|
496
|
+
tst.is(isDeepEqual(fn1, fn), true, 'should be equal');
|
|
497
|
+
tst.is(isDeepEqual(fn2, fn), false, 'should not be equal');
|
|
498
|
+
tst.end();
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
st.test('with primitives', (tst) => {
|
|
502
|
+
tst.is(isDeepEqual(10%3, 1), true, 'should be equal');
|
|
503
|
+
tst.is(isDeepEqual('Bingo'[0], 'B'), true, 'should be equal');
|
|
504
|
+
tst.is(isDeepEqual(!true, false), true, 'should be equal');
|
|
505
|
+
tst.is(isDeepEqual(obj.e, null), true, 'should be equal');
|
|
506
|
+
tst.is(isDeepEqual(obj.d, undefined), true, 'should be equal');
|
|
507
|
+
tst.end();
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
st.test('with circular references', (tst) => {
|
|
511
|
+
const obj1 = {
|
|
512
|
+
a: 'a',
|
|
513
|
+
}
|
|
514
|
+
const obj2 = {
|
|
515
|
+
b: 'b',
|
|
516
|
+
}
|
|
517
|
+
obj1.child = obj1;
|
|
518
|
+
obj1.sibling = obj2;
|
|
519
|
+
obj2.sibling = obj1;
|
|
520
|
+
|
|
521
|
+
const objA = [obj1, obj2];
|
|
522
|
+
const objB = deepClone(objA);
|
|
523
|
+
|
|
524
|
+
tst.is(isDeepEqual(objA, objB), true, 'should be equal');
|
|
525
|
+
tst.end();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
st.test('can ignore some attributes', (tst) => {
|
|
529
|
+
const noCompare1 = {
|
|
530
|
+
ref: 1,
|
|
531
|
+
};
|
|
532
|
+
const noCompare2 = new Set(['alpha', 'omega']);
|
|
533
|
+
const noCompare3 = new Map([[1, 'alpha'], [22, 'omega']]);
|
|
534
|
+
const deep1 = {
|
|
535
|
+
a: 'alpha',
|
|
536
|
+
noCompare: noCompare3,
|
|
537
|
+
};
|
|
538
|
+
const objA = {
|
|
539
|
+
id: 'ref',
|
|
540
|
+
noCopy: noCompare1,
|
|
541
|
+
nop: noCompare2,
|
|
542
|
+
not: 42,
|
|
543
|
+
deep: deep1,
|
|
544
|
+
};
|
|
545
|
+
const objB = deepClone(objA);
|
|
546
|
+
objB.not = 24;
|
|
547
|
+
|
|
548
|
+
const result1 = isDeepEqual(objA, objB, ['noCopy', 'nothing', 'nop', 'not']);
|
|
549
|
+
const result2 = isDeepEqual(objA, objB, ['noCopy', 'nothing', 'nop']);
|
|
550
|
+
|
|
551
|
+
tst.is(result1, true, 'should be equal');
|
|
552
|
+
tst.is(result2, false, 'should not be equal');
|
|
553
|
+
tst.end();
|
|
554
|
+
});
|
|
555
|
+
});
|
package/types/List.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export default class List extends Vue<Props> {
|
|
|
30
30
|
private onMouseOver;
|
|
31
31
|
onIndexChange(): void;
|
|
32
32
|
onOffsetChange(): void;
|
|
33
|
-
onFilteredOptionsChange(): void;
|
|
33
|
+
onFilteredOptionsChange(oldVal: OptionItem[], newVal: OptionItem[]): void;
|
|
34
34
|
onGroupIdChange(): void;
|
|
35
35
|
mounted(): void;
|
|
36
36
|
render(): h.JSX.Element;
|
package/types/tools.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { OptionValue } from './Store';
|
|
2
1
|
/**
|
|
3
2
|
* Clone the object and its inner properties.
|
|
4
3
|
* @param obj The object to be clone.
|
|
@@ -21,10 +20,17 @@ export declare function deepClone<T = any>(origObject: T, ignoreAttributes?: str
|
|
|
21
20
|
export declare function convertToRegExp(name: string, flag?: string): RegExp;
|
|
22
21
|
/** Does the same as Object.assign but does not replace if value is undefined */
|
|
23
22
|
export declare function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial<T>>): T;
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
23
|
+
/**
|
|
24
|
+
* Performs a deep comparison between two objects to determine if they
|
|
25
|
+
* should be considered equal.
|
|
26
|
+
*
|
|
27
|
+
* @param objA object to compare to objB.
|
|
28
|
+
* @param objB object to compare to objA.
|
|
29
|
+
* @param attributes list of attributes to not compare.
|
|
30
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
31
|
+
* @returns true if objA should be considered equal to objB.
|
|
26
32
|
*/
|
|
27
|
-
export declare function
|
|
33
|
+
export declare function isDeepEqual<T = any>(objA: T, objB: T, ignoreAttributes?: string[], refs?: WeakMap<any, any>): boolean;
|
|
28
34
|
export declare function debug(fName: string, step: string, ...args: any[]): void;
|
|
29
35
|
export declare namespace debug {
|
|
30
36
|
var enable: (display: boolean) => void;
|