selectic 3.0.8 → 3.0.13
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/README.md +1 -0
- package/dist/selectic.common.js +59 -22
- package/dist/selectic.esm.js +61 -24
- package/package.json +4 -2
- package/rollup.config.js +8 -1
- package/src/Store.tsx +5 -40
- package/src/index.tsx +10 -13
- package/src/tools.ts +86 -0
- package/test/Store/Store_creation.spec.js +0 -13
- package/test/Store/commit.spec.js +0 -13
- package/test/Store/getItem.spec.js +0 -13
- package/test/Store/getItems.spec.js +0 -13
- package/test/Store/selectItem.spec.js +0 -13
- package/test/Store/toggleSelectAll.spec.js +0 -13
- package/test/Tools/tools.spec.js +404 -0
- package/types/tools.d.ts +21 -0
- package/.package.json.un~ +0 -0
package/README.md
CHANGED
package/dist/selectic.common.js
CHANGED
|
@@ -35,13 +35,50 @@ function styleInject(css, ref) {
|
|
|
35
35
|
var css_248z = "/* {{{ Variables */\n\n:root {\n --selectic-font-size: 14px;\n --selectic-cursor-disabled: not-allowed;\n\n /* The main element */\n --selectic-color: #555555;\n --selectic-bg: #ffffff;\n\n /* The main element (when disabled) */\n --selectic-color-disabled: #787878;\n --selectic-bg-disabled: #eeeeee;\n\n /* The list */\n --selectic-panel-bg: #f0f0f0;\n --selectic-separator-bordercolor: #cccccc;\n /* --selectic-item-color: var(--selectic-color); /* Can be set in any CSS configuration */\n\n /* The current selected item */\n --selectic-selected-item-color: #428bca;\n\n /* When mouse is over items or by selecting with key arrows */\n --selectic-active-item-color: #ffffff;\n --selectic-active-item-bg: #66afe9;\n\n /* Selected values in main element */\n --selectic-value-bg: #f0f0f0;\n /* --selectic-more-items-bg: var(--selectic-info-bg); /* can be set in any CSS configuration */\n /* --selectic-more-items-color: var(--selectic-info-color); /* can be set in any CSS configuration */\n --selectic-more-items-bg-disabled: #cccccc;\n\n /* Information message */\n --selectic-info-bg: #5bc0de;\n --selectic-info-color: #ffffff;\n\n /* Error message */\n --selectic-error-bg: #b72c29;\n --selectic-error-color: #ffffff;\n\n /* XXX: Currently it is important to keep this size for a correct scroll\n * height estimation */\n --selectic-input-height: 30px;\n}\n\n/* }}} */\n/* {{{ Bootstrap equivalent style */\n\n.selectic .form-control {\n display: block;\n width: 100%;\n height: calc(var(--selectic-input-height) - 2px);\n font-size: var(--selectic-font-size);\n line-height: 1.42857143;\n color: var(--selectic-color);\n background-color: var(--selectic-bg);\n background-image: none;\n border: 1px solid var(--selectic-separator-bordercolor); /* should use a better variable */\n border-radius: 4px;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;\n}\n.selectic .has-feedback {\n position: relative;\n}\n.selectic .has-feedback .form-control {\n padding-right: calc(var(--selectic-input-height) + 4px);\n}\n\n.selectic .form-control-feedback.fa,\n.selectic .form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: calc(var(--selectic-input-height) + 4px);\n height: calc(var(--selectic-input-height) + 4px);\n line-height: var(--selectic-input-height);\n text-align: center;\n pointer-events: none;\n}\n\n.selectic .alert-info {\n background-color: var(--selectic-info-bg);\n color: var(--selectic-info-color);\n}\n\n.selectic .alert-danger {\n background-color: var(--selectic-error-bg);\n color: var(--selectic-error-color);\n}\n\n/* }}} */\n\n.selectic * {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n.selectic.form-control {\n display: inline-block;\n padding: 0;\n cursor: pointer;\n border: unset;\n}\n\n.has-feedback .selectic__icon-container.form-control-feedback {\n right: 0;\n}\n\n/* The input which contains the selected value\n * XXX: This input should stay hidden behind other elements, but is \"visible\"\n * (in term of DOM point of view) in order to get and to trigger the `focus`\n * DOM event. */\n.selectic__input-value {\n position: fixed;\n opacity: 0;\n z-index: -1000;\n top: -100px;\n}\n\n/* XXX: .form-control has been added to this selector to improve priority and\n * override some rules of the original .form-control */\n.selectic-input.form-control {\n display: inline-flex;\n justify-content: space-between;\n overflow: hidden;\n width: 100%;\n min-height: var(--selectic-input-height);\n padding-top: 0;\n padding-bottom: 0;\n padding-left: 5px;\n line-height: calc(var(--selectic-input-height) - 4px);\n color: var(--selectic-color);\n}\n\n.selectic-input__reverse-icon {\n align-self: center;\n margin-right: 3px;\n cursor: default;\n}\n.selectic-input__clear-icon {\n align-self: center;\n margin-left: 3px;\n cursor: pointer;\n}\n.selectic-input__clear-icon:hover {\n color: var(--selectic-selected-item-color);\n}\n\n.selectic-input.focused {\n border-bottom-left-radius: 0px;\n border-bottom-right-radius: 0px;\n}\n\n.selectic-input.disabled {\n cursor: var(--selectic-cursor-disabled);\n background-color: var(--selectic-bg-disabled);\n}\n.selectic-input.disabled .more-items {\n\tbackground-color: var(--selectic-more-items-bg-disabled);\n}\n\n.selectic-input__selected-items {\n display: inline-flex;\n flex-wrap: nowrap;\n align-items: center;\n white-space: nowrap;\n}\n\n.selectic-input__selected-items__placeholder {\n font-style: italic;\n opacity: 0.7;\n white-space: nowrap;\n}\n\n.selectic-icon {\n color: var(--selectic-color);\n text-align: center;\n vertical-align: middle;\n}\n\n.selectic__extended-list {\n position: fixed;\n z-index: 2000;\n height: auto;\n background-color: var(--selectic-bg, #ffffff);\n box-shadow: 2px 5px 12px 0px #888888;\n border-radius: 0 0 4px 4px;\n padding: 0;\n min-width: 200px;\n}\n.selectic__extended-list__list-items {\n max-height: calc(var(--selectic-input-height) * 10);\n overflow: auto;\n padding-left: 0;\n}\n\n.selectic-item {\n display: block;\n position: relative;\n box-sizing: border-box;\n padding: 2px 8px;\n color: var(--selectic-item-color, var(--selectic-color));\n min-height: calc(var(--selectic-input-height) - 3px);\n list-style-type: none;\n white-space: nowrap;\n cursor: pointer;\n}\n\n.selectic-item_text {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n.selectic-item:not(.selected) .selectic-item_icon {\n opacity: 0;\n}\n\n.selectic-item_text {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n.selectic-item__active {\n background-color: var(--selectic-active-item-bg);\n color: var(--selectic-active-item-color);\n}\n.selectic-item__active:not(.selected) .selectic-item_icon {\n opacity: 0.2;\n}\n\n.selectic-item__disabled {\n color: var(--selectic-color-disabled);\n background-color: var(--selectic-bg-disabled);\n}\n\n.selectic-item__is-in-group {\n padding-left: 2em;\n}\n\n.selectic-item__is-group {\n font-weight: bold;\n cursor: default;\n}\n\n.selectic-item.selected {\n color: var(--selectic-selected-item-color);\n}\n.selectic-search-scope {\n color: #e0e0e0;\n left: auto;\n right: 10px;\n}\n\n.selectic__message {\n text-align: center;\n padding: 3px;\n}\n\n.selectic .filter-panel {\n padding: 3px;\n margin-left: 0px;\n margin-right: 0px;\n background-color: var(--selectic-panel-bg);\n border-bottom: 1px solid var(--selectic-separator-bordercolor);\n}\n\n.selectic .panelclosed {\n max-height: 0px;\n transition: max-height 0.3s ease-out;\n overflow: hidden;\n}\n\n.panelopened {\n max-height: 200px;\n transition: max-height 0.3s ease-in;\n overflow: hidden;\n}\n\n.selectic .filter-panel__input {\n padding-left: 0px;\n padding-right: 0px;\n padding-bottom: 10px;\n margin-bottom: 0px;\n}\n.selectic .filter-input {\n height: calc(var(--selectic-input-height) * 0.75);\n}\n\n.selectic .checkbox-filter {\n padding: 5px;\n text-align: center;\n}\n\n.selectic .curtain-handler {\n text-align: center;\n}\n\n.selectic .toggle-selectic {\n margin: 5px;\n padding-left: 0px;\n padding-right: 0px;\n}\n\n.selectic .toggle-boolean-select-all-toggle {\n display: inline;\n margin-right: 15px;\n}\n\n.selectic .toggle-boolean-excluding-toggle {\n display: inline;\n margin-right: 15px;\n}\n\n.selectic .single-value {\n display: grid;\n grid-template: \"value icon\" 1fr / max-content max-content;\n\n padding: 2px;\n padding-left: 5px;\n margin-left: 0;\n margin-right: 5px;\n /* margin top/bottom are mainly to create a gutter in multilines */\n margin-top: 2px;\n margin-bottom: 2px;\n\n border-radius: 3px;\n background-color: var(--selectic-value-bg);\n max-height: calc(var(--selectic-input-height) - 10px);\n max-width: 100%;\n min-width: 30px;\n\n overflow: hidden;\n white-space: nowrap;\n line-height: initial;\n vertical-align: middle;\n}\n\n.selectic .more-items {\n display: inline-block;\n\n padding-left: 5px;\n padding-right: 5px;\n border-radius: 10px;\n\n background-color: var(--selectic-more-items-bg, var(--selectic-info-bg));\n color: var(--selectic-more-items-color, var(--selectic-info-color));\n cursor: help;\n}\n.selectic-input__selected-items__value {\n grid-area: value;\n align-self: center;\n justify-self: normal;\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n}\n\n.selectic-input__selected-items__icon {\n grid-area: icon;\n align-self: center;\n justify-self: center;\n margin-left: 5px;\n}\n.selectic-input__selected-items__icon:hover {\n color: var(--selectic-selected-item-color);\n}\n\n.selectic__label-disabled {\n opacity: 0.5;\n transition: opacity 400ms;\n}\n\n/* XXX: override padding of bootstrap input-sm.\n * This padding introduce a line shift. */\n.selectic.input-sm {\n padding: 0;\n}\n\n/* {{{ overflow multiline */\n\n.selectic--overflow-multiline,\n.selectic--overflow-multiline.form-control,\n.selectic--overflow-multiline .form-control {\n height: unset;\n}\n\n.selectic--overflow-multiline .selectic-input {\n overflow: unset;\n}\n\n.selectic--overflow-multiline .selectic-input__selected-items {\n flex-wrap: wrap;\n}\n\n/* }}} */\n";
|
|
36
36
|
styleInject(css_248z);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
/**
|
|
39
|
+
* Clone the object and its inner properties.
|
|
40
|
+
* @param obj The object to be clone.
|
|
41
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
42
|
+
* @returns a copy of obj
|
|
42
43
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
function deepClone(origObject, ignoreAttributes = [], refs = new WeakMap()) {
|
|
45
|
+
const obj = vue.unref(origObject);
|
|
46
|
+
/* For circular references */
|
|
47
|
+
if (refs.has(obj)) {
|
|
48
|
+
return refs.get(obj);
|
|
49
|
+
}
|
|
50
|
+
if (typeof obj === 'object') {
|
|
51
|
+
if (obj === null) {
|
|
52
|
+
return obj;
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(obj)) {
|
|
55
|
+
const ref = [];
|
|
56
|
+
refs.set(obj, ref);
|
|
57
|
+
obj.forEach((val, idx) => {
|
|
58
|
+
ref[idx] = deepClone(val, ignoreAttributes, refs);
|
|
59
|
+
});
|
|
60
|
+
return ref;
|
|
61
|
+
}
|
|
62
|
+
if (obj instanceof RegExp) {
|
|
63
|
+
const ref = new RegExp(obj.source, obj.flags);
|
|
64
|
+
refs.set(obj, ref);
|
|
65
|
+
return ref;
|
|
66
|
+
}
|
|
67
|
+
/* This should be an object */
|
|
68
|
+
const ref = {};
|
|
69
|
+
refs.set(obj, ref);
|
|
70
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
71
|
+
if (ignoreAttributes.includes(key)) {
|
|
72
|
+
ref[key] = val;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
ref[key] = deepClone(val, ignoreAttributes, refs);
|
|
76
|
+
}
|
|
77
|
+
return ref;
|
|
78
|
+
}
|
|
79
|
+
/* This should be a primitive */
|
|
80
|
+
return obj;
|
|
81
|
+
}
|
|
45
82
|
/**
|
|
46
83
|
* Escape search string to consider regexp special characters as they
|
|
47
84
|
* are and not like special characters.
|
|
@@ -72,6 +109,12 @@ function assignObject(obj, ...sourceObjects) {
|
|
|
72
109
|
}
|
|
73
110
|
return result;
|
|
74
111
|
}
|
|
112
|
+
|
|
113
|
+
/* File Purpose:
|
|
114
|
+
* It keeps and computes all states at a single place.
|
|
115
|
+
* Every inner components of Selectic should communicate with this file to
|
|
116
|
+
* change or to get states.
|
|
117
|
+
*/
|
|
75
118
|
/* }}} */
|
|
76
119
|
/* {{{ Static */
|
|
77
120
|
function changeTexts$1(texts) {
|
|
@@ -253,10 +296,10 @@ class SelecticStore {
|
|
|
253
296
|
this.setAutomaticClose();
|
|
254
297
|
this.commit('isOpen', false);
|
|
255
298
|
};
|
|
256
|
-
const value = this.props.value;
|
|
299
|
+
const value = deepClone(this.props.value);
|
|
257
300
|
/* set initial value for non reactive attribute */
|
|
258
301
|
this.cacheRequest = new Map();
|
|
259
|
-
const stateParam =
|
|
302
|
+
const stateParam = deepClone(this.props.params);
|
|
260
303
|
if (stateParam.optionBehavior) {
|
|
261
304
|
this.buildOptionBehavior(stateParam.optionBehavior, stateParam);
|
|
262
305
|
delete stateParam.optionBehavior;
|
|
@@ -646,7 +689,7 @@ class SelecticStore {
|
|
|
646
689
|
}
|
|
647
690
|
/* This method is for the computed property listOptions */
|
|
648
691
|
getListOptions() {
|
|
649
|
-
const options = this.props.options;
|
|
692
|
+
const options = deepClone(this.props.options, ['data']);
|
|
650
693
|
const listOptions = [];
|
|
651
694
|
if (!Array.isArray(options)) {
|
|
652
695
|
return listOptions;
|
|
@@ -683,7 +726,7 @@ class SelecticStore {
|
|
|
683
726
|
}
|
|
684
727
|
/* This method is for the computed property elementOptions */
|
|
685
728
|
getElementOptions() {
|
|
686
|
-
const options = this.props.childOptions;
|
|
729
|
+
const options = deepClone(this.props.childOptions, ['data']);
|
|
687
730
|
const childOptions = [];
|
|
688
731
|
if (!Array.isArray(options) || options.length === 0) {
|
|
689
732
|
return childOptions;
|
|
@@ -2076,7 +2119,7 @@ let Selectic = class Selectic extends vtyx.Vue {
|
|
|
2076
2119
|
}];
|
|
2077
2120
|
}
|
|
2078
2121
|
get hasGivenValue() {
|
|
2079
|
-
const value = this.value;
|
|
2122
|
+
const value = vue.unref(this.value);
|
|
2080
2123
|
return value !== null && value !== undefined;
|
|
2081
2124
|
}
|
|
2082
2125
|
get defaultValue() {
|
|
@@ -2221,7 +2264,7 @@ let Selectic = class Selectic extends vtyx.Vue {
|
|
|
2221
2264
|
this.store.props.selectionIsExcluded = this.selectionIsExcluded;
|
|
2222
2265
|
}
|
|
2223
2266
|
onOptionsChange() {
|
|
2224
|
-
this.store.props.options =
|
|
2267
|
+
this.store.props.options = deepClone(this.options);
|
|
2225
2268
|
}
|
|
2226
2269
|
onTextsChange() {
|
|
2227
2270
|
const texts = this.texts;
|
|
@@ -2396,12 +2439,12 @@ let Selectic = class Selectic extends vtyx.Vue {
|
|
|
2396
2439
|
var _a, _b, _c;
|
|
2397
2440
|
this._elementsListeners = [];
|
|
2398
2441
|
this.store = new SelecticStore({
|
|
2399
|
-
options: this.options,
|
|
2400
|
-
value: this.value,
|
|
2442
|
+
options: deepClone(this.options),
|
|
2443
|
+
value: deepClone(this.value),
|
|
2401
2444
|
selectionIsExcluded: this.selectionIsExcluded,
|
|
2402
2445
|
disabled: this.disabled,
|
|
2403
2446
|
texts: this.texts,
|
|
2404
|
-
groups: this.groups,
|
|
2447
|
+
groups: deepClone(this.groups),
|
|
2405
2448
|
keepOpenWithOtherSelectic: !!this.params.keepOpenWithOtherSelectic,
|
|
2406
2449
|
params: {
|
|
2407
2450
|
multiple: ((_a = this.multiple) !== null && _a !== void 0 ? _a : false) !== false,
|
|
@@ -2568,13 +2611,7 @@ __decorate([
|
|
|
2568
2611
|
vtyx.Watch('store.state.internalValue', { deep: true })
|
|
2569
2612
|
], Selectic.prototype, "onInternalValueChange", null);
|
|
2570
2613
|
__decorate([
|
|
2571
|
-
vtyx.
|
|
2572
|
-
vtyx.Emit('change'),
|
|
2573
|
-
vtyx.Emit('open'),
|
|
2574
|
-
vtyx.Emit('focus'),
|
|
2575
|
-
vtyx.Emit('close'),
|
|
2576
|
-
vtyx.Emit('blur'),
|
|
2577
|
-
vtyx.Emit('item:click')
|
|
2614
|
+
vtyx.Emits(['input', 'change', 'open', 'focus', 'close', 'blur', 'item:click'])
|
|
2578
2615
|
], Selectic.prototype, "render", null);
|
|
2579
2616
|
Selectic = __decorate([
|
|
2580
2617
|
vtyx.Component
|
package/dist/selectic.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Prop, Watch, Component, Vue, h,
|
|
2
|
-
import { reactive, computed,
|
|
1
|
+
import { Prop, Watch, Component, Vue, h, Emits } from 'vtyx';
|
|
2
|
+
import { unref, reactive, computed, watch } from 'vue';
|
|
3
3
|
|
|
4
4
|
function styleInject(css, ref) {
|
|
5
5
|
if ( ref === void 0 ) ref = {};
|
|
@@ -31,13 +31,50 @@ function styleInject(css, ref) {
|
|
|
31
31
|
var css_248z = "/* {{{ Variables */\n\n:root {\n --selectic-font-size: 14px;\n --selectic-cursor-disabled: not-allowed;\n\n /* The main element */\n --selectic-color: #555555;\n --selectic-bg: #ffffff;\n\n /* The main element (when disabled) */\n --selectic-color-disabled: #787878;\n --selectic-bg-disabled: #eeeeee;\n\n /* The list */\n --selectic-panel-bg: #f0f0f0;\n --selectic-separator-bordercolor: #cccccc;\n /* --selectic-item-color: var(--selectic-color); /* Can be set in any CSS configuration */\n\n /* The current selected item */\n --selectic-selected-item-color: #428bca;\n\n /* When mouse is over items or by selecting with key arrows */\n --selectic-active-item-color: #ffffff;\n --selectic-active-item-bg: #66afe9;\n\n /* Selected values in main element */\n --selectic-value-bg: #f0f0f0;\n /* --selectic-more-items-bg: var(--selectic-info-bg); /* can be set in any CSS configuration */\n /* --selectic-more-items-color: var(--selectic-info-color); /* can be set in any CSS configuration */\n --selectic-more-items-bg-disabled: #cccccc;\n\n /* Information message */\n --selectic-info-bg: #5bc0de;\n --selectic-info-color: #ffffff;\n\n /* Error message */\n --selectic-error-bg: #b72c29;\n --selectic-error-color: #ffffff;\n\n /* XXX: Currently it is important to keep this size for a correct scroll\n * height estimation */\n --selectic-input-height: 30px;\n}\n\n/* }}} */\n/* {{{ Bootstrap equivalent style */\n\n.selectic .form-control {\n display: block;\n width: 100%;\n height: calc(var(--selectic-input-height) - 2px);\n font-size: var(--selectic-font-size);\n line-height: 1.42857143;\n color: var(--selectic-color);\n background-color: var(--selectic-bg);\n background-image: none;\n border: 1px solid var(--selectic-separator-bordercolor); /* should use a better variable */\n border-radius: 4px;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;\n}\n.selectic .has-feedback {\n position: relative;\n}\n.selectic .has-feedback .form-control {\n padding-right: calc(var(--selectic-input-height) + 4px);\n}\n\n.selectic .form-control-feedback.fa,\n.selectic .form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: calc(var(--selectic-input-height) + 4px);\n height: calc(var(--selectic-input-height) + 4px);\n line-height: var(--selectic-input-height);\n text-align: center;\n pointer-events: none;\n}\n\n.selectic .alert-info {\n background-color: var(--selectic-info-bg);\n color: var(--selectic-info-color);\n}\n\n.selectic .alert-danger {\n background-color: var(--selectic-error-bg);\n color: var(--selectic-error-color);\n}\n\n/* }}} */\n\n.selectic * {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\n.selectic.form-control {\n display: inline-block;\n padding: 0;\n cursor: pointer;\n border: unset;\n}\n\n.has-feedback .selectic__icon-container.form-control-feedback {\n right: 0;\n}\n\n/* The input which contains the selected value\n * XXX: This input should stay hidden behind other elements, but is \"visible\"\n * (in term of DOM point of view) in order to get and to trigger the `focus`\n * DOM event. */\n.selectic__input-value {\n position: fixed;\n opacity: 0;\n z-index: -1000;\n top: -100px;\n}\n\n/* XXX: .form-control has been added to this selector to improve priority and\n * override some rules of the original .form-control */\n.selectic-input.form-control {\n display: inline-flex;\n justify-content: space-between;\n overflow: hidden;\n width: 100%;\n min-height: var(--selectic-input-height);\n padding-top: 0;\n padding-bottom: 0;\n padding-left: 5px;\n line-height: calc(var(--selectic-input-height) - 4px);\n color: var(--selectic-color);\n}\n\n.selectic-input__reverse-icon {\n align-self: center;\n margin-right: 3px;\n cursor: default;\n}\n.selectic-input__clear-icon {\n align-self: center;\n margin-left: 3px;\n cursor: pointer;\n}\n.selectic-input__clear-icon:hover {\n color: var(--selectic-selected-item-color);\n}\n\n.selectic-input.focused {\n border-bottom-left-radius: 0px;\n border-bottom-right-radius: 0px;\n}\n\n.selectic-input.disabled {\n cursor: var(--selectic-cursor-disabled);\n background-color: var(--selectic-bg-disabled);\n}\n.selectic-input.disabled .more-items {\n\tbackground-color: var(--selectic-more-items-bg-disabled);\n}\n\n.selectic-input__selected-items {\n display: inline-flex;\n flex-wrap: nowrap;\n align-items: center;\n white-space: nowrap;\n}\n\n.selectic-input__selected-items__placeholder {\n font-style: italic;\n opacity: 0.7;\n white-space: nowrap;\n}\n\n.selectic-icon {\n color: var(--selectic-color);\n text-align: center;\n vertical-align: middle;\n}\n\n.selectic__extended-list {\n position: fixed;\n z-index: 2000;\n height: auto;\n background-color: var(--selectic-bg, #ffffff);\n box-shadow: 2px 5px 12px 0px #888888;\n border-radius: 0 0 4px 4px;\n padding: 0;\n min-width: 200px;\n}\n.selectic__extended-list__list-items {\n max-height: calc(var(--selectic-input-height) * 10);\n overflow: auto;\n padding-left: 0;\n}\n\n.selectic-item {\n display: block;\n position: relative;\n box-sizing: border-box;\n padding: 2px 8px;\n color: var(--selectic-item-color, var(--selectic-color));\n min-height: calc(var(--selectic-input-height) - 3px);\n list-style-type: none;\n white-space: nowrap;\n cursor: pointer;\n}\n\n.selectic-item_text {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n.selectic-item:not(.selected) .selectic-item_icon {\n opacity: 0;\n}\n\n.selectic-item_text {\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n.selectic-item__active {\n background-color: var(--selectic-active-item-bg);\n color: var(--selectic-active-item-color);\n}\n.selectic-item__active:not(.selected) .selectic-item_icon {\n opacity: 0.2;\n}\n\n.selectic-item__disabled {\n color: var(--selectic-color-disabled);\n background-color: var(--selectic-bg-disabled);\n}\n\n.selectic-item__is-in-group {\n padding-left: 2em;\n}\n\n.selectic-item__is-group {\n font-weight: bold;\n cursor: default;\n}\n\n.selectic-item.selected {\n color: var(--selectic-selected-item-color);\n}\n.selectic-search-scope {\n color: #e0e0e0;\n left: auto;\n right: 10px;\n}\n\n.selectic__message {\n text-align: center;\n padding: 3px;\n}\n\n.selectic .filter-panel {\n padding: 3px;\n margin-left: 0px;\n margin-right: 0px;\n background-color: var(--selectic-panel-bg);\n border-bottom: 1px solid var(--selectic-separator-bordercolor);\n}\n\n.selectic .panelclosed {\n max-height: 0px;\n transition: max-height 0.3s ease-out;\n overflow: hidden;\n}\n\n.panelopened {\n max-height: 200px;\n transition: max-height 0.3s ease-in;\n overflow: hidden;\n}\n\n.selectic .filter-panel__input {\n padding-left: 0px;\n padding-right: 0px;\n padding-bottom: 10px;\n margin-bottom: 0px;\n}\n.selectic .filter-input {\n height: calc(var(--selectic-input-height) * 0.75);\n}\n\n.selectic .checkbox-filter {\n padding: 5px;\n text-align: center;\n}\n\n.selectic .curtain-handler {\n text-align: center;\n}\n\n.selectic .toggle-selectic {\n margin: 5px;\n padding-left: 0px;\n padding-right: 0px;\n}\n\n.selectic .toggle-boolean-select-all-toggle {\n display: inline;\n margin-right: 15px;\n}\n\n.selectic .toggle-boolean-excluding-toggle {\n display: inline;\n margin-right: 15px;\n}\n\n.selectic .single-value {\n display: grid;\n grid-template: \"value icon\" 1fr / max-content max-content;\n\n padding: 2px;\n padding-left: 5px;\n margin-left: 0;\n margin-right: 5px;\n /* margin top/bottom are mainly to create a gutter in multilines */\n margin-top: 2px;\n margin-bottom: 2px;\n\n border-radius: 3px;\n background-color: var(--selectic-value-bg);\n max-height: calc(var(--selectic-input-height) - 10px);\n max-width: 100%;\n min-width: 30px;\n\n overflow: hidden;\n white-space: nowrap;\n line-height: initial;\n vertical-align: middle;\n}\n\n.selectic .more-items {\n display: inline-block;\n\n padding-left: 5px;\n padding-right: 5px;\n border-radius: 10px;\n\n background-color: var(--selectic-more-items-bg, var(--selectic-info-bg));\n color: var(--selectic-more-items-color, var(--selectic-info-color));\n cursor: help;\n}\n.selectic-input__selected-items__value {\n grid-area: value;\n align-self: center;\n justify-self: normal;\n text-overflow: ellipsis;\n overflow: hidden;\n white-space: nowrap;\n}\n\n.selectic-input__selected-items__icon {\n grid-area: icon;\n align-self: center;\n justify-self: center;\n margin-left: 5px;\n}\n.selectic-input__selected-items__icon:hover {\n color: var(--selectic-selected-item-color);\n}\n\n.selectic__label-disabled {\n opacity: 0.5;\n transition: opacity 400ms;\n}\n\n/* XXX: override padding of bootstrap input-sm.\n * This padding introduce a line shift. */\n.selectic.input-sm {\n padding: 0;\n}\n\n/* {{{ overflow multiline */\n\n.selectic--overflow-multiline,\n.selectic--overflow-multiline.form-control,\n.selectic--overflow-multiline .form-control {\n height: unset;\n}\n\n.selectic--overflow-multiline .selectic-input {\n overflow: unset;\n}\n\n.selectic--overflow-multiline .selectic-input__selected-items {\n flex-wrap: wrap;\n}\n\n/* }}} */\n";
|
|
32
32
|
styleInject(css_248z);
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
34
|
+
/**
|
|
35
|
+
* Clone the object and its inner properties.
|
|
36
|
+
* @param obj The object to be clone.
|
|
37
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
38
|
+
* @returns a copy of obj
|
|
38
39
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
function deepClone(origObject, ignoreAttributes = [], refs = new WeakMap()) {
|
|
41
|
+
const obj = unref(origObject);
|
|
42
|
+
/* For circular references */
|
|
43
|
+
if (refs.has(obj)) {
|
|
44
|
+
return refs.get(obj);
|
|
45
|
+
}
|
|
46
|
+
if (typeof obj === 'object') {
|
|
47
|
+
if (obj === null) {
|
|
48
|
+
return obj;
|
|
49
|
+
}
|
|
50
|
+
if (Array.isArray(obj)) {
|
|
51
|
+
const ref = [];
|
|
52
|
+
refs.set(obj, ref);
|
|
53
|
+
obj.forEach((val, idx) => {
|
|
54
|
+
ref[idx] = deepClone(val, ignoreAttributes, refs);
|
|
55
|
+
});
|
|
56
|
+
return ref;
|
|
57
|
+
}
|
|
58
|
+
if (obj instanceof RegExp) {
|
|
59
|
+
const ref = new RegExp(obj.source, obj.flags);
|
|
60
|
+
refs.set(obj, ref);
|
|
61
|
+
return ref;
|
|
62
|
+
}
|
|
63
|
+
/* This should be an object */
|
|
64
|
+
const ref = {};
|
|
65
|
+
refs.set(obj, ref);
|
|
66
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
67
|
+
if (ignoreAttributes.includes(key)) {
|
|
68
|
+
ref[key] = val;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
ref[key] = deepClone(val, ignoreAttributes, refs);
|
|
72
|
+
}
|
|
73
|
+
return ref;
|
|
74
|
+
}
|
|
75
|
+
/* This should be a primitive */
|
|
76
|
+
return obj;
|
|
77
|
+
}
|
|
41
78
|
/**
|
|
42
79
|
* Escape search string to consider regexp special characters as they
|
|
43
80
|
* are and not like special characters.
|
|
@@ -68,6 +105,12 @@ function assignObject(obj, ...sourceObjects) {
|
|
|
68
105
|
}
|
|
69
106
|
return result;
|
|
70
107
|
}
|
|
108
|
+
|
|
109
|
+
/* File Purpose:
|
|
110
|
+
* It keeps and computes all states at a single place.
|
|
111
|
+
* Every inner components of Selectic should communicate with this file to
|
|
112
|
+
* change or to get states.
|
|
113
|
+
*/
|
|
71
114
|
/* }}} */
|
|
72
115
|
/* {{{ Static */
|
|
73
116
|
function changeTexts$1(texts) {
|
|
@@ -249,10 +292,10 @@ class SelecticStore {
|
|
|
249
292
|
this.setAutomaticClose();
|
|
250
293
|
this.commit('isOpen', false);
|
|
251
294
|
};
|
|
252
|
-
const value = this.props.value;
|
|
295
|
+
const value = deepClone(this.props.value);
|
|
253
296
|
/* set initial value for non reactive attribute */
|
|
254
297
|
this.cacheRequest = new Map();
|
|
255
|
-
const stateParam =
|
|
298
|
+
const stateParam = deepClone(this.props.params);
|
|
256
299
|
if (stateParam.optionBehavior) {
|
|
257
300
|
this.buildOptionBehavior(stateParam.optionBehavior, stateParam);
|
|
258
301
|
delete stateParam.optionBehavior;
|
|
@@ -642,7 +685,7 @@ class SelecticStore {
|
|
|
642
685
|
}
|
|
643
686
|
/* This method is for the computed property listOptions */
|
|
644
687
|
getListOptions() {
|
|
645
|
-
const options = this.props.options;
|
|
688
|
+
const options = deepClone(this.props.options, ['data']);
|
|
646
689
|
const listOptions = [];
|
|
647
690
|
if (!Array.isArray(options)) {
|
|
648
691
|
return listOptions;
|
|
@@ -679,7 +722,7 @@ class SelecticStore {
|
|
|
679
722
|
}
|
|
680
723
|
/* This method is for the computed property elementOptions */
|
|
681
724
|
getElementOptions() {
|
|
682
|
-
const options = this.props.childOptions;
|
|
725
|
+
const options = deepClone(this.props.childOptions, ['data']);
|
|
683
726
|
const childOptions = [];
|
|
684
727
|
if (!Array.isArray(options) || options.length === 0) {
|
|
685
728
|
return childOptions;
|
|
@@ -2072,7 +2115,7 @@ let Selectic = class Selectic extends Vue {
|
|
|
2072
2115
|
}];
|
|
2073
2116
|
}
|
|
2074
2117
|
get hasGivenValue() {
|
|
2075
|
-
const value = this.value;
|
|
2118
|
+
const value = unref(this.value);
|
|
2076
2119
|
return value !== null && value !== undefined;
|
|
2077
2120
|
}
|
|
2078
2121
|
get defaultValue() {
|
|
@@ -2217,7 +2260,7 @@ let Selectic = class Selectic extends Vue {
|
|
|
2217
2260
|
this.store.props.selectionIsExcluded = this.selectionIsExcluded;
|
|
2218
2261
|
}
|
|
2219
2262
|
onOptionsChange() {
|
|
2220
|
-
this.store.props.options =
|
|
2263
|
+
this.store.props.options = deepClone(this.options);
|
|
2221
2264
|
}
|
|
2222
2265
|
onTextsChange() {
|
|
2223
2266
|
const texts = this.texts;
|
|
@@ -2392,12 +2435,12 @@ let Selectic = class Selectic extends Vue {
|
|
|
2392
2435
|
var _a, _b, _c;
|
|
2393
2436
|
this._elementsListeners = [];
|
|
2394
2437
|
this.store = new SelecticStore({
|
|
2395
|
-
options: this.options,
|
|
2396
|
-
value: this.value,
|
|
2438
|
+
options: deepClone(this.options),
|
|
2439
|
+
value: deepClone(this.value),
|
|
2397
2440
|
selectionIsExcluded: this.selectionIsExcluded,
|
|
2398
2441
|
disabled: this.disabled,
|
|
2399
2442
|
texts: this.texts,
|
|
2400
|
-
groups: this.groups,
|
|
2443
|
+
groups: deepClone(this.groups),
|
|
2401
2444
|
keepOpenWithOtherSelectic: !!this.params.keepOpenWithOtherSelectic,
|
|
2402
2445
|
params: {
|
|
2403
2446
|
multiple: ((_a = this.multiple) !== null && _a !== void 0 ? _a : false) !== false,
|
|
@@ -2564,13 +2607,7 @@ __decorate([
|
|
|
2564
2607
|
Watch('store.state.internalValue', { deep: true })
|
|
2565
2608
|
], Selectic.prototype, "onInternalValueChange", null);
|
|
2566
2609
|
__decorate([
|
|
2567
|
-
|
|
2568
|
-
Emit('change'),
|
|
2569
|
-
Emit('open'),
|
|
2570
|
-
Emit('focus'),
|
|
2571
|
-
Emit('close'),
|
|
2572
|
-
Emit('blur'),
|
|
2573
|
-
Emit('item:click')
|
|
2610
|
+
Emits(['input', 'change', 'open', 'focus', 'close', 'blur', 'item:click'])
|
|
2574
2611
|
], Selectic.prototype, "render", null);
|
|
2575
2612
|
Selectic = __decorate([
|
|
2576
2613
|
Component
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "selectic",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.13",
|
|
4
4
|
"description": "Smart Select for VueJS 3.x",
|
|
5
5
|
"main": "dist/selectic.common.js",
|
|
6
6
|
"module": "dist/selectic.esm.js",
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"select",
|
|
20
20
|
"multiselect",
|
|
21
21
|
"multi-select",
|
|
22
|
+
"multi-select",
|
|
22
23
|
"dynamic select",
|
|
24
|
+
"smart select",
|
|
23
25
|
"vue",
|
|
24
26
|
"vue.js",
|
|
25
27
|
"vueJS",
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
"test": "npm run build && tape test/**/*.spec.js"
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
|
-
"vtyx": "4.0.
|
|
41
|
+
"vtyx": "4.0.4"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"@babel/types": "^7.16.7",
|
package/rollup.config.js
CHANGED
package/src/Store.tsx
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { reactive, watch, unref, computed, ComputedRef } from 'vue';
|
|
8
|
+
import { convertToRegExp, assignObject, deepClone } from './tools';
|
|
8
9
|
|
|
9
10
|
/* {{{ Types definitions */
|
|
10
11
|
|
|
@@ -336,42 +337,6 @@ interface Messages {
|
|
|
336
337
|
|
|
337
338
|
export type PartialMessages = { [K in keyof Messages]?: Messages[K] };
|
|
338
339
|
|
|
339
|
-
/* }}} */
|
|
340
|
-
/* {{{ Helper */
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Escape search string to consider regexp special characters as they
|
|
344
|
-
* are and not like special characters.
|
|
345
|
-
* Consider * characters as a wildcards characters (meanings 0 or
|
|
346
|
-
* more characters) and convert them to .* (the wildcard characters
|
|
347
|
-
* in Regexp)
|
|
348
|
-
*
|
|
349
|
-
* @param {String} name the original string to convert
|
|
350
|
-
* @param {String} [flag] mode to apply for regExp
|
|
351
|
-
* @return {String} the string ready to use for RegExp format
|
|
352
|
-
*/
|
|
353
|
-
function convertToRegExp(name: string, flag = 'i'): RegExp {
|
|
354
|
-
const pattern = name.replace(/[\\^$.+?(){}[\]|]/g, '\\$&')
|
|
355
|
-
.replace(/\*/g, '.*');
|
|
356
|
-
|
|
357
|
-
return new RegExp(pattern, flag);
|
|
358
|
-
}
|
|
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
340
|
/* }}} */
|
|
376
341
|
/* {{{ Static */
|
|
377
342
|
|
|
@@ -622,13 +587,13 @@ export default class SelecticStore {
|
|
|
622
587
|
this.commit('isOpen', false);
|
|
623
588
|
}
|
|
624
589
|
|
|
625
|
-
const value = this.props.value;
|
|
590
|
+
const value = deepClone(this.props.value);
|
|
626
591
|
|
|
627
592
|
/* set initial value for non reactive attribute */
|
|
628
593
|
this.cacheRequest = new Map();
|
|
629
594
|
|
|
630
595
|
const stateParam: SelecticStoreStateParams | SelecticStoreState =
|
|
631
|
-
|
|
596
|
+
deepClone(this.props.params);
|
|
632
597
|
|
|
633
598
|
if (stateParam.optionBehavior) {
|
|
634
599
|
this.buildOptionBehavior(
|
|
@@ -1095,7 +1060,7 @@ export default class SelecticStore {
|
|
|
1095
1060
|
|
|
1096
1061
|
/* This method is for the computed property listOptions */
|
|
1097
1062
|
private getListOptions(): OptionValue[] {
|
|
1098
|
-
const options = this.props.options;
|
|
1063
|
+
const options = deepClone(this.props.options, ['data']);
|
|
1099
1064
|
const listOptions: OptionValue[] = [];
|
|
1100
1065
|
|
|
1101
1066
|
if (!Array.isArray(options)) {
|
|
@@ -1141,7 +1106,7 @@ export default class SelecticStore {
|
|
|
1141
1106
|
|
|
1142
1107
|
/* This method is for the computed property elementOptions */
|
|
1143
1108
|
private getElementOptions(): OptionValue[] {
|
|
1144
|
-
const options = this.props.childOptions;
|
|
1109
|
+
const options = deepClone(this.props.childOptions, ['data']);
|
|
1145
1110
|
const childOptions: OptionValue[] = [];
|
|
1146
1111
|
|
|
1147
1112
|
if (!Array.isArray(options) || options.length === 0) {
|
package/src/index.tsx
CHANGED
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
* close [component]: triggered when the list closes.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {Vue, Component,
|
|
21
|
+
import {Vue, Component, Emits, Prop, Watch, h} from 'vtyx';
|
|
22
|
+
import { unref } from 'vue';
|
|
22
23
|
import './css/selectic.css';
|
|
23
24
|
|
|
25
|
+
import { deepClone } from './tools';
|
|
26
|
+
|
|
24
27
|
import Store, {
|
|
25
28
|
changeTexts as storeChangeTexts,
|
|
26
29
|
OptionProp,
|
|
@@ -371,7 +374,7 @@ export default class Selectic extends Vue<Props> {
|
|
|
371
374
|
}
|
|
372
375
|
|
|
373
376
|
get hasGivenValue() {
|
|
374
|
-
const value = this.value;
|
|
377
|
+
const value = unref(this.value);
|
|
375
378
|
|
|
376
379
|
return value !== null && value !== undefined;
|
|
377
380
|
}
|
|
@@ -558,7 +561,7 @@ export default class Selectic extends Vue<Props> {
|
|
|
558
561
|
|
|
559
562
|
@Watch('options', { deep: true })
|
|
560
563
|
public onOptionsChange() {
|
|
561
|
-
this.store.props.options =
|
|
564
|
+
this.store.props.options = deepClone(this.options);
|
|
562
565
|
}
|
|
563
566
|
|
|
564
567
|
@Watch('texts', { deep: true })
|
|
@@ -779,12 +782,12 @@ export default class Selectic extends Vue<Props> {
|
|
|
779
782
|
this._elementsListeners = [];
|
|
780
783
|
|
|
781
784
|
this.store = new Store({
|
|
782
|
-
options: this.options,
|
|
783
|
-
value: this.value,
|
|
785
|
+
options: deepClone(this.options),
|
|
786
|
+
value: deepClone(this.value),
|
|
784
787
|
selectionIsExcluded: this.selectionIsExcluded,
|
|
785
788
|
disabled: this.disabled,
|
|
786
789
|
texts: this.texts,
|
|
787
|
-
groups: this.groups,
|
|
790
|
+
groups: deepClone(this.groups),
|
|
788
791
|
keepOpenWithOtherSelectic: !!this.params.keepOpenWithOtherSelectic,
|
|
789
792
|
params: {
|
|
790
793
|
multiple: (this.multiple ?? false) !== false,
|
|
@@ -857,13 +860,7 @@ export default class Selectic extends Vue<Props> {
|
|
|
857
860
|
|
|
858
861
|
/* }}} */
|
|
859
862
|
|
|
860
|
-
@
|
|
861
|
-
@Emit('change')
|
|
862
|
-
@Emit('open')
|
|
863
|
-
@Emit('focus')
|
|
864
|
-
@Emit('close')
|
|
865
|
-
@Emit('blur')
|
|
866
|
-
@Emit('item:click')
|
|
863
|
+
@Emits(['input', 'change', 'open', 'focus', 'close', 'blur', 'item:click'])
|
|
867
864
|
public render() {
|
|
868
865
|
const id = this.id || undefined;
|
|
869
866
|
const store = this.store;
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { unref } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clone the object and its inner properties.
|
|
5
|
+
* @param obj The object to be clone.
|
|
6
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
7
|
+
* @returns a copy of obj
|
|
8
|
+
*/
|
|
9
|
+
export function deepClone<T = any>(origObject: T, ignoreAttributes: string[] = [], refs: WeakMap<any, any> = new WeakMap()): T {
|
|
10
|
+
const obj = unref(origObject);
|
|
11
|
+
|
|
12
|
+
/* For circular references */
|
|
13
|
+
if (refs.has(obj)) {
|
|
14
|
+
return refs.get(obj);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (typeof obj === 'object') {
|
|
18
|
+
if (obj === null) {
|
|
19
|
+
return obj;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (Array.isArray(obj)) {
|
|
23
|
+
const ref: any[] = [];
|
|
24
|
+
refs.set(obj, ref);
|
|
25
|
+
obj.forEach((val, idx) => {
|
|
26
|
+
ref[idx] = deepClone(val, ignoreAttributes, refs);
|
|
27
|
+
});
|
|
28
|
+
return ref as unknown as T;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (obj instanceof RegExp) {
|
|
32
|
+
const ref = new RegExp(obj.source, obj.flags);
|
|
33
|
+
refs.set(obj, ref);
|
|
34
|
+
return ref as unknown as T;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* This should be an object */
|
|
38
|
+
const ref: any = {};
|
|
39
|
+
refs.set(obj, ref);
|
|
40
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
41
|
+
if (ignoreAttributes.includes(key)) {
|
|
42
|
+
ref[key] = val;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
ref[key] = deepClone(val, ignoreAttributes, refs);
|
|
47
|
+
}
|
|
48
|
+
return ref as unknown as T;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* This should be a primitive */
|
|
52
|
+
return obj;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Escape search string to consider regexp special characters as they
|
|
57
|
+
* are and not like special characters.
|
|
58
|
+
* Consider * characters as a wildcards characters (meanings 0 or
|
|
59
|
+
* more characters) and convert them to .* (the wildcard characters
|
|
60
|
+
* in Regexp)
|
|
61
|
+
*
|
|
62
|
+
* @param {String} name the original string to convert
|
|
63
|
+
* @param {String} [flag] mode to apply for regExp
|
|
64
|
+
* @return {String} the string ready to use for RegExp format
|
|
65
|
+
*/
|
|
66
|
+
export function convertToRegExp(name: string, flag = 'i'): RegExp {
|
|
67
|
+
const pattern = name.replace(/[\\^$.+?(){}[\]|]/g, '\\$&')
|
|
68
|
+
.replace(/\*/g, '.*');
|
|
69
|
+
|
|
70
|
+
return new RegExp(pattern, flag);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Does the same as Object.assign but does not replace if value is undefined */
|
|
74
|
+
export function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial<T>>): T {
|
|
75
|
+
const result = obj;
|
|
76
|
+
for (const source of sourceObjects) {
|
|
77
|
+
for (const key of Object.keys(source)) {
|
|
78
|
+
const value = source[key as keyof T];
|
|
79
|
+
if (value === undefined) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
result[key as keyof T] = value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result as T;
|
|
86
|
+
}
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**************************************************************************/
|
|
2
|
-
/* */
|
|
3
|
-
/* Copyright (C) INTERSEC SA */
|
|
4
|
-
/* */
|
|
5
|
-
/* Should you receive a copy of this source code, you must check you */
|
|
6
|
-
/* have a proper, written authorization of INTERSEC to hold it. If you */
|
|
7
|
-
/* don't have such an authorization, you must DELETE all source code */
|
|
8
|
-
/* files in your possession, and inform INTERSEC of the fact you obtain */
|
|
9
|
-
/* these files. Should you not comply to these terms, you can be */
|
|
10
|
-
/* prosecuted in the extent permitted by applicable law. */
|
|
11
|
-
/* */
|
|
12
|
-
/**************************************************************************/
|
|
13
|
-
|
|
14
1
|
const _ = require('../tools.js');
|
|
15
2
|
const {
|
|
16
3
|
getInitialState,
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**************************************************************************/
|
|
2
|
-
/* */
|
|
3
|
-
/* Copyright (C) INTERSEC SA */
|
|
4
|
-
/* */
|
|
5
|
-
/* Should you receive a copy of this source code, you must check you */
|
|
6
|
-
/* have a proper, written authorization of INTERSEC to hold it. If you */
|
|
7
|
-
/* don't have such an authorization, you must DELETE all source code */
|
|
8
|
-
/* files in your possession, and inform INTERSEC of the fact you obtain */
|
|
9
|
-
/* these files. Should you not comply to these terms, you can be */
|
|
10
|
-
/* prosecuted in the extent permitted by applicable law. */
|
|
11
|
-
/* */
|
|
12
|
-
/**************************************************************************/
|
|
13
|
-
|
|
14
1
|
const _ = require('../tools.js');
|
|
15
2
|
const {
|
|
16
3
|
getInitialState,
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**************************************************************************/
|
|
2
|
-
/* */
|
|
3
|
-
/* Copyright (C) INTERSEC SA */
|
|
4
|
-
/* */
|
|
5
|
-
/* Should you receive a copy of this source code, you must check you */
|
|
6
|
-
/* have a proper, written authorization of INTERSEC to hold it. If you */
|
|
7
|
-
/* don't have such an authorization, you must DELETE all source code */
|
|
8
|
-
/* files in your possession, and inform INTERSEC of the fact you obtain */
|
|
9
|
-
/* these files. Should you not comply to these terms, you can be */
|
|
10
|
-
/* prosecuted in the extent permitted by applicable law. */
|
|
11
|
-
/* */
|
|
12
|
-
/**************************************************************************/
|
|
13
|
-
|
|
14
1
|
const _ = require('../tools.js');
|
|
15
2
|
const {
|
|
16
3
|
buildFetchCb,
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**************************************************************************/
|
|
2
|
-
/* */
|
|
3
|
-
/* Copyright (C) INTERSEC SA */
|
|
4
|
-
/* */
|
|
5
|
-
/* Should you receive a copy of this source code, you must check you */
|
|
6
|
-
/* have a proper, written authorization of INTERSEC to hold it. If you */
|
|
7
|
-
/* don't have such an authorization, you must DELETE all source code */
|
|
8
|
-
/* files in your possession, and inform INTERSEC of the fact you obtain */
|
|
9
|
-
/* these files. Should you not comply to these terms, you can be */
|
|
10
|
-
/* prosecuted in the extent permitted by applicable law. */
|
|
11
|
-
/* */
|
|
12
|
-
/**************************************************************************/
|
|
13
|
-
|
|
14
1
|
const _ = require('../tools.js');
|
|
15
2
|
const {
|
|
16
3
|
getOptions,
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**************************************************************************/
|
|
2
|
-
/* */
|
|
3
|
-
/* Copyright (C) INTERSEC SA */
|
|
4
|
-
/* */
|
|
5
|
-
/* Should you receive a copy of this source code, you must check you */
|
|
6
|
-
/* have a proper, written authorization of INTERSEC to hold it. If you */
|
|
7
|
-
/* don't have such an authorization, you must DELETE all source code */
|
|
8
|
-
/* files in your possession, and inform INTERSEC of the fact you obtain */
|
|
9
|
-
/* these files. Should you not comply to these terms, you can be */
|
|
10
|
-
/* prosecuted in the extent permitted by applicable law. */
|
|
11
|
-
/* */
|
|
12
|
-
/**************************************************************************/
|
|
13
|
-
|
|
14
1
|
const _ = require('../tools.js');
|
|
15
2
|
const {
|
|
16
3
|
getOptions,
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**************************************************************************/
|
|
2
|
-
/* */
|
|
3
|
-
/* Copyright (C) INTERSEC SA */
|
|
4
|
-
/* */
|
|
5
|
-
/* Should you receive a copy of this source code, you must check you */
|
|
6
|
-
/* have a proper, written authorization of INTERSEC to hold it. If you */
|
|
7
|
-
/* don't have such an authorization, you must DELETE all source code */
|
|
8
|
-
/* files in your possession, and inform INTERSEC of the fact you obtain */
|
|
9
|
-
/* these files. Should you not comply to these terms, you can be */
|
|
10
|
-
/* prosecuted in the extent permitted by applicable law. */
|
|
11
|
-
/* */
|
|
12
|
-
/**************************************************************************/
|
|
13
|
-
|
|
14
1
|
const _ = require('../tools.js');
|
|
15
2
|
const {
|
|
16
3
|
getOptions,
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
const tape = require('tape');
|
|
2
|
+
const toolFile = require('../dist/tools.js');
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
assignObject,
|
|
6
|
+
convertToRegExp,
|
|
7
|
+
deepClone,
|
|
8
|
+
} = toolFile;
|
|
9
|
+
|
|
10
|
+
tape.test('assignObject()', (st) => {
|
|
11
|
+
st.test('behaves like Object.assign', (tst) => {
|
|
12
|
+
const deep1 = {
|
|
13
|
+
dp: 1,
|
|
14
|
+
};
|
|
15
|
+
const deep2 = {
|
|
16
|
+
dp: 2,
|
|
17
|
+
other: 'value',
|
|
18
|
+
};
|
|
19
|
+
const obj1 = {
|
|
20
|
+
a: 1,
|
|
21
|
+
b: 2,
|
|
22
|
+
c: false,
|
|
23
|
+
deep: deep1,
|
|
24
|
+
};
|
|
25
|
+
const obj2 = {
|
|
26
|
+
a: 3,
|
|
27
|
+
c: true,
|
|
28
|
+
d: false,
|
|
29
|
+
deep: deep2,
|
|
30
|
+
other: {
|
|
31
|
+
attr: 'str',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const result = assignObject(obj1, obj2);
|
|
36
|
+
|
|
37
|
+
tst.is(result, obj1, 'should modify first argument');
|
|
38
|
+
tst.deepEqual(result, {
|
|
39
|
+
a: 3,
|
|
40
|
+
b: 2,
|
|
41
|
+
c: true,
|
|
42
|
+
d: false,
|
|
43
|
+
deep: {
|
|
44
|
+
dp: 2,
|
|
45
|
+
other: 'value',
|
|
46
|
+
},
|
|
47
|
+
other: {
|
|
48
|
+
attr: 'str',
|
|
49
|
+
},
|
|
50
|
+
}, 'should merge all attributes');
|
|
51
|
+
tst.is(result.deep, deep2, 'should keep references');
|
|
52
|
+
tst.deepEqual(obj2, {
|
|
53
|
+
a: 3,
|
|
54
|
+
c: true,
|
|
55
|
+
d: false,
|
|
56
|
+
deep: deep2,
|
|
57
|
+
other: {
|
|
58
|
+
attr: 'str',
|
|
59
|
+
},
|
|
60
|
+
}, 'should not change second argument');
|
|
61
|
+
tst.end();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
st.test('accept multiple arguments', (tst) => {
|
|
65
|
+
const obj1 = {
|
|
66
|
+
a: 1,
|
|
67
|
+
b: 2,
|
|
68
|
+
};
|
|
69
|
+
const obj2 = {
|
|
70
|
+
a: 2,
|
|
71
|
+
c: 3,
|
|
72
|
+
};
|
|
73
|
+
const obj3 = {
|
|
74
|
+
a: 3,
|
|
75
|
+
d: 4,
|
|
76
|
+
};
|
|
77
|
+
const obj4 = {
|
|
78
|
+
a: 4,
|
|
79
|
+
e: 5,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const result = assignObject(obj1, obj2, obj3, obj4);
|
|
83
|
+
|
|
84
|
+
tst.is(result, obj1, 'should modify first argument');
|
|
85
|
+
tst.deepEqual(result, {
|
|
86
|
+
a: 4,
|
|
87
|
+
b: 2,
|
|
88
|
+
c: 3,
|
|
89
|
+
d: 4,
|
|
90
|
+
e: 5,
|
|
91
|
+
}, 'should merge all attributes');
|
|
92
|
+
tst.deepEqual(obj2, {
|
|
93
|
+
a: 2,
|
|
94
|
+
c: 3,
|
|
95
|
+
}, 'should not change second argument');
|
|
96
|
+
tst.deepEqual(obj3, {
|
|
97
|
+
a: 3,
|
|
98
|
+
d: 4,
|
|
99
|
+
}, 'should not change third argument');
|
|
100
|
+
tst.deepEqual(obj4, {
|
|
101
|
+
a: 4,
|
|
102
|
+
e: 5,
|
|
103
|
+
}, 'should not change fourth argument');
|
|
104
|
+
tst.end();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
st.test('ignores undefined attributes', (tst) => {
|
|
108
|
+
const obj1 = {
|
|
109
|
+
a: 1, // modified by all
|
|
110
|
+
b: 2, // not modified by all (undefined)
|
|
111
|
+
c: 3, // obj2: undefined, modified by obj3
|
|
112
|
+
d: 4, // modified by obj2, obj3: undefined
|
|
113
|
+
e: 5, // not set by obj2, obj3: undefined
|
|
114
|
+
f: 6, // not set by all
|
|
115
|
+
};
|
|
116
|
+
const obj2 = {
|
|
117
|
+
a: 'a',
|
|
118
|
+
b: undefined,
|
|
119
|
+
c: undefined,
|
|
120
|
+
d: 'd',
|
|
121
|
+
};
|
|
122
|
+
const obj3 = {
|
|
123
|
+
a: 2,
|
|
124
|
+
b: undefined,
|
|
125
|
+
c: 'c',
|
|
126
|
+
d: undefined,
|
|
127
|
+
e: undefined,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const result = assignObject(obj1, obj2, obj3);
|
|
131
|
+
|
|
132
|
+
tst.is(result, obj1, 'should modify first argument');
|
|
133
|
+
tst.deepEqual(result, {
|
|
134
|
+
a: 2,
|
|
135
|
+
b: 2,
|
|
136
|
+
c: 'c',
|
|
137
|
+
d: 'd',
|
|
138
|
+
e: 5,
|
|
139
|
+
f: 6,
|
|
140
|
+
}, 'should merge all attributes');
|
|
141
|
+
tst.deepEqual(obj2, {
|
|
142
|
+
a: 'a',
|
|
143
|
+
b: undefined,
|
|
144
|
+
c: undefined,
|
|
145
|
+
d: 'd',
|
|
146
|
+
}, 'should not change second argument');
|
|
147
|
+
tst.deepEqual(obj3, {
|
|
148
|
+
a: 2,
|
|
149
|
+
b: undefined,
|
|
150
|
+
c: 'c',
|
|
151
|
+
d: undefined,
|
|
152
|
+
e: undefined,
|
|
153
|
+
}, 'should not change third argument');
|
|
154
|
+
tst.end();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
tape.test('convertToRegExp()', (st) => {
|
|
159
|
+
st.test('convert wildcard', (tst) => {
|
|
160
|
+
const str = '*the search*';
|
|
161
|
+
|
|
162
|
+
const result = convertToRegExp(str);
|
|
163
|
+
|
|
164
|
+
tst.is(result instanceof RegExp, true, 'should create a RegExp');
|
|
165
|
+
|
|
166
|
+
const pattern = result.source;
|
|
167
|
+
const flags = result.flags;
|
|
168
|
+
|
|
169
|
+
tst.is(pattern, '.*the search.*', 'should create wildcard sequence');
|
|
170
|
+
tst.is(flags, 'i', 'should be case insensitive by default');
|
|
171
|
+
|
|
172
|
+
tst.end();
|
|
173
|
+
});
|
|
174
|
+
st.test('escape special characters', (tst) => {
|
|
175
|
+
const str = '\\^$.+?(){}[]|';
|
|
176
|
+
|
|
177
|
+
const result = convertToRegExp(str);
|
|
178
|
+
|
|
179
|
+
tst.is(result instanceof RegExp, true, 'should create a RegExp');
|
|
180
|
+
|
|
181
|
+
const pattern = result.source;
|
|
182
|
+
const flags = result.flags;
|
|
183
|
+
|
|
184
|
+
tst.is(pattern, '\\\\\\^\\$\\.\\+\\?\\(\\)\\{\\}\\[\\]\\|', 'should escape special characters');
|
|
185
|
+
tst.is(flags, 'i', 'should be case insensitive by default');
|
|
186
|
+
|
|
187
|
+
tst.end();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
st.test('allow RegExp flags', (tst) => {
|
|
191
|
+
const str = 'file*.*';
|
|
192
|
+
|
|
193
|
+
const result = convertToRegExp(str, 'gm');
|
|
194
|
+
|
|
195
|
+
tst.is(result instanceof RegExp, true, 'should create a RegExp');
|
|
196
|
+
|
|
197
|
+
const pattern = result.source;
|
|
198
|
+
const flags = result.flags;
|
|
199
|
+
|
|
200
|
+
tst.is(pattern, 'file.*\\..*', 'should convert special characters');
|
|
201
|
+
tst.is(flags, 'gm', 'should set flags');
|
|
202
|
+
|
|
203
|
+
tst.end();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
tape.test('deepClone()', (st) => {
|
|
208
|
+
st.test('should copy simple object', (tst) => {
|
|
209
|
+
const fn = () => {};
|
|
210
|
+
const objRef = {
|
|
211
|
+
a: 1,
|
|
212
|
+
b: 'b',
|
|
213
|
+
c: false,
|
|
214
|
+
d: undefined,
|
|
215
|
+
e: null,
|
|
216
|
+
f: {},
|
|
217
|
+
g: fn,
|
|
218
|
+
spe1: NaN,
|
|
219
|
+
spe2: Infinity,
|
|
220
|
+
spe3: '',
|
|
221
|
+
spe4: [],
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const result = deepClone(objRef);
|
|
225
|
+
|
|
226
|
+
tst.isNot(result, objRef, 'should create a copy');
|
|
227
|
+
tst.deepEqual(result, objRef, 'should have been similar to original');
|
|
228
|
+
|
|
229
|
+
tst.end();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
st.test('should copy nested object', (tst) => {
|
|
233
|
+
const fn = () => {};
|
|
234
|
+
const deep1 = {
|
|
235
|
+
a: 1,
|
|
236
|
+
b: 'b',
|
|
237
|
+
c: false,
|
|
238
|
+
d: undefined,
|
|
239
|
+
e: null,
|
|
240
|
+
f: {},
|
|
241
|
+
g: fn,
|
|
242
|
+
spe1: NaN,
|
|
243
|
+
spe2: Infinity,
|
|
244
|
+
spe3: '',
|
|
245
|
+
spe4: [],
|
|
246
|
+
};
|
|
247
|
+
const deep2 = {
|
|
248
|
+
deep: deep1,
|
|
249
|
+
added: 'a value',
|
|
250
|
+
};
|
|
251
|
+
const objRef = {
|
|
252
|
+
d: deep2,
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const result = deepClone(objRef);
|
|
256
|
+
|
|
257
|
+
tst.isNot(result, objRef, 'should create a copy');
|
|
258
|
+
tst.deepEqual(result, objRef, 'should have been similar to original');
|
|
259
|
+
tst.isNot(result.d, deep2, 'should copy nested object');
|
|
260
|
+
tst.isNot(result.d.deep, deep1, 'should copy deeper nested object');
|
|
261
|
+
|
|
262
|
+
tst.end();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
st.test('with arrays', (tst) => {
|
|
267
|
+
const fn = () => {};
|
|
268
|
+
const objRef = {
|
|
269
|
+
a: 1,
|
|
270
|
+
b: 'b',
|
|
271
|
+
c: false,
|
|
272
|
+
d: undefined,
|
|
273
|
+
e: null,
|
|
274
|
+
f: {},
|
|
275
|
+
g: fn,
|
|
276
|
+
spe1: NaN,
|
|
277
|
+
spe2: Infinity,
|
|
278
|
+
spe3: '',
|
|
279
|
+
spe4: [],
|
|
280
|
+
};
|
|
281
|
+
const nestedArray1 = [
|
|
282
|
+
objRef,
|
|
283
|
+
{ a: 'value' },
|
|
284
|
+
null,
|
|
285
|
+
undefined,
|
|
286
|
+
42,
|
|
287
|
+
'value',
|
|
288
|
+
];
|
|
289
|
+
const nestedArray2 = [
|
|
290
|
+
objRef,
|
|
291
|
+
{ a: 'value' },
|
|
292
|
+
null,
|
|
293
|
+
undefined,
|
|
294
|
+
42,
|
|
295
|
+
'value',
|
|
296
|
+
];
|
|
297
|
+
const arrayRef = [
|
|
298
|
+
{
|
|
299
|
+
deep: nestedArray1,
|
|
300
|
+
},
|
|
301
|
+
nestedArray2,
|
|
302
|
+
42, 'value', null, undefined, [[]],
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
const result = deepClone(arrayRef);
|
|
306
|
+
|
|
307
|
+
tst.isNot(result, arrayRef, 'should create a copy');
|
|
308
|
+
tst.is(Array.isArray(arrayRef), true, 'should create an array');
|
|
309
|
+
tst.deepEqual(result, arrayRef, 'should have been similar to original');
|
|
310
|
+
tst.isNot(result[1], nestedArray2, 'should copy nested array');
|
|
311
|
+
tst.isNot(result[0].deep, nestedArray1, 'should copy deeper nested array');
|
|
312
|
+
tst.isNot(result[1][0], objRef, 'should copy nested object');
|
|
313
|
+
tst.isNot(result[0].deep[0], objRef, 'should copy deeper nested object');
|
|
314
|
+
|
|
315
|
+
tst.end();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
st.test('with RegExp', (tst) => {
|
|
319
|
+
const r1 = /hello?/gi;
|
|
320
|
+
const r2 = /.* [aA]+?/;
|
|
321
|
+
|
|
322
|
+
const result1 = deepClone(r1);
|
|
323
|
+
const result2 = deepClone({rgx: r2});
|
|
324
|
+
|
|
325
|
+
tst.isNot(result1, r1, 'should copy RegExp');
|
|
326
|
+
tst.is(result1 instanceof RegExp, true, 'should create a RegExp');
|
|
327
|
+
tst.is(result1.source, 'hello?', 'should copy pattern');
|
|
328
|
+
tst.is(result1.flags, 'gi', 'should copy flags');
|
|
329
|
+
|
|
330
|
+
tst.isNot(result2.rgx, r2, 'should copy the RegExp');
|
|
331
|
+
tst.is(result2.rgx.source, '.* [aA]+?', 'should copy the attribute pattern');
|
|
332
|
+
tst.is(result2.rgx.flags, '', 'should copy the attribute flags');
|
|
333
|
+
tst.end();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
st.test('with primitives', (tst) => {
|
|
337
|
+
const result1 = deepClone(1);
|
|
338
|
+
const result2 = deepClone('a');
|
|
339
|
+
const result3 = deepClone(false);
|
|
340
|
+
const result4 = deepClone(null);
|
|
341
|
+
const result5 = deepClone(undefined);
|
|
342
|
+
|
|
343
|
+
tst.is(result1, 1, 'should return number');
|
|
344
|
+
tst.is(result2, 'a', 'should return string');
|
|
345
|
+
tst.is(result3, false, 'should return boolean');
|
|
346
|
+
tst.is(result4, null, 'should return null');
|
|
347
|
+
tst.is(result5, undefined, 'should return undefined');
|
|
348
|
+
tst.end();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
st.test('with circular references', (tst) => {
|
|
352
|
+
const obj1 = {
|
|
353
|
+
a: 'a',
|
|
354
|
+
}
|
|
355
|
+
const obj2 = {
|
|
356
|
+
b: 'b',
|
|
357
|
+
}
|
|
358
|
+
obj1.child = obj1;
|
|
359
|
+
obj1.sibling = obj2;
|
|
360
|
+
obj2.sibling = obj1;
|
|
361
|
+
|
|
362
|
+
const ref = [obj1, obj2];
|
|
363
|
+
|
|
364
|
+
const result = deepClone(ref);
|
|
365
|
+
|
|
366
|
+
tst.isNot(result, ref, 'should create a new object');
|
|
367
|
+
tst.isNot(result[0], obj1, 'should copy inner objects');
|
|
368
|
+
tst.is(result[0].child, result[0], 'should keep the circular reference');
|
|
369
|
+
tst.is(result[1].sibling, result[0], 'should keep similar references');
|
|
370
|
+
tst.is(result[0].sibling, result[1], 'should keep similar references (2)');
|
|
371
|
+
|
|
372
|
+
tst.end();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
st.test('can ignore some attributes', (tst) => {
|
|
376
|
+
const noCopy1 = {
|
|
377
|
+
ref: 1,
|
|
378
|
+
};
|
|
379
|
+
const noCopy2 = new Set(['alpha', 'omega']);
|
|
380
|
+
const noCopy3 = new Map([[1, 'alpha'], [22, 'omega']]);
|
|
381
|
+
const deep1 = {
|
|
382
|
+
a: 'alpha',
|
|
383
|
+
noCopy: noCopy3,
|
|
384
|
+
};
|
|
385
|
+
const ref = {
|
|
386
|
+
id: 'ref',
|
|
387
|
+
noCopy: noCopy1,
|
|
388
|
+
nop: noCopy2,
|
|
389
|
+
not: 42,
|
|
390
|
+
deep: deep1,
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const result = deepClone(ref, ['noCopy', 'nothing', 'nop', 'not']);
|
|
394
|
+
|
|
395
|
+
tst.isNot(result, ref, 'should create a new object');
|
|
396
|
+
tst.isNot(result.deep, deep1, 'should create a new object');
|
|
397
|
+
tst.is(result.noCopy, noCopy1, 'should keep original reference');
|
|
398
|
+
tst.is(result.nop, noCopy2, 'should keep original reference (2)');
|
|
399
|
+
tst.is(result.not, 42, 'should keep primitive');
|
|
400
|
+
tst.is(result.deep.noCopy, noCopy3, 'should keep nested original reference');
|
|
401
|
+
|
|
402
|
+
tst.end();
|
|
403
|
+
});
|
|
404
|
+
});
|
package/types/tools.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clone the object and its inner properties.
|
|
3
|
+
* @param obj The object to be clone.
|
|
4
|
+
* @param refs internal reference to object to avoid cyclic references
|
|
5
|
+
* @returns a copy of obj
|
|
6
|
+
*/
|
|
7
|
+
export declare function deepClone<T = any>(origObject: T, ignoreAttributes?: string[], refs?: WeakMap<any, any>): T;
|
|
8
|
+
/**
|
|
9
|
+
* Escape search string to consider regexp special characters as they
|
|
10
|
+
* are and not like special characters.
|
|
11
|
+
* Consider * characters as a wildcards characters (meanings 0 or
|
|
12
|
+
* more characters) and convert them to .* (the wildcard characters
|
|
13
|
+
* in Regexp)
|
|
14
|
+
*
|
|
15
|
+
* @param {String} name the original string to convert
|
|
16
|
+
* @param {String} [flag] mode to apply for regExp
|
|
17
|
+
* @return {String} the string ready to use for RegExp format
|
|
18
|
+
*/
|
|
19
|
+
export declare function convertToRegExp(name: string, flag?: string): RegExp;
|
|
20
|
+
/** Does the same as Object.assign but does not replace if value is undefined */
|
|
21
|
+
export declare function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial<T>>): T;
|
package/.package.json.un~
DELETED
|
Binary file
|