selectic 3.1.2 → 3.1.4

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.
@@ -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\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.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";
32
32
  styleInject(css_248z);
33
33
 
34
34
  /**
@@ -554,25 +554,54 @@ class SelecticStore {
554
554
  if (selected === undefined) {
555
555
  selected = !isAlreadySelected;
556
556
  }
557
+ const selectedOptions = Array.isArray(state.selectedOptions)
558
+ ? state.selectedOptions
559
+ : [];
557
560
  if (id === null) {
558
- state.internalValue = [];
559
- hasChanged = internalValue.length > 0;
561
+ /* Keep disabled items: we cannot removed them because they
562
+ * are disabled */
563
+ const newSelection = selectedOptions.reduce((list, item) => {
564
+ if (item.disabled && item.id) {
565
+ list.push(item.id);
566
+ }
567
+ return list;
568
+ }, []);
569
+ state.internalValue = newSelection;
570
+ hasChanged = internalValue.length > newSelection.length;
560
571
  }
561
572
  else if (selected && !isAlreadySelected) {
573
+ let addItem = true;
562
574
  if (item === null || item === void 0 ? void 0 : item.exclusive) {
563
- /* clear the current selection because the item is exclusive */
564
- internalValue.splice(0, Infinity);
575
+ const hasDisabledSelected = selectedOptions.some((opt) => {
576
+ return opt.disabled;
577
+ });
578
+ if (hasDisabledSelected) {
579
+ /* do not remove disabled item from selection */
580
+ addItem = false;
581
+ }
582
+ else {
583
+ /* clear the current selection because the item is exclusive */
584
+ internalValue.splice(0, Infinity);
585
+ }
565
586
  }
566
587
  else if (internalValue.length === 1) {
567
588
  const selectedId = internalValue[0];
568
589
  const selectedItem = state.allOptions.find((opt) => opt.id === selectedId);
569
590
  if (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.exclusive) {
570
- /* clear the current selection because the old item was exclusive */
571
- internalValue.pop();
591
+ if (selectedItem.disabled) {
592
+ /* If selected item is disabled and exclusive do not change the selection */
593
+ addItem = false;
594
+ }
595
+ else {
596
+ /* clear the current selection because the old item was exclusive */
597
+ internalValue.pop();
598
+ }
572
599
  }
573
600
  }
574
- internalValue.push(id);
575
- hasChanged = true;
601
+ if (addItem) {
602
+ internalValue.push(id);
603
+ hasChanged = true;
604
+ }
576
605
  }
577
606
  else if (!selected && isAlreadySelected) {
578
607
  internalValue.splice(internalValue.indexOf(id), 1);
@@ -595,6 +624,11 @@ class SelecticStore {
595
624
  if (id !== oldValue) {
596
625
  return hasChanged;
597
626
  }
627
+ const oldOption = state.selectedOptions;
628
+ if (oldOption === null || oldOption === void 0 ? void 0 : oldOption.disabled) {
629
+ /* old selection is disabled so do not unselect it */
630
+ return hasChanged;
631
+ }
598
632
  id = null;
599
633
  }
600
634
  else if (id === oldValue) {
@@ -1364,15 +1398,23 @@ class SelecticStore {
1364
1398
  if (doNotCheck || !hasFetchedAllItems) {
1365
1399
  return;
1366
1400
  }
1401
+ const selectedOptions = state.selectedOptions;
1367
1402
  const enabledOptions = state.allOptions.filter((opt) => !opt.disabled);
1368
- const nb = enabledOptions.length;
1403
+ const nbEnabled = enabledOptions.length;
1369
1404
  const value = state.internalValue;
1370
1405
  const hasValue = Array.isArray(value) ? value.length > 0 : value !== null;
1371
- const hasValidValue = hasValue && (Array.isArray(value) ? value.every((val) => this.hasValue(val)) :
1406
+ const hasDisabledSelected = Array.isArray(selectedOptions)
1407
+ ? selectedOptions.some((opt) => opt.disabled)
1408
+ : false;
1409
+ const hasOnlyValidValue = hasValue && !hasDisabledSelected && (Array.isArray(value) ? value.every((val) => this.hasValue(val)) :
1372
1410
  this.hasValue(value));
1373
- const isEmpty = nb === 0;
1374
- const hasOnlyOneOption = nb === 1 && hasValidValue && !state.allowClearSelection;
1375
- if (hasOnlyOneOption || isEmpty) {
1411
+ const isEmpty = nbEnabled === 0;
1412
+ const hasOnlyOneOption = nbEnabled === 1 && hasOnlyValidValue && !state.allowClearSelection;
1413
+ const isExclusiveDisabledItem = Array.isArray(selectedOptions) /* which means "multiple" mode */
1414
+ && selectedOptions.length === 1
1415
+ && selectedOptions[0].exclusive
1416
+ && selectedOptions[0].disabled;
1417
+ if (hasOnlyOneOption || isEmpty || isExclusiveDisabledItem) {
1376
1418
  if (state.isOpen) {
1377
1419
  this.setAutomaticClose();
1378
1420
  this.commit('isOpen', false);
@@ -1792,6 +1834,18 @@ let MainInput = class MainInput extends Vue {
1792
1834
  ? Array.isArray(value) && value.length > 0
1793
1835
  : value !== null;
1794
1836
  }
1837
+ get disabledList() {
1838
+ const state = this.store.state;
1839
+ const isMultiple = state.multiple;
1840
+ const value = state.selectedOptions;
1841
+ if (!isMultiple || !value) {
1842
+ return [];
1843
+ }
1844
+ const disabledValues = value.filter((option) => {
1845
+ return option.disabled;
1846
+ });
1847
+ return disabledValues;
1848
+ }
1795
1849
  get displayPlaceholder() {
1796
1850
  const placeholder = this.store.state.placeholder;
1797
1851
  const hasValue = this.hasValue;
@@ -1810,10 +1864,12 @@ let MainInput = class MainInput extends Vue {
1810
1864
  const state = this.store.state;
1811
1865
  const isMultiple = state.multiple;
1812
1866
  const value = state.internalValue;
1813
- const hasOnlyOneValue = Array.isArray(value) && value.length === 1;
1867
+ const nbSelection = (Array.isArray(value) && value.length) || 0;
1868
+ const hasOnlyOneValue = nbSelection === 1;
1869
+ const hasOnlyDisabled = nbSelection <= this.disabledList.length;
1814
1870
  /* Should not display the clear action if there is only one selected
1815
1871
  * item in multiple (as this item has already its remove icon) */
1816
- return !isMultiple || !hasOnlyOneValue;
1872
+ return !isMultiple || !hasOnlyOneValue || !hasOnlyDisabled;
1817
1873
  }
1818
1874
  get clearedLabel() {
1819
1875
  const isMultiple = this.store.state.multiple;
@@ -1931,6 +1987,9 @@ let MainInput = class MainInput extends Vue {
1931
1987
  /* Check if there is enough space to display items like there are
1932
1988
  * currently shown */
1933
1989
  const el = this.$refs.selectedItems;
1990
+ if (!el) {
1991
+ return;
1992
+ }
1934
1993
  const parentEl = el.parentElement;
1935
1994
  if (!document.contains(parentEl)) {
1936
1995
  /* The element is currently not in DOM */
@@ -2029,7 +2088,7 @@ let MainInput = class MainInput extends Vue {
2029
2088
  click: () => this.$emit('item:click', item.id),
2030
2089
  } },
2031
2090
  h("span", { class: "selectic-input__selected-items__value" }, item.text),
2032
- !this.isDisabled && (h(Icon$1, { icon: "times", class: "selectic-input__selected-items__icon", store: this.store, on: {
2091
+ !this.isDisabled && !item.disabled && (h(Icon$1, { icon: "times", class: "selectic-input__selected-items__icon", store: this.store, on: {
2033
2092
  'click.prevent.stop': () => this.selectItem(item.id),
2034
2093
  } }))))),
2035
2094
  this.moreSelectedNb && (h("div", { class: "single-value more-items", title: this.moreSelectedTitle }, this.moreSelectedNb)))),
@@ -2503,42 +2562,6 @@ let ExtendedList = class ExtendedList extends Vue {
2503
2562
  }
2504
2563
  return '';
2505
2564
  }
2506
- get onKeyDown() {
2507
- return (evt) => {
2508
- const key = evt.key;
2509
- if (key === 'Escape') {
2510
- this.store.commit('isOpen', false);
2511
- }
2512
- else if (key === 'Enter') {
2513
- const index = this.store.state.activeItemIdx;
2514
- if (index !== -1) {
2515
- const item = this.store.state.filteredOptions[index];
2516
- if (!item.disabled && !item.isGroup) {
2517
- this.store.selectItem(item.id);
2518
- }
2519
- }
2520
- evt.stopPropagation();
2521
- evt.preventDefault();
2522
- }
2523
- else if (key === 'ArrowUp') {
2524
- const index = this.store.state.activeItemIdx;
2525
- if (index > 0) {
2526
- this.store.commit('activeItemIdx', index - 1);
2527
- }
2528
- evt.stopPropagation();
2529
- evt.preventDefault();
2530
- }
2531
- else if (key === 'ArrowDown') {
2532
- const index = this.store.state.activeItemIdx;
2533
- const max = this.store.state.totalFilteredOptions - 1;
2534
- if (index < max) {
2535
- this.store.commit('activeItemIdx', index + 1);
2536
- }
2537
- evt.stopPropagation();
2538
- evt.preventDefault();
2539
- }
2540
- };
2541
- }
2542
2565
  get bestPosition() {
2543
2566
  const windowHeight = window.innerHeight;
2544
2567
  const isFullyEstimated = this.isFullyEstimated;
@@ -2654,6 +2677,40 @@ let ExtendedList = class ExtendedList extends Vue {
2654
2677
  clickHeaderGroup() {
2655
2678
  this.store.selectGroup(this.topGroupId, !this.topGroupSelected);
2656
2679
  }
2680
+ onKeyDown(evt) {
2681
+ const key = evt.key;
2682
+ if (key === 'Escape') {
2683
+ this.store.commit('isOpen', false);
2684
+ }
2685
+ else if (key === 'Enter') {
2686
+ const index = this.store.state.activeItemIdx;
2687
+ if (index !== -1) {
2688
+ const item = this.store.state.filteredOptions[index];
2689
+ if (!item.disabled && !item.isGroup) {
2690
+ this.store.selectItem(item.id);
2691
+ }
2692
+ }
2693
+ evt.stopPropagation();
2694
+ evt.preventDefault();
2695
+ }
2696
+ else if (key === 'ArrowUp') {
2697
+ const index = this.store.state.activeItemIdx;
2698
+ if (index > 0) {
2699
+ this.store.commit('activeItemIdx', index - 1);
2700
+ }
2701
+ evt.stopPropagation();
2702
+ evt.preventDefault();
2703
+ }
2704
+ else if (key === 'ArrowDown') {
2705
+ const index = this.store.state.activeItemIdx;
2706
+ const max = this.store.state.totalFilteredOptions - 1;
2707
+ if (index < max) {
2708
+ this.store.commit('activeItemIdx', index + 1);
2709
+ }
2710
+ evt.stopPropagation();
2711
+ evt.preventDefault();
2712
+ }
2713
+ }
2657
2714
  /* }}} */
2658
2715
  /* {{{ Life cycles */
2659
2716
  mounted() {
@@ -2783,7 +2840,8 @@ let Selectic = class Selectic extends Vue {
2783
2840
  const store = this.store;
2784
2841
  const keepOpenWithOtherSelectic = this.params.keepOpenWithOtherSelectic;
2785
2842
  const extendedList = this.$refs.extendedList;
2786
- if (!extendedList) {
2843
+ const extendedListEl = extendedList === null || extendedList === void 0 ? void 0 : extendedList.$el;
2844
+ if (!extendedListEl) {
2787
2845
  /* this component is not focused anymore */
2788
2846
  if (!keepOpenWithOtherSelectic) {
2789
2847
  this.removeListeners();
@@ -2792,7 +2850,7 @@ let Selectic = class Selectic extends Vue {
2792
2850
  return;
2793
2851
  }
2794
2852
  const target = evt.target;
2795
- if (!extendedList.$el.contains(target) && !this.$el.contains(target)) {
2853
+ if (!extendedListEl.contains(target) && !this.$el.contains(target)) {
2796
2854
  store.commit('isOpen', false);
2797
2855
  }
2798
2856
  };
@@ -2885,14 +2943,23 @@ let Selectic = class Selectic extends Vue {
2885
2943
  /* }}} */
2886
2944
  /* {{{ private methods */
2887
2945
  computeWidth() {
2888
- const el = this.$refs.mainInput.$el;
2889
- this.width = el.offsetWidth;
2946
+ var _a;
2947
+ const mainInput = (_a = this.$refs) === null || _a === void 0 ? void 0 : _a.mainInput;
2948
+ const mainEl = mainInput === null || mainInput === void 0 ? void 0 : mainInput.$el;
2949
+ if (!mainEl) {
2950
+ /* This method has been called too soon (before render function)
2951
+ * or too late (after unmount) */
2952
+ return;
2953
+ }
2954
+ this.width = mainEl.offsetWidth;
2890
2955
  }
2891
2956
  computeOffset(doNotAddListener = false) {
2892
- const mainInput = this.$refs.mainInput;
2957
+ var _a;
2958
+ const mainInput = (_a = this.$refs) === null || _a === void 0 ? void 0 : _a.mainInput;
2893
2959
  const mainEl = mainInput === null || mainInput === void 0 ? void 0 : mainInput.$el;
2894
2960
  if (!mainEl) {
2895
- /* This method has been called too soon (before render function) */
2961
+ /* This method has been called too soon (before render function)
2962
+ * or too late (after unmount) */
2896
2963
  return;
2897
2964
  }
2898
2965
  const _elementsListeners = this._elementsListeners;
@@ -3026,13 +3093,14 @@ let Selectic = class Selectic extends Vue {
3026
3093
  checkFocus() {
3027
3094
  /* Await that focused element becomes active */
3028
3095
  setTimeout(() => {
3096
+ var _a;
3029
3097
  const focusedEl = document.activeElement;
3030
- const extendedList = this.$refs.extendedList;
3098
+ const extendedList = (_a = this.$refs) === null || _a === void 0 ? void 0 : _a.extendedList;
3031
3099
  /* check if there is a focused element (if none the body is
3032
3100
  * selected) and if it is inside current Selectic */
3033
3101
  if (focusedEl === document.body
3034
3102
  || this.$el.contains(focusedEl)
3035
- || (extendedList && extendedList.$el.contains(focusedEl))) {
3103
+ || (extendedList === null || extendedList === void 0 ? void 0 : extendedList.$el.contains(focusedEl))) {
3036
3104
  return;
3037
3105
  }
3038
3106
  this.store.commit('isOpen', false);
package/doc/list.md CHANGED
@@ -28,12 +28,13 @@ It is possible to define the `option` more precisely.
28
28
  * **text** {`string`} _(mandatory)_: The text which is displayed to select the option or when it is selected.
29
29
  * **title** {`string`}: Text displayed in `title` when cursor is over the option (default: `''`).
30
30
  * **disabled** {`boolean`}: if `true`, this option cannot be selected (default: `false`).
31
+ In "multiple" mode, if the option is already selected, it cannot be removed from selection (it can always be removed by changing `value` of the component).
31
32
  * **className** {`string`}: `class` that are applied on the option (default: `''`).
32
33
  * **style** {`string`}: css style which are applied on the option (default: `''`).
33
34
  * **icon** {`string`}: class names which are applied on a `<span>` before text in the option to display an icon (default: `''`).
34
35
  * **options** {`options[]`}: an other list of options. The current option is considered as a group (equivalent of `optgroup`) (default: `undefined`).
35
36
  * **group** {`string | number`}: If set, the option is part of the given group. This property is needed only in dynamic mode if the option is part of an optgroup (default: `null`).
36
- * **exclusive** {`boolean`}: If set to `true`, in multiple mode, this option will be the only one selected. It means that it clears the previous selected options, and if another option is selected, this option is no more selected.
37
+ * **exclusive** {`boolean`}: If set to `true`, in "multiple" mode, this option will be the only one selected. It means that it clears the previous selected options, and if another option is selected, this option is no more selected.
37
38
  * **data** {`any`}: You can store any information here, it will be provided when getting selected options. _It is not used by selectic so it can be anything you want_ (default: `undefined`).
38
39
 
39
40
  ```javascript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selectic",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "Smart Select for VueJS 3.x",
5
5
  "main": "dist/selectic.common.js",
6
6
  "module": "dist/selectic.esm.js",
@@ -17,6 +17,7 @@
17
17
  "homepage": "https://github.com/intersec/selectic#readme",
18
18
  "keywords": [
19
19
  "select",
20
+ "selectic",
20
21
  "multiselect",
21
22
  "multi-select",
22
23
  "multi-select",
@@ -97,48 +97,6 @@ export default class ExtendedList extends Vue<Props> {
97
97
  return '';
98
98
  }
99
99
 
100
- get onKeyDown() {
101
- return (evt: KeyboardEvent) => {
102
- const key = evt.key;
103
-
104
- if (key === 'Escape') {
105
- this.store.commit('isOpen', false);
106
- } else
107
- if (key === 'Enter') {
108
- const index = this.store.state.activeItemIdx;
109
-
110
- if (index !== -1) {
111
- const item = this.store.state.filteredOptions[index];
112
-
113
- if (!item.disabled && !item.isGroup) {
114
- this.store.selectItem(item.id);
115
- }
116
- }
117
- evt.stopPropagation();
118
- evt.preventDefault();
119
- } else
120
- if (key === 'ArrowUp') {
121
- const index = this.store.state.activeItemIdx;
122
-
123
- if (index > 0) {
124
- this.store.commit('activeItemIdx', index - 1);
125
- }
126
- evt.stopPropagation();
127
- evt.preventDefault();
128
- } else
129
- if (key === 'ArrowDown') {
130
- const index = this.store.state.activeItemIdx;
131
- const max = this.store.state.totalFilteredOptions - 1;
132
-
133
- if (index < max) {
134
- this.store.commit('activeItemIdx', index + 1);
135
- }
136
- evt.stopPropagation();
137
- evt.preventDefault();
138
- }
139
- };
140
- }
141
-
142
100
  get bestPosition(): 'top' | 'bottom' {
143
101
  const windowHeight = window.innerHeight;
144
102
  const isFullyEstimated = this.isFullyEstimated;
@@ -288,6 +246,46 @@ export default class ExtendedList extends Vue<Props> {
288
246
  this.store.selectGroup(this.topGroupId, !this.topGroupSelected);
289
247
  }
290
248
 
249
+ private onKeyDown(evt: KeyboardEvent) {
250
+ const key = evt.key;
251
+
252
+ if (key === 'Escape') {
253
+ this.store.commit('isOpen', false);
254
+ } else
255
+ if (key === 'Enter') {
256
+ const index = this.store.state.activeItemIdx;
257
+
258
+ if (index !== -1) {
259
+ const item = this.store.state.filteredOptions[index];
260
+
261
+ if (!item.disabled && !item.isGroup) {
262
+ this.store.selectItem(item.id);
263
+ }
264
+ }
265
+ evt.stopPropagation();
266
+ evt.preventDefault();
267
+ } else
268
+ if (key === 'ArrowUp') {
269
+ const index = this.store.state.activeItemIdx;
270
+
271
+ if (index > 0) {
272
+ this.store.commit('activeItemIdx', index - 1);
273
+ }
274
+ evt.stopPropagation();
275
+ evt.preventDefault();
276
+ } else
277
+ if (key === 'ArrowDown') {
278
+ const index = this.store.state.activeItemIdx;
279
+ const max = this.store.state.totalFilteredOptions - 1;
280
+
281
+ if (index < max) {
282
+ this.store.commit('activeItemIdx', index + 1);
283
+ }
284
+ evt.stopPropagation();
285
+ evt.preventDefault();
286
+ }
287
+ }
288
+
291
289
  /* }}} */
292
290
  /* {{{ Life cycles */
293
291
 
package/src/MainInput.tsx CHANGED
@@ -17,7 +17,7 @@ export interface Props {
17
17
  @Component
18
18
  export default class MainInput extends Vue<Props> {
19
19
  public $refs: {
20
- selectedItems: HTMLDivElement;
20
+ selectedItems?: HTMLDivElement;
21
21
  };
22
22
 
23
23
  /* {{{ props */
@@ -53,6 +53,22 @@ export default class MainInput extends Vue<Props> {
53
53
  : value !== null;
54
54
  }
55
55
 
56
+ get disabledList(): OptionItem[] {
57
+ const state = this.store.state;
58
+ const isMultiple = state.multiple;
59
+ const value = state.selectedOptions;
60
+
61
+ if (!isMultiple || !value) {
62
+ return [];
63
+ }
64
+
65
+ const disabledValues = (value as OptionItem[]).filter((option) => {
66
+ return option.disabled;
67
+ });
68
+
69
+ return disabledValues;
70
+ }
71
+
56
72
  get displayPlaceholder(): boolean {
57
73
  const placeholder = this.store.state.placeholder;
58
74
  const hasValue = this.hasValue;
@@ -76,11 +92,13 @@ export default class MainInput extends Vue<Props> {
76
92
  const state = this.store.state;
77
93
  const isMultiple = state.multiple;
78
94
  const value = state.internalValue;
79
- const hasOnlyOneValue = Array.isArray(value) && value.length === 1;
95
+ const nbSelection = (Array.isArray(value) && value.length) || 0;
96
+ const hasOnlyOneValue = nbSelection === 1;
97
+ const hasOnlyDisabled = nbSelection <= this.disabledList.length;
80
98
 
81
99
  /* Should not display the clear action if there is only one selected
82
100
  * item in multiple (as this item has already its remove icon) */
83
- return !isMultiple || !hasOnlyOneValue;
101
+ return !isMultiple || !hasOnlyOneValue || !hasOnlyDisabled;
84
102
  }
85
103
 
86
104
  get clearedLabel(): string {
@@ -239,6 +257,11 @@ export default class MainInput extends Vue<Props> {
239
257
  /* Check if there is enough space to display items like there are
240
258
  * currently shown */
241
259
  const el = this.$refs.selectedItems;
260
+
261
+ if (!el) {
262
+ return;
263
+ }
264
+
242
265
  const parentEl = el.parentElement as HTMLDivElement;
243
266
 
244
267
  if (!document.contains(parentEl)) {
@@ -401,7 +424,7 @@ export default class MainInput extends Vue<Props> {
401
424
  >
402
425
  { item.text }
403
426
  </span>
404
- {!this.isDisabled && (
427
+ {!this.isDisabled && !item.disabled && (
405
428
  <Icon
406
429
  icon="times"
407
430
  class="selectic-input__selected-items__icon"