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 CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  > VueJS 3.X + Typescript + JSX + Select
4
4
 
5
+ [![npm](https://img.shields.io/npm/v/selectic.svg)](https://www.npmjs.com/package/selectic)
5
6
 
6
7
  ## Introduction
7
8
 
@@ -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
- /* File Purpose:
39
- * It keeps and computes all states at a single place.
40
- * Every inner components of Selectic should communicate with this file to
41
- * change or to get states.
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
- /* {{{ Helper */
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 = Object.assign({}, this.props.params);
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 = Array.from(this.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.Emit('input'),
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
@@ -1,5 +1,5 @@
1
- import { Prop, Watch, Component, Vue, h, Emit } from 'vtyx';
2
- import { reactive, computed, unref, watch } from 'vue';
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
- /* File Purpose:
35
- * It keeps and computes all states at a single place.
36
- * Every inner components of Selectic should communicate with this file to
37
- * change or to get states.
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
- /* {{{ Helper */
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 = Object.assign({}, this.props.params);
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 = Array.from(this.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
- Emit('input'),
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.8",
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.3"
41
+ "vtyx": "4.0.4"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@babel/types": "^7.16.7",
package/rollup.config.js CHANGED
@@ -30,5 +30,12 @@ export default [{
30
30
  'vtyx',
31
31
  ],
32
32
  context: 'this',
33
+ }, {
34
+ input: 'lib/tools.js',
35
+ output: [{
36
+ file: 'test/dist/tools.js',
37
+ exports: 'named',
38
+ format: 'cjs',
39
+ }],
40
+ context: 'this',
33
41
  }];
34
-
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
- Object.assign({}, this.props.params);
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, Emit, Prop, Watch, h} from 'vtyx';
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 = Array.from(this.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
- @Emit('input')
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
+ });
@@ -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