selectic 3.1.4 → 3.2.0

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.
@@ -32,7 +32,7 @@ function styleInject(css, ref) {
32
32
  }
33
33
  }
34
34
 
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 top: var(--top-position, 0);\n z-index: 2000;\n height: auto;\n max-height: var(--availableSpace);\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 width: var(--list-width, 200px);\n min-width: 200px;\n display: grid;\n grid-template-rows: minmax(0, max-content) 1fr;\n}\n.selectic__extended-list.selectic-position-top {\n box-shadow: 2px -3px 12px 0px #888888;\n}\n.selectic__extended-list__list-container{\n overflow: auto;\n}\n.selectic__extended-list__list-items {\n max-height: calc(var(--selectic-input-height) * 10);\n min-width: max-content;\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.selectic-item__active.selectic-item__disabled:not(.selected) .selectic-item_icon {\n opacity: 0;\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__is-group.selectable {\n cursor: pointer;\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.selectic .form-control-feedback.fa.selectic-search-scope {\n width: calc(var(--selectic-input-height) * 0.75);\n height: calc(var(--selectic-input-height) * 0.75);\n line-height: calc(var(--selectic-input-height) * 0.75);\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/* {{{ icons */\n\n@keyframes selectic-animation-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(359deg);\n }\n}\n\n.selectic__icon {\n height: 1em;\n fill: currentColor;\n}\n\n.selectic-spin {\n animation: selectic-animation-spin 2s infinite linear;\n}\n\n/* }}} */\n";
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\n.selectic .has-feedback {\n position: relative;\n}\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\n.selectic-input__clear-icon {\n align-self: center;\n margin-left: 3px;\n cursor: pointer;\n}\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\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 top: var(--top-position, 0);\n z-index: 2000;\n height: auto;\n max-height: var(--availableSpace);\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 width: var(--list-width, 200px);\n min-width: 200px;\n display: grid;\n grid-template-rows: minmax(0, max-content) 1fr;\n}\n\n.selectic__extended-list.selectic-position-top {\n box-shadow: 2px -3px 12px 0px #888888;\n}\n\n.selectic__extended-list__list-container{\n overflow: auto;\n}\n\n.selectic__extended-list__list-items {\n max-height: calc(var(--selectic-input-height) * 10);\n min-width: max-content;\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\n.selectic-item__active:not(.selected) .selectic-item_icon {\n opacity: 0.2;\n}\n\n.selectic-item__active:not(.selected) .single-select_icon {\n opacity: 0;\n}\n\n.selectic-item__active.selectic-item__disabled:not(.selected) .selectic-item_icon {\n opacity: 0;\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__is-group.selectable {\n cursor: pointer;\n}\n\n.selectic-item.selected {\n color: var(--selectic-selected-item-color);\n}\n\n.selectic-search-scope {\n color: #e0e0e0;\n left: auto;\n right: 10px;\n}\n\n.selectic .form-control-feedback.fa.selectic-search-scope {\n width: calc(var(--selectic-input-height) * 0.75);\n height: calc(var(--selectic-input-height) * 0.75);\n line-height: calc(var(--selectic-input-height) * 0.75);\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\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\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\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/* {{{ icons */\n\n@keyframes selectic-animation-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(359deg);\n }\n}\n\n.selectic__icon {\n height: 1em;\n fill: currentColor;\n}\n\n.selectic-spin {\n animation: selectic-animation-spin 2s infinite linear;\n}\n\n/* }}} */\n";
36
36
  styleInject(css_248z);
37
37
 
38
38
  /**
@@ -111,32 +111,76 @@ function assignObject(obj, ...sourceObjects) {
111
111
  }
112
112
  return result;
113
113
  }
114
- /** Compare 2 list of options.
115
- * @returns true if there are no difference
114
+ /**
115
+ * Ckeck whether a value is primitive.
116
+ * @returns true if val is primitive and false otherwise.
116
117
  */
117
- function compareOptions(oldOptions, newOptions) {
118
- if (oldOptions.length !== newOptions.length) {
119
- return false;
120
- }
121
- return oldOptions.every((oldOption, idx) => {
122
- const newOption = newOptions[idx];
123
- const keys = Object.keys(oldOption);
124
- if (keys.length !== Object.keys(newOption).length) {
118
+ function isPrimitive(val) {
119
+ /* The value null is treated explicitly because in JavaScript
120
+ * `typeof null === 'object'` is evaluated to `true`.
121
+ */
122
+ return val === null || (typeof val !== 'object' && typeof val !== 'function');
123
+ }
124
+ /**
125
+ * Performs a deep comparison between two objects to determine if they
126
+ * should be considered equal.
127
+ *
128
+ * @param objA object to compare to objB.
129
+ * @param objB object to compare to objA.
130
+ * @param attributes list of attributes to not compare.
131
+ * @param refs internal reference to object to avoid cyclic references
132
+ * @returns true if objA should be considered equal to objB.
133
+ */
134
+ function isDeepEqual(objA, objB, ignoreAttributes = [], refs = new WeakMap()) {
135
+ objA = vue.unref(objA);
136
+ objB = vue.unref(objB);
137
+ /* For primitive types */
138
+ if (isPrimitive(objA)) {
139
+ return isPrimitive(objB) && Object.is(objA, objB);
140
+ }
141
+ /* For functions (follow the behavior of _.isEqual and compare functions
142
+ * by reference). */
143
+ if (typeof objA === 'function') {
144
+ return typeof objB === 'function' && objA === objB;
145
+ }
146
+ /* For circular references */
147
+ if (refs.has(objA)) {
148
+ return refs.get(objA) === objB;
149
+ }
150
+ refs.set(objA, objB);
151
+ /* For objects */
152
+ if (typeof objA === 'object') {
153
+ if (typeof objB !== 'object') {
125
154
  return false;
126
155
  }
127
- return keys.every((optionKey) => {
128
- const key = optionKey;
129
- const oldValue = oldOption[key];
130
- const newValue = newOption[key];
131
- if (key === 'options') {
132
- return compareOptions(oldValue, newValue);
133
- }
134
- if (key === 'data') {
135
- return JSON.stringify(oldValue) === JSON.stringify(newValue);
136
- }
137
- return oldValue === newValue;
156
+ /* For arrays */
157
+ if (Array.isArray(objA)) {
158
+ return Array.isArray(objB) &&
159
+ objA.length === objB.length &&
160
+ !objA.some((val, idx) => !isDeepEqual(val, objB[idx], ignoreAttributes, refs));
161
+ }
162
+ /* For RegExp */
163
+ if (objA instanceof RegExp) {
164
+ return objB instanceof RegExp &&
165
+ objA.source === objB.source &&
166
+ objA.flags === objB.flags;
167
+ }
168
+ /* For Date */
169
+ if (objA instanceof Date) {
170
+ return objB instanceof Date && objA.getTime() === objB.getTime();
171
+ }
172
+ /* This should be an object */
173
+ const aRec = objA;
174
+ const bRec = objB;
175
+ const aKeys = Object.keys(aRec).filter((key) => !ignoreAttributes.includes(key));
176
+ const bKeys = Object.keys(bRec).filter((key) => !ignoreAttributes.includes(key));
177
+ const differentKeyFound = aKeys.some((key) => {
178
+ return !bKeys.includes(key) ||
179
+ !isDeepEqual(aRec[key], bRec[key], ignoreAttributes, refs);
138
180
  });
139
- });
181
+ return aKeys.length === bKeys.length && !differentKeyFound;
182
+ }
183
+ return true;
140
184
  }
141
185
  let displayLog = false;
142
186
  function debug(fName, step, ...args) {
@@ -1190,7 +1234,7 @@ class SelecticStore {
1190
1234
  /* update cache */
1191
1235
  state.totalDynOptions = total;
1192
1236
  const old = state.dynOptions.splice(offset, result.length, ...result);
1193
- if (compareOptions(old, result)) {
1237
+ if (isDeepEqual(old, result)) {
1194
1238
  /* Added options are the same as previous ones.
1195
1239
  * Stop fetching to avoid infinite loop
1196
1240
  */
@@ -1493,7 +1537,7 @@ class SelecticStore {
1493
1537
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1494
1538
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1495
1539
  */
1496
- var __decorate$d = (this && this.__decorate) || function (decorators, target, key, desc) {
1540
+ var __decorate$e = (this && this.__decorate) || function (decorators, target, key, desc) {
1497
1541
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1498
1542
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1499
1543
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1505,7 +1549,7 @@ let IconCaretDown = class IconCaretDown extends vtyx.Vue {
1505
1549
  vtyx.h("path", { d: "M7,10L12,15L17,10H7Z" })));
1506
1550
  }
1507
1551
  };
1508
- IconCaretDown = __decorate$d([
1552
+ IconCaretDown = __decorate$e([
1509
1553
  vtyx.Component
1510
1554
  ], IconCaretDown);
1511
1555
  var IconCaretDown$1 = IconCaretDown;
@@ -1513,7 +1557,7 @@ var IconCaretDown$1 = IconCaretDown;
1513
1557
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1514
1558
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1515
1559
  */
1516
- var __decorate$c = (this && this.__decorate) || function (decorators, target, key, desc) {
1560
+ var __decorate$d = (this && this.__decorate) || function (decorators, target, key, desc) {
1517
1561
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1518
1562
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1519
1563
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1525,7 +1569,7 @@ let IconCaretUp = class IconCaretUp extends vtyx.Vue {
1525
1569
  vtyx.h("path", { d: "M7,15L12,10L17,15H7Z" })));
1526
1570
  }
1527
1571
  };
1528
- IconCaretUp = __decorate$c([
1572
+ IconCaretUp = __decorate$d([
1529
1573
  vtyx.Component
1530
1574
  ], IconCaretUp);
1531
1575
  var IconCaretUp$1 = IconCaretUp;
@@ -1533,7 +1577,7 @@ var IconCaretUp$1 = IconCaretUp;
1533
1577
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1534
1578
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1535
1579
  */
1536
- var __decorate$b = (this && this.__decorate) || function (decorators, target, key, desc) {
1580
+ var __decorate$c = (this && this.__decorate) || function (decorators, target, key, desc) {
1537
1581
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1538
1582
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1539
1583
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1545,11 +1589,28 @@ let IconCheck = class IconCheck extends vtyx.Vue {
1545
1589
  vtyx.h("path", { d: "M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z" })));
1546
1590
  }
1547
1591
  };
1548
- IconCheck = __decorate$b([
1592
+ IconCheck = __decorate$c([
1549
1593
  vtyx.Component
1550
1594
  ], IconCheck);
1551
1595
  var IconCheck$1 = IconCheck;
1552
1596
 
1597
+ var __decorate$b = (this && this.__decorate) || function (decorators, target, key, desc) {
1598
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1599
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1600
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1601
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1602
+ };
1603
+ let IconDot = class IconDot extends vtyx.Vue {
1604
+ render() {
1605
+ return (vtyx.h("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" },
1606
+ vtyx.h("circle", { cx: "8", cy: "16", r: "3.5" })));
1607
+ }
1608
+ };
1609
+ IconDot = __decorate$b([
1610
+ vtyx.Component
1611
+ ], IconDot);
1612
+ var IconDot$1 = IconDot;
1613
+
1553
1614
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1554
1615
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1555
1616
  */
@@ -1698,6 +1759,8 @@ let Icon = class Icon extends vtyx.Vue {
1698
1759
  return IconCaretUp$1;
1699
1760
  case 'check':
1700
1761
  return IconCheck$1;
1762
+ case 'dot':
1763
+ return IconDot$1;
1701
1764
  case 'search':
1702
1765
  return IconSearch$1;
1703
1766
  case 'spinner':
@@ -2452,8 +2515,10 @@ let List = class List extends vtyx.Vue {
2452
2515
  onOffsetChange() {
2453
2516
  this.checkOffset();
2454
2517
  }
2455
- onFilteredOptionsChange() {
2456
- this.checkOffset();
2518
+ onFilteredOptionsChange(oldVal, newVal) {
2519
+ if (!isDeepEqual(oldVal, newVal)) {
2520
+ this.checkOffset();
2521
+ }
2457
2522
  }
2458
2523
  onGroupIdChange() {
2459
2524
  this.$emit('groupId', this.groupId);
@@ -2483,6 +2548,7 @@ let List = class List extends vtyx.Vue {
2483
2548
  'selectic-item__is-group': option.isGroup,
2484
2549
  }], style: option.style, title: option.title, key: 'selectic-item-' + (idx + this.startIndex) },
2485
2550
  this.isMultiple && (vtyx.h(Icon$1, { icon: "check", store: this.store, class: "selectic-item_icon" })),
2551
+ !this.isMultiple && (vtyx.h(Icon$1, { icon: "dot", store: this.store, class: "selectic-item_icon single-select_icon" })),
2486
2552
  option.icon && (option.icon.includes(':')
2487
2553
  ? vtyx.h(Icon$1, { icon: option.icon, store: this.store })
2488
2554
  : vtyx.h(Icon$1, { icon: `raw:${option.icon}`, store: this.store })),
@@ -3013,7 +3079,7 @@ let Selectic = class Selectic extends vtyx.Vue {
3013
3079
  else {
3014
3080
  this.removeListeners();
3015
3081
  if (state.status.hasChanged) {
3016
- this.$emit('change', this.getValue(), state.selectionIsExcluded, this);
3082
+ this.emit('change', this.getValue(), state.selectionIsExcluded);
3017
3083
  this.store.resetChange();
3018
3084
  }
3019
3085
  this.emit('close');
@@ -28,7 +28,7 @@ function styleInject(css, ref) {
28
28
  }
29
29
  }
30
30
 
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 top: var(--top-position, 0);\n z-index: 2000;\n height: auto;\n max-height: var(--availableSpace);\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 width: var(--list-width, 200px);\n min-width: 200px;\n display: grid;\n grid-template-rows: minmax(0, max-content) 1fr;\n}\n.selectic__extended-list.selectic-position-top {\n box-shadow: 2px -3px 12px 0px #888888;\n}\n.selectic__extended-list__list-container{\n overflow: auto;\n}\n.selectic__extended-list__list-items {\n max-height: calc(var(--selectic-input-height) * 10);\n min-width: max-content;\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.selectic-item__active.selectic-item__disabled:not(.selected) .selectic-item_icon {\n opacity: 0;\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__is-group.selectable {\n cursor: pointer;\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.selectic .form-control-feedback.fa.selectic-search-scope {\n width: calc(var(--selectic-input-height) * 0.75);\n height: calc(var(--selectic-input-height) * 0.75);\n line-height: calc(var(--selectic-input-height) * 0.75);\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/* {{{ icons */\n\n@keyframes selectic-animation-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(359deg);\n }\n}\n\n.selectic__icon {\n height: 1em;\n fill: currentColor;\n}\n\n.selectic-spin {\n animation: selectic-animation-spin 2s infinite linear;\n}\n\n/* }}} */\n";
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\n.selectic .has-feedback {\n position: relative;\n}\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\n.selectic-input__clear-icon {\n align-self: center;\n margin-left: 3px;\n cursor: pointer;\n}\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\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 top: var(--top-position, 0);\n z-index: 2000;\n height: auto;\n max-height: var(--availableSpace);\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 width: var(--list-width, 200px);\n min-width: 200px;\n display: grid;\n grid-template-rows: minmax(0, max-content) 1fr;\n}\n\n.selectic__extended-list.selectic-position-top {\n box-shadow: 2px -3px 12px 0px #888888;\n}\n\n.selectic__extended-list__list-container{\n overflow: auto;\n}\n\n.selectic__extended-list__list-items {\n max-height: calc(var(--selectic-input-height) * 10);\n min-width: max-content;\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\n.selectic-item__active:not(.selected) .selectic-item_icon {\n opacity: 0.2;\n}\n\n.selectic-item__active:not(.selected) .single-select_icon {\n opacity: 0;\n}\n\n.selectic-item__active.selectic-item__disabled:not(.selected) .selectic-item_icon {\n opacity: 0;\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__is-group.selectable {\n cursor: pointer;\n}\n\n.selectic-item.selected {\n color: var(--selectic-selected-item-color);\n}\n\n.selectic-search-scope {\n color: #e0e0e0;\n left: auto;\n right: 10px;\n}\n\n.selectic .form-control-feedback.fa.selectic-search-scope {\n width: calc(var(--selectic-input-height) * 0.75);\n height: calc(var(--selectic-input-height) * 0.75);\n line-height: calc(var(--selectic-input-height) * 0.75);\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\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\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\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/* {{{ icons */\n\n@keyframes selectic-animation-spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(359deg);\n }\n}\n\n.selectic__icon {\n height: 1em;\n fill: currentColor;\n}\n\n.selectic-spin {\n animation: selectic-animation-spin 2s infinite linear;\n}\n\n/* }}} */\n";
32
32
  styleInject(css_248z);
33
33
 
34
34
  /**
@@ -107,32 +107,76 @@ function assignObject(obj, ...sourceObjects) {
107
107
  }
108
108
  return result;
109
109
  }
110
- /** Compare 2 list of options.
111
- * @returns true if there are no difference
110
+ /**
111
+ * Ckeck whether a value is primitive.
112
+ * @returns true if val is primitive and false otherwise.
112
113
  */
113
- function compareOptions(oldOptions, newOptions) {
114
- if (oldOptions.length !== newOptions.length) {
115
- return false;
116
- }
117
- return oldOptions.every((oldOption, idx) => {
118
- const newOption = newOptions[idx];
119
- const keys = Object.keys(oldOption);
120
- if (keys.length !== Object.keys(newOption).length) {
114
+ function isPrimitive(val) {
115
+ /* The value null is treated explicitly because in JavaScript
116
+ * `typeof null === 'object'` is evaluated to `true`.
117
+ */
118
+ return val === null || (typeof val !== 'object' && typeof val !== 'function');
119
+ }
120
+ /**
121
+ * Performs a deep comparison between two objects to determine if they
122
+ * should be considered equal.
123
+ *
124
+ * @param objA object to compare to objB.
125
+ * @param objB object to compare to objA.
126
+ * @param attributes list of attributes to not compare.
127
+ * @param refs internal reference to object to avoid cyclic references
128
+ * @returns true if objA should be considered equal to objB.
129
+ */
130
+ function isDeepEqual(objA, objB, ignoreAttributes = [], refs = new WeakMap()) {
131
+ objA = unref(objA);
132
+ objB = unref(objB);
133
+ /* For primitive types */
134
+ if (isPrimitive(objA)) {
135
+ return isPrimitive(objB) && Object.is(objA, objB);
136
+ }
137
+ /* For functions (follow the behavior of _.isEqual and compare functions
138
+ * by reference). */
139
+ if (typeof objA === 'function') {
140
+ return typeof objB === 'function' && objA === objB;
141
+ }
142
+ /* For circular references */
143
+ if (refs.has(objA)) {
144
+ return refs.get(objA) === objB;
145
+ }
146
+ refs.set(objA, objB);
147
+ /* For objects */
148
+ if (typeof objA === 'object') {
149
+ if (typeof objB !== 'object') {
121
150
  return false;
122
151
  }
123
- return keys.every((optionKey) => {
124
- const key = optionKey;
125
- const oldValue = oldOption[key];
126
- const newValue = newOption[key];
127
- if (key === 'options') {
128
- return compareOptions(oldValue, newValue);
129
- }
130
- if (key === 'data') {
131
- return JSON.stringify(oldValue) === JSON.stringify(newValue);
132
- }
133
- return oldValue === newValue;
152
+ /* For arrays */
153
+ if (Array.isArray(objA)) {
154
+ return Array.isArray(objB) &&
155
+ objA.length === objB.length &&
156
+ !objA.some((val, idx) => !isDeepEqual(val, objB[idx], ignoreAttributes, refs));
157
+ }
158
+ /* For RegExp */
159
+ if (objA instanceof RegExp) {
160
+ return objB instanceof RegExp &&
161
+ objA.source === objB.source &&
162
+ objA.flags === objB.flags;
163
+ }
164
+ /* For Date */
165
+ if (objA instanceof Date) {
166
+ return objB instanceof Date && objA.getTime() === objB.getTime();
167
+ }
168
+ /* This should be an object */
169
+ const aRec = objA;
170
+ const bRec = objB;
171
+ const aKeys = Object.keys(aRec).filter((key) => !ignoreAttributes.includes(key));
172
+ const bKeys = Object.keys(bRec).filter((key) => !ignoreAttributes.includes(key));
173
+ const differentKeyFound = aKeys.some((key) => {
174
+ return !bKeys.includes(key) ||
175
+ !isDeepEqual(aRec[key], bRec[key], ignoreAttributes, refs);
134
176
  });
135
- });
177
+ return aKeys.length === bKeys.length && !differentKeyFound;
178
+ }
179
+ return true;
136
180
  }
137
181
  let displayLog = false;
138
182
  function debug(fName, step, ...args) {
@@ -1186,7 +1230,7 @@ class SelecticStore {
1186
1230
  /* update cache */
1187
1231
  state.totalDynOptions = total;
1188
1232
  const old = state.dynOptions.splice(offset, result.length, ...result);
1189
- if (compareOptions(old, result)) {
1233
+ if (isDeepEqual(old, result)) {
1190
1234
  /* Added options are the same as previous ones.
1191
1235
  * Stop fetching to avoid infinite loop
1192
1236
  */
@@ -1489,7 +1533,7 @@ class SelecticStore {
1489
1533
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1490
1534
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1491
1535
  */
1492
- var __decorate$d = (this && this.__decorate) || function (decorators, target, key, desc) {
1536
+ var __decorate$e = (this && this.__decorate) || function (decorators, target, key, desc) {
1493
1537
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1494
1538
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1495
1539
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1501,7 +1545,7 @@ let IconCaretDown = class IconCaretDown extends Vue {
1501
1545
  h("path", { d: "M7,10L12,15L17,10H7Z" })));
1502
1546
  }
1503
1547
  };
1504
- IconCaretDown = __decorate$d([
1548
+ IconCaretDown = __decorate$e([
1505
1549
  Component
1506
1550
  ], IconCaretDown);
1507
1551
  var IconCaretDown$1 = IconCaretDown;
@@ -1509,7 +1553,7 @@ var IconCaretDown$1 = IconCaretDown;
1509
1553
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1510
1554
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1511
1555
  */
1512
- var __decorate$c = (this && this.__decorate) || function (decorators, target, key, desc) {
1556
+ var __decorate$d = (this && this.__decorate) || function (decorators, target, key, desc) {
1513
1557
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1514
1558
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1515
1559
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1521,7 +1565,7 @@ let IconCaretUp = class IconCaretUp extends Vue {
1521
1565
  h("path", { d: "M7,15L12,10L17,15H7Z" })));
1522
1566
  }
1523
1567
  };
1524
- IconCaretUp = __decorate$c([
1568
+ IconCaretUp = __decorate$d([
1525
1569
  Component
1526
1570
  ], IconCaretUp);
1527
1571
  var IconCaretUp$1 = IconCaretUp;
@@ -1529,7 +1573,7 @@ var IconCaretUp$1 = IconCaretUp;
1529
1573
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1530
1574
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1531
1575
  */
1532
- var __decorate$b = (this && this.__decorate) || function (decorators, target, key, desc) {
1576
+ var __decorate$c = (this && this.__decorate) || function (decorators, target, key, desc) {
1533
1577
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1534
1578
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1535
1579
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
@@ -1541,11 +1585,28 @@ let IconCheck = class IconCheck extends Vue {
1541
1585
  h("path", { d: "M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z" })));
1542
1586
  }
1543
1587
  };
1544
- IconCheck = __decorate$b([
1588
+ IconCheck = __decorate$c([
1545
1589
  Component
1546
1590
  ], IconCheck);
1547
1591
  var IconCheck$1 = IconCheck;
1548
1592
 
1593
+ var __decorate$b = (this && this.__decorate) || function (decorators, target, key, desc) {
1594
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1595
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1596
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1597
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1598
+ };
1599
+ let IconDot = class IconDot extends Vue {
1600
+ render() {
1601
+ return (h("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" },
1602
+ h("circle", { cx: "8", cy: "16", r: "3.5" })));
1603
+ }
1604
+ };
1605
+ IconDot = __decorate$b([
1606
+ Component
1607
+ ], IconDot);
1608
+ var IconDot$1 = IconDot;
1609
+
1549
1610
  /* This icon is from <https://github.com/Templarian/MaterialDesign>,
1550
1611
  * distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license
1551
1612
  */
@@ -1694,6 +1755,8 @@ let Icon = class Icon extends Vue {
1694
1755
  return IconCaretUp$1;
1695
1756
  case 'check':
1696
1757
  return IconCheck$1;
1758
+ case 'dot':
1759
+ return IconDot$1;
1697
1760
  case 'search':
1698
1761
  return IconSearch$1;
1699
1762
  case 'spinner':
@@ -2448,8 +2511,10 @@ let List = class List extends Vue {
2448
2511
  onOffsetChange() {
2449
2512
  this.checkOffset();
2450
2513
  }
2451
- onFilteredOptionsChange() {
2452
- this.checkOffset();
2514
+ onFilteredOptionsChange(oldVal, newVal) {
2515
+ if (!isDeepEqual(oldVal, newVal)) {
2516
+ this.checkOffset();
2517
+ }
2453
2518
  }
2454
2519
  onGroupIdChange() {
2455
2520
  this.$emit('groupId', this.groupId);
@@ -2479,6 +2544,7 @@ let List = class List extends Vue {
2479
2544
  'selectic-item__is-group': option.isGroup,
2480
2545
  }], style: option.style, title: option.title, key: 'selectic-item-' + (idx + this.startIndex) },
2481
2546
  this.isMultiple && (h(Icon$1, { icon: "check", store: this.store, class: "selectic-item_icon" })),
2547
+ !this.isMultiple && (h(Icon$1, { icon: "dot", store: this.store, class: "selectic-item_icon single-select_icon" })),
2482
2548
  option.icon && (option.icon.includes(':')
2483
2549
  ? h(Icon$1, { icon: option.icon, store: this.store })
2484
2550
  : h(Icon$1, { icon: `raw:${option.icon}`, store: this.store })),
@@ -3009,7 +3075,7 @@ let Selectic = class Selectic extends Vue {
3009
3075
  else {
3010
3076
  this.removeListeners();
3011
3077
  if (state.status.hasChanged) {
3012
- this.$emit('change', this.getValue(), state.selectionIsExcluded, this);
3078
+ this.emit('change', this.getValue(), state.selectionIsExcluded);
3013
3079
  this.store.resetChange();
3014
3080
  }
3015
3081
  this.emit('close');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selectic",
3
- "version": "3.1.4",
3
+ "version": "3.2.0",
4
4
  "description": "Smart Select for VueJS 3.x",
5
5
  "main": "dist/selectic.common.js",
6
6
  "module": "dist/selectic.esm.js",
@@ -42,8 +42,8 @@
42
42
  "vtyx": "4.2.1"
43
43
  },
44
44
  "devDependencies": {
45
- "@babel/types": "^7.23.6",
46
- "rollup": "^2.79.1",
45
+ "@babel/types": "^7.28.1",
46
+ "rollup": "^2.79.2",
47
47
  "rollup-plugin-postcss": "^4.0.2",
48
48
  "tape": "^4.17.0",
49
49
  "typescript": "~4.8"
package/src/Icon.tsx CHANGED
@@ -8,6 +8,7 @@ import Store, { IconFamily, IconKey, IconValue } from './Store';
8
8
  import IconCaretDown from './icons/caret-down';
9
9
  import IconCaretUp from './icons/caret-up';
10
10
  import IconCheck from './icons/check';
11
+ import IconDot from './icons/dot';
11
12
  import IconQuestion from './icons/question';
12
13
  import IconSearch from './icons/search';
13
14
  import IconSpinner from './icons/spinner';
@@ -87,6 +88,8 @@ export default class Icon extends Vue<Props> {
87
88
  return IconCaretUp;
88
89
  case 'check':
89
90
  return IconCheck;
91
+ case 'dot':
92
+ return IconDot;
90
93
  case 'search':
91
94
  return IconSearch;
92
95
  case 'spinner':
package/src/List.tsx CHANGED
@@ -3,7 +3,7 @@
3
3
  * It handles interactions with these items.
4
4
  */
5
5
 
6
- import {Vue, Component, Prop, Watch, h} from 'vtyx';
6
+ import { Vue, Component, Prop, Watch, h } from 'vtyx';
7
7
  import { unref } from 'vue';
8
8
 
9
9
  import Store, {
@@ -11,6 +11,7 @@ import Store, {
11
11
  OptionId,
12
12
  } from './Store';
13
13
  import Icon from './Icon';
14
+ import { isDeepEqual } from './tools';
14
15
 
15
16
  export interface Props {
16
17
  store: Store;
@@ -222,8 +223,10 @@ export default class List extends Vue<Props> {
222
223
  }
223
224
 
224
225
  @Watch('filteredOptions', { deep: true })
225
- public onFilteredOptionsChange() {
226
- this.checkOffset();
226
+ public onFilteredOptionsChange(oldVal: OptionItem[], newVal: OptionItem[]) {
227
+ if (!isDeepEqual(oldVal, newVal)) {
228
+ this.checkOffset();
229
+ }
227
230
  }
228
231
 
229
232
  @Watch('groupId')
@@ -280,6 +283,9 @@ export default class List extends Vue<Props> {
280
283
  {this.isMultiple && (
281
284
  <Icon icon="check" store={this.store} class="selectic-item_icon" />
282
285
  )}
286
+ {!this.isMultiple && (
287
+ <Icon icon="dot" store={this.store} class="selectic-item_icon single-select_icon" />
288
+ )}
283
289
  {option.icon && (
284
290
  option.icon.includes(':')
285
291
  ? <Icon icon={option.icon} store={this.store} />
package/src/Store.tsx CHANGED
@@ -7,7 +7,7 @@
7
7
  import { reactive, watch, unref, computed, ComputedRef } from 'vue';
8
8
  import {
9
9
  assignObject,
10
- compareOptions,
10
+ isDeepEqual,
11
11
  convertToRegExp,
12
12
  debug,
13
13
  deepClone,
@@ -378,6 +378,7 @@ export type IconKey =
378
378
  | 'caret-down'
379
379
  | 'caret-up'
380
380
  | 'check'
381
+ | 'dot'
381
382
  | 'search'
382
383
  | 'spinner'
383
384
  | 'strikethrough'
@@ -1704,7 +1705,7 @@ export default class SelecticStore {
1704
1705
  state.totalDynOptions = total;
1705
1706
  const old = state.dynOptions.splice(offset, result.length, ...result);
1706
1707
 
1707
- if (compareOptions(old, result)) {
1708
+ if (isDeepEqual(old, result)) {
1708
1709
  /* Added options are the same as previous ones.
1709
1710
  * Stop fetching to avoid infinite loop
1710
1711
  */
@@ -60,9 +60,11 @@
60
60
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
61
61
  transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
62
62
  }
63
+
63
64
  .selectic .has-feedback {
64
65
  position: relative;
65
66
  }
67
+
66
68
  .selectic .has-feedback .form-control {
67
69
  padding-right: calc(var(--selectic-input-height) + 4px);
68
70
  }
@@ -141,11 +143,13 @@
141
143
  margin-right: 3px;
142
144
  cursor: default;
143
145
  }
146
+
144
147
  .selectic-input__clear-icon {
145
148
  align-self: center;
146
149
  margin-left: 3px;
147
150
  cursor: pointer;
148
151
  }
152
+
149
153
  .selectic-input__clear-icon:hover {
150
154
  color: var(--selectic-selected-item-color);
151
155
  }
@@ -159,6 +163,7 @@
159
163
  cursor: var(--selectic-cursor-disabled);
160
164
  background-color: var(--selectic-bg-disabled);
161
165
  }
166
+
162
167
  .selectic-input.disabled .more-items {
163
168
  background-color: var(--selectic-more-items-bg-disabled);
164
169
  }
@@ -197,12 +202,15 @@
197
202
  display: grid;
198
203
  grid-template-rows: minmax(0, max-content) 1fr;
199
204
  }
205
+
200
206
  .selectic__extended-list.selectic-position-top {
201
207
  box-shadow: 2px -3px 12px 0px #888888;
202
208
  }
209
+
203
210
  .selectic__extended-list__list-container{
204
211
  overflow: auto;
205
212
  }
213
+
206
214
  .selectic__extended-list__list-items {
207
215
  max-height: calc(var(--selectic-input-height) * 10);
208
216
  min-width: max-content;
@@ -241,9 +249,15 @@
241
249
  background-color: var(--selectic-active-item-bg);
242
250
  color: var(--selectic-active-item-color);
243
251
  }
252
+
244
253
  .selectic-item__active:not(.selected) .selectic-item_icon {
245
254
  opacity: 0.2;
246
255
  }
256
+
257
+ .selectic-item__active:not(.selected) .single-select_icon {
258
+ opacity: 0;
259
+ }
260
+
247
261
  .selectic-item__active.selectic-item__disabled:not(.selected) .selectic-item_icon {
248
262
  opacity: 0;
249
263
  }
@@ -269,11 +283,13 @@
269
283
  .selectic-item.selected {
270
284
  color: var(--selectic-selected-item-color);
271
285
  }
286
+
272
287
  .selectic-search-scope {
273
288
  color: #e0e0e0;
274
289
  left: auto;
275
290
  right: 10px;
276
291
  }
292
+
277
293
  .selectic .form-control-feedback.fa.selectic-search-scope {
278
294
  width: calc(var(--selectic-input-height) * 0.75);
279
295
  height: calc(var(--selectic-input-height) * 0.75);
@@ -311,6 +327,7 @@
311
327
  padding-bottom: 10px;
312
328
  margin-bottom: 0px;
313
329
  }
330
+
314
331
  .selectic .filter-input {
315
332
  height: calc(var(--selectic-input-height) * 0.75);
316
333
  }
@@ -375,6 +392,7 @@
375
392
  color: var(--selectic-more-items-color, var(--selectic-info-color));
376
393
  cursor: help;
377
394
  }
395
+
378
396
  .selectic-input__selected-items__value {
379
397
  grid-area: value;
380
398
  align-self: center;
@@ -390,6 +408,7 @@
390
408
  justify-self: center;
391
409
  margin-left: 5px;
392
410
  }
411
+
393
412
  .selectic-input__selected-items__icon:hover {
394
413
  color: var(--selectic-selected-item-color);
395
414
  }
@@ -0,0 +1,17 @@
1
+ import {Vue, Component, h} from 'vtyx';
2
+
3
+ export interface Props {}
4
+
5
+ @Component
6
+ export default class IconDot extends Vue<Props> {
7
+ public render() {
8
+ return (
9
+ <svg
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ viewBox="0 0 24 24"
12
+ >
13
+ <circle cx="8" cy="16" r="3.5" />
14
+ </svg>
15
+ );
16
+ }
17
+ }
package/src/index.tsx CHANGED
@@ -560,7 +560,7 @@ export default class Selectic extends Vue<Props> {
560
560
  } else {
561
561
  this.removeListeners();
562
562
  if (state.status.hasChanged) {
563
- this.$emit('change', this.getValue(), state.selectionIsExcluded, this);
563
+ this.emit('change', this.getValue(), state.selectionIsExcluded);
564
564
  this.store.resetChange();
565
565
  }
566
566
  this.emit('close');
package/src/tools.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { unref } from 'vue';
2
- import { OptionValue } from './Store';
3
2
 
4
3
  /**
5
4
  * Clone the object and its inner properties.
@@ -8,7 +7,11 @@ import { OptionValue } from './Store';
8
7
  * @param refs internal reference to object to avoid cyclic references
9
8
  * @returns a copy of obj
10
9
  */
11
- export function deepClone<T = any>(origObject: T, ignoreAttributes: string[] = [], refs: WeakMap<any, any> = new WeakMap()): T {
10
+ export function deepClone<T = any>(
11
+ origObject: T,
12
+ ignoreAttributes: string[] = [],
13
+ refs: WeakMap<any, any> = new WeakMap()
14
+ ): T {
12
15
  const obj = unref(origObject);
13
16
 
14
17
  /* For circular references */
@@ -88,34 +91,93 @@ export function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial
88
91
  return result as T;
89
92
  }
90
93
 
91
- /** Compare 2 list of options.
92
- * @returns true if there are no difference
94
+ /**
95
+ * Ckeck whether a value is primitive.
96
+ * @returns true if val is primitive and false otherwise.
97
+ */
98
+ function isPrimitive<T = any>(val: T): boolean {
99
+ /* The value null is treated explicitly because in JavaScript
100
+ * `typeof null === 'object'` is evaluated to `true`.
101
+ */
102
+ return val === null || (typeof val !== 'object' && typeof val !== 'function');
103
+ }
104
+
105
+ /**
106
+ * Performs a deep comparison between two objects to determine if they
107
+ * should be considered equal.
108
+ *
109
+ * @param objA object to compare to objB.
110
+ * @param objB object to compare to objA.
111
+ * @param attributes list of attributes to not compare.
112
+ * @param refs internal reference to object to avoid cyclic references
113
+ * @returns true if objA should be considered equal to objB.
93
114
  */
94
- export function compareOptions(oldOptions: OptionValue[], newOptions: OptionValue[]): boolean {
95
- if (oldOptions.length !== newOptions.length) {
96
- return false;
115
+ export function isDeepEqual<T = any>(
116
+ objA: T,
117
+ objB: T,
118
+ ignoreAttributes: string[] = [],
119
+ refs: WeakMap<any, any> = new WeakMap()
120
+ ): boolean {
121
+ objA = unref(objA);
122
+ objB = unref(objB);
123
+
124
+ /* For primitive types */
125
+ if (isPrimitive(objA)) {
126
+ return isPrimitive(objB) && Object.is(objA, objB);
127
+ }
128
+
129
+ /* For functions (follow the behavior of _.isEqual and compare functions
130
+ * by reference). */
131
+ if (typeof objA === 'function') {
132
+ return typeof objB === 'function' && objA === objB;
133
+ }
134
+
135
+ /* For circular references */
136
+ if (refs.has(objA)) {
137
+ return refs.get(objA) === objB;
97
138
  }
139
+ refs.set(objA, objB);
98
140
 
99
- return oldOptions.every((oldOption, idx) => {
100
- const newOption = newOptions[idx];
101
- const keys = Object.keys(oldOption);
102
- if (keys.length !== Object.keys(newOption).length) {
141
+ /* For objects */
142
+ if (typeof objA === 'object') {
143
+ if (typeof objB !== 'object') {
103
144
  return false;
104
145
  }
105
- return keys.every((optionKey) => {
106
- const key = optionKey as keyof OptionValue;
107
- const oldValue = oldOption[key];
108
- const newValue = newOption[key];
109
146
 
110
- if (key === 'options') {
111
- return compareOptions(oldValue, newValue);
112
- }
113
- if (key === 'data') {
114
- return JSON.stringify(oldValue) === JSON.stringify(newValue);
115
- }
116
- return oldValue === newValue;
147
+ /* For arrays */
148
+ if (Array.isArray(objA)) {
149
+ return Array.isArray(objB) &&
150
+ objA.length === objB.length &&
151
+ !objA.some((val, idx) => !isDeepEqual(val, (objB as unknown[])[idx], ignoreAttributes, refs));
152
+ }
153
+
154
+ /* For RegExp */
155
+ if (objA instanceof RegExp) {
156
+ return objB instanceof RegExp &&
157
+ objA.source === objB.source &&
158
+ objA.flags === objB.flags;
159
+ }
160
+
161
+ /* For Date */
162
+ if (objA instanceof Date) {
163
+ return objB instanceof Date && objA.getTime() === objB.getTime();
164
+ }
165
+
166
+ /* This should be an object */
167
+ const aRec = objA as Record<string, any>;
168
+ const bRec = objB as Record<string, any>;
169
+ const aKeys = Object.keys(aRec).filter((key) => !ignoreAttributes.includes(key));
170
+ const bKeys = Object.keys(bRec).filter((key) => !ignoreAttributes.includes(key));
171
+
172
+ const differentKeyFound = aKeys.some((key) => {
173
+ return !bKeys.includes(key) ||
174
+ !isDeepEqual(aRec[key], bRec[key], ignoreAttributes, refs);
117
175
  });
118
- });
176
+
177
+ return aKeys.length === bKeys.length && !differentKeyFound;
178
+ }
179
+
180
+ return true;
119
181
  }
120
182
 
121
183
  let displayLog = false;
@@ -5,6 +5,7 @@ const {
5
5
  assignObject,
6
6
  convertToRegExp,
7
7
  deepClone,
8
+ isDeepEqual,
8
9
  } = toolFile;
9
10
 
10
11
  tape.test('assignObject()', (st) => {
@@ -402,3 +403,153 @@ tape.test('deepClone()', (st) => {
402
403
  tst.end();
403
404
  });
404
405
  });
406
+
407
+ tape.test('isDeepEqual()', (st) => {
408
+ const fn = () => {};
409
+ const obj = {
410
+ a: 1,
411
+ b: 'b',
412
+ c: false,
413
+ d: undefined,
414
+ e: null,
415
+ f: {},
416
+ g: fn,
417
+ spe1: NaN,
418
+ spe2: Infinity,
419
+ spe3: '',
420
+ spe4: [],
421
+ };
422
+
423
+ st.test('should compare simple objects', (tst) => {
424
+ const objA = obj;
425
+ const objB = deepClone(obj);
426
+
427
+ tst.is(isDeepEqual(objB, objA), true, 'should be equal');
428
+
429
+ objB.a = objB.a.toString();
430
+ tst.is(isDeepEqual(objA, objB), false, 'should not be equal');
431
+ tst.end();
432
+ });
433
+
434
+ st.test('should compare nested objects', (tst) => {
435
+ const deep1 = obj;
436
+ const deep2 = {
437
+ deep: deep1,
438
+ added: 'a value',
439
+ };
440
+ const objA = {
441
+ d: deep2,
442
+ }
443
+ const objB = deepClone(objA);
444
+
445
+ tst.is(isDeepEqual(objA, objB), true, 'should be equal');
446
+ tst.end();
447
+ });
448
+
449
+ st.test('with arrays', (tst) => {
450
+ const nestedArray1 = [
451
+ obj,
452
+ { a: 'value' },
453
+ null,
454
+ undefined,
455
+ 42,
456
+ 'value',
457
+ ];
458
+ const nestedArray2 = deepClone(nestedArray1);
459
+ const arrayA = [
460
+ {
461
+ deep: nestedArray1,
462
+ },
463
+ nestedArray2,
464
+ 42, 'value', null, undefined, [[]],
465
+ ];
466
+ const arrayB = deepClone(arrayA);
467
+
468
+ tst.is(isDeepEqual(arrayA, arrayB), true, 'should be equal');
469
+ tst.end();
470
+ });
471
+
472
+ st.test('with RegExp', (tst) => {
473
+ const regA1 = /hello?/gi;
474
+ const regA2 = /.* [aA]+?/;
475
+
476
+ const regB1 = deepClone(regA1);
477
+ const regB2 = {rgx: regA2};
478
+
479
+ tst.is(isDeepEqual(regA1, regB1), true, 'should be equal');
480
+ tst.is(isDeepEqual(regA2, regB2), false, 'should not be equal');
481
+ tst.end();
482
+ });
483
+
484
+ st.test('with Date', (tst) => {
485
+ const dateA = new Date('1996-06-27');
486
+ const dateB = new Date('1996-06-27');
487
+
488
+ tst.is(isDeepEqual(dateA, dateB), true, 'should be equal');
489
+ tst.end();
490
+ });
491
+
492
+ st.test('with functions', (tst) => {
493
+ const fn1 = fn;
494
+ const fn2 = () => {};
495
+
496
+ tst.is(isDeepEqual(fn1, fn), true, 'should be equal');
497
+ tst.is(isDeepEqual(fn2, fn), false, 'should not be equal');
498
+ tst.end();
499
+ });
500
+
501
+ st.test('with primitives', (tst) => {
502
+ tst.is(isDeepEqual(10%3, 1), true, 'should be equal');
503
+ tst.is(isDeepEqual('Bingo'[0], 'B'), true, 'should be equal');
504
+ tst.is(isDeepEqual(!true, false), true, 'should be equal');
505
+ tst.is(isDeepEqual(obj.e, null), true, 'should be equal');
506
+ tst.is(isDeepEqual(obj.d, undefined), true, 'should be equal');
507
+ tst.end();
508
+ });
509
+
510
+ st.test('with circular references', (tst) => {
511
+ const obj1 = {
512
+ a: 'a',
513
+ }
514
+ const obj2 = {
515
+ b: 'b',
516
+ }
517
+ obj1.child = obj1;
518
+ obj1.sibling = obj2;
519
+ obj2.sibling = obj1;
520
+
521
+ const objA = [obj1, obj2];
522
+ const objB = deepClone(objA);
523
+
524
+ tst.is(isDeepEqual(objA, objB), true, 'should be equal');
525
+ tst.end();
526
+ });
527
+
528
+ st.test('can ignore some attributes', (tst) => {
529
+ const noCompare1 = {
530
+ ref: 1,
531
+ };
532
+ const noCompare2 = new Set(['alpha', 'omega']);
533
+ const noCompare3 = new Map([[1, 'alpha'], [22, 'omega']]);
534
+ const deep1 = {
535
+ a: 'alpha',
536
+ noCompare: noCompare3,
537
+ };
538
+ const objA = {
539
+ id: 'ref',
540
+ noCopy: noCompare1,
541
+ nop: noCompare2,
542
+ not: 42,
543
+ deep: deep1,
544
+ };
545
+ const objB = deepClone(objA);
546
+ objB.not = 24;
547
+
548
+ const result1 = isDeepEqual(objA, objB, ['noCopy', 'nothing', 'nop', 'not']);
549
+ const result2 = isDeepEqual(objA, objB, ['noCopy', 'nothing', 'nop']);
550
+
551
+ tst.is(result1, true, 'should be equal');
552
+ tst.is(result2, false, 'should not be equal');
553
+ tst.end();
554
+ });
555
+ });
package/types/List.d.ts CHANGED
@@ -30,7 +30,7 @@ export default class List extends Vue<Props> {
30
30
  private onMouseOver;
31
31
  onIndexChange(): void;
32
32
  onOffsetChange(): void;
33
- onFilteredOptionsChange(): void;
33
+ onFilteredOptionsChange(oldVal: OptionItem[], newVal: OptionItem[]): void;
34
34
  onGroupIdChange(): void;
35
35
  mounted(): void;
36
36
  render(): h.JSX.Element;
package/types/Store.d.ts CHANGED
@@ -247,7 +247,7 @@ export interface SelecticStoreState {
247
247
  };
248
248
  }
249
249
  export declare type IconFamily = '' | 'selectic' | 'font-awesome-4' | 'font-awesome-5' | 'font-awesome-6' | 'raw' | `prefix:${string}`;
250
- export declare type IconKey = 'caret-down' | 'caret-up' | 'check' | 'search' | 'spinner' | 'strikethrough' | 'times' | 'question' | 'spin';
250
+ export declare type IconKey = 'caret-down' | 'caret-up' | 'check' | 'dot' | 'search' | 'spinner' | 'strikethrough' | 'times' | 'question' | 'spin';
251
251
  export declare type IconValue = `selectic:${IconKey}${'' | ':spin'}` | `raw:${string}` | `current:${IconKey}${'' | ':spin'}` | string;
252
252
  export declare type Icons = Record<IconKey, IconValue>;
253
253
  export declare type PartialIcons = {
@@ -0,0 +1,6 @@
1
+ import { Vue, h } from 'vtyx';
2
+ export interface Props {
3
+ }
4
+ export default class IconDot extends Vue<Props> {
5
+ render(): h.JSX.Element;
6
+ }
package/types/tools.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { OptionValue } from './Store';
2
1
  /**
3
2
  * Clone the object and its inner properties.
4
3
  * @param obj The object to be clone.
@@ -21,10 +20,17 @@ export declare function deepClone<T = any>(origObject: T, ignoreAttributes?: str
21
20
  export declare function convertToRegExp(name: string, flag?: string): RegExp;
22
21
  /** Does the same as Object.assign but does not replace if value is undefined */
23
22
  export declare function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial<T>>): T;
24
- /** Compare 2 list of options.
25
- * @returns true if there are no difference
23
+ /**
24
+ * Performs a deep comparison between two objects to determine if they
25
+ * should be considered equal.
26
+ *
27
+ * @param objA object to compare to objB.
28
+ * @param objB object to compare to objA.
29
+ * @param attributes list of attributes to not compare.
30
+ * @param refs internal reference to object to avoid cyclic references
31
+ * @returns true if objA should be considered equal to objB.
26
32
  */
27
- export declare function compareOptions(oldOptions: OptionValue[], newOptions: OptionValue[]): boolean;
33
+ export declare function isDeepEqual<T = any>(objA: T, objB: T, ignoreAttributes?: string[], refs?: WeakMap<any, any>): boolean;
28
34
  export declare function debug(fName: string, step: string, ...args: any[]): void;
29
35
  export declare namespace debug {
30
36
  var enable: (display: boolean) => void;