selectic 3.0.17 → 3.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/selectic.common.js +76 -23
- package/dist/selectic.esm.js +76 -23
- package/doc/params.md +26 -0
- package/package.json +5 -5
- package/src/ExtendedList.tsx +54 -16
- package/src/Filter.tsx +31 -8
- package/src/MainInput.tsx +1 -1
- package/src/Store.tsx +13 -0
- package/src/css/selectic.css +8 -0
- package/src/index.tsx +5 -0
- package/src/tools.ts +3 -2
- package/test/helper.js +1 -0
- package/types/ExtendedList.d.ts +4 -0
- package/types/Filter.d.ts +3 -0
- package/types/Store.d.ts +3 -0
- package/types/index.d.ts +2 -1
package/dist/selectic.common.js
CHANGED
|
@@ -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 z-index: 2000;\n height: auto;\n background-color: var(--selectic-bg, #ffffff);\n box-shadow: 2px 5px 12px 0px #888888;\n border-radius: 0 0 4px 4px;\n padding: 0;\n min-width: 200px;\n}\n.selectic__extended-list__list-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.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/* }}} */\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.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.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/* }}} */\n";
|
|
36
36
|
styleInject(css_248z);
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -101,11 +101,12 @@ function assignObject(obj, ...sourceObjects) {
|
|
|
101
101
|
const result = obj;
|
|
102
102
|
for (const source of sourceObjects) {
|
|
103
103
|
for (const key of Object.keys(source)) {
|
|
104
|
-
const
|
|
104
|
+
const typedKey = key;
|
|
105
|
+
const value = source[typedKey];
|
|
105
106
|
if (value === undefined) {
|
|
106
107
|
continue;
|
|
107
108
|
}
|
|
108
|
-
result[
|
|
109
|
+
result[typedKey] = value;
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
112
|
return result;
|
|
@@ -182,6 +183,7 @@ class SelecticStore {
|
|
|
182
183
|
isOpen: false,
|
|
183
184
|
searchText: '',
|
|
184
185
|
selectionIsExcluded: false,
|
|
186
|
+
forceSelectAll: 'auto',
|
|
185
187
|
allOptions: [],
|
|
186
188
|
dynOptions: [],
|
|
187
189
|
filteredOptions: [],
|
|
@@ -1405,7 +1407,7 @@ let MainInput = class MainInput extends vtyx.Vue {
|
|
|
1405
1407
|
}
|
|
1406
1408
|
/* }}} */
|
|
1407
1409
|
render() {
|
|
1408
|
-
return (vtyx.h("div", { class: "has-feedback", on: {
|
|
1410
|
+
return (vtyx.h("div", { class: "selectic-container has-feedback", on: {
|
|
1409
1411
|
'click.prevent.stop': () => this.toggleFocus(),
|
|
1410
1412
|
} },
|
|
1411
1413
|
vtyx.h("div", { id: this.selecticId, class: ['selectic-input form-control',
|
|
@@ -1477,14 +1479,31 @@ let FilterPanel = class FilterPanel extends vtyx.Vue {
|
|
|
1477
1479
|
get selectionIsExcluded() {
|
|
1478
1480
|
return this.store.state.selectionIsExcluded;
|
|
1479
1481
|
}
|
|
1482
|
+
/* {{{ select all */
|
|
1483
|
+
get hasNotAllItems() {
|
|
1484
|
+
return !vue.unref(this.store.hasAllItems);
|
|
1485
|
+
}
|
|
1486
|
+
get disabledPartialData() {
|
|
1487
|
+
const state = this.store.state;
|
|
1488
|
+
const autoDisplay = state.forceSelectAll === 'auto';
|
|
1489
|
+
return this.hasNotAllItems && !this.enableRevert && autoDisplay;
|
|
1490
|
+
}
|
|
1480
1491
|
get disableSelectAll() {
|
|
1481
1492
|
const store = this.store;
|
|
1482
1493
|
const state = store.state;
|
|
1483
1494
|
const isMultiple = state.multiple;
|
|
1484
|
-
const
|
|
1485
|
-
const canNotSelect =
|
|
1486
|
-
|
|
1495
|
+
const hasNoItems = state.filteredOptions.length === 0;
|
|
1496
|
+
const canNotSelect = this.hasNotAllItems && !!state.searchText;
|
|
1497
|
+
const partialDataDsbld = this.disabledPartialData;
|
|
1498
|
+
return !isMultiple || hasNoItems || canNotSelect || partialDataDsbld;
|
|
1499
|
+
}
|
|
1500
|
+
get titleSelectAll() {
|
|
1501
|
+
if (this.disableSelectAll && this.disabledPartialData) {
|
|
1502
|
+
return this.store.data.labels.cannotSelectAllRevertItems;
|
|
1503
|
+
}
|
|
1504
|
+
return '';
|
|
1487
1505
|
}
|
|
1506
|
+
/* }}} */
|
|
1488
1507
|
get disableRevert() {
|
|
1489
1508
|
const store = this.store;
|
|
1490
1509
|
return !store.state.multiple || !vue.unref(store.hasFetchedAllItems);
|
|
@@ -1572,7 +1591,7 @@ let FilterPanel = class FilterPanel extends vtyx.Vue {
|
|
|
1572
1591
|
vtyx.h("label", { class: ['control-label', {
|
|
1573
1592
|
'selectic__label-disabled': this.disableSelectAll,
|
|
1574
1593
|
}] },
|
|
1575
|
-
vtyx.h("input", { type: "checkbox", checked: state.status.areAllSelected, disabled: this.disableSelectAll, on: {
|
|
1594
|
+
vtyx.h("input", { type: "checkbox", checked: state.status.areAllSelected, disabled: this.disableSelectAll, title: this.titleSelectAll, on: {
|
|
1576
1595
|
change: this.onSelectAll,
|
|
1577
1596
|
} }),
|
|
1578
1597
|
labels.selectAll))),
|
|
@@ -1832,6 +1851,10 @@ var __decorate$1 = (this && this.__decorate) || function (decorators, target, ke
|
|
|
1832
1851
|
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;
|
|
1833
1852
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1834
1853
|
};
|
|
1854
|
+
/* list estimation height
|
|
1855
|
+
* 30px × 10 + 20px (for panel header)
|
|
1856
|
+
*/
|
|
1857
|
+
const DEFAULT_LIST_HEIGHT = 320;
|
|
1835
1858
|
let ExtendedList = class ExtendedList extends vtyx.Vue {
|
|
1836
1859
|
constructor() {
|
|
1837
1860
|
/* {{{ props */
|
|
@@ -1839,11 +1862,18 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
|
|
|
1839
1862
|
/* }}} */
|
|
1840
1863
|
/* {{{ data */
|
|
1841
1864
|
this.topGroup = ' ';
|
|
1842
|
-
this.listHeight =
|
|
1865
|
+
this.listHeight = 0;
|
|
1843
1866
|
this.listWidth = 200;
|
|
1867
|
+
this.availableSpace = 0;
|
|
1844
1868
|
}
|
|
1845
1869
|
/* }}} */
|
|
1846
1870
|
/* {{{ computed */
|
|
1871
|
+
/** check if the height of the box has been completely estimated. */
|
|
1872
|
+
get isFullyEstimated() {
|
|
1873
|
+
const listHeight = this.listHeight;
|
|
1874
|
+
const availableSpace = this.availableSpace;
|
|
1875
|
+
return listHeight !== 0 && listHeight < availableSpace;
|
|
1876
|
+
}
|
|
1847
1877
|
get searchingLabel() {
|
|
1848
1878
|
return this.store.data.labels.searching;
|
|
1849
1879
|
}
|
|
@@ -1904,17 +1934,30 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
|
|
|
1904
1934
|
}
|
|
1905
1935
|
get bestPosition() {
|
|
1906
1936
|
const windowHeight = window.innerHeight;
|
|
1907
|
-
const
|
|
1937
|
+
const isFullyEstimated = this.isFullyEstimated;
|
|
1938
|
+
/* XXX: The max() is because if listHeight is greater than default,
|
|
1939
|
+
* it means that the value is more accurate than the default. */
|
|
1940
|
+
const listHeight = isFullyEstimated ? this.listHeight
|
|
1941
|
+
: Math.max(DEFAULT_LIST_HEIGHT, this.listHeight);
|
|
1908
1942
|
const inputTop = this.elementTop;
|
|
1909
1943
|
const inputBottom = this.elementBottom;
|
|
1910
|
-
|
|
1944
|
+
const availableTop = inputTop;
|
|
1945
|
+
const availableBottom = windowHeight - inputBottom;
|
|
1946
|
+
if (listHeight < availableBottom) {
|
|
1911
1947
|
return 'bottom';
|
|
1912
1948
|
}
|
|
1913
|
-
if (listHeight <
|
|
1949
|
+
if (listHeight < availableTop) {
|
|
1914
1950
|
return 'top';
|
|
1915
1951
|
}
|
|
1916
1952
|
/* There are not enough space neither at bottom nor at top */
|
|
1917
|
-
return
|
|
1953
|
+
return availableBottom < availableTop ? 'top' : 'bottom';
|
|
1954
|
+
}
|
|
1955
|
+
get position() {
|
|
1956
|
+
const listPosition = this.store.state.listPosition;
|
|
1957
|
+
if (listPosition === 'auto') {
|
|
1958
|
+
return this.bestPosition;
|
|
1959
|
+
}
|
|
1960
|
+
return listPosition;
|
|
1918
1961
|
}
|
|
1919
1962
|
get horizontalStyle() {
|
|
1920
1963
|
const windowWidth = window.innerWidth;
|
|
@@ -1934,26 +1977,32 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
|
|
|
1934
1977
|
return `left: ${inputLeft}px; min-width: unset;`;
|
|
1935
1978
|
}
|
|
1936
1979
|
get positionStyle() {
|
|
1937
|
-
|
|
1980
|
+
const listPosition = this.position;
|
|
1938
1981
|
const horizontalStyle = this.horizontalStyle;
|
|
1939
|
-
|
|
1940
|
-
listPosition = this.bestPosition;
|
|
1941
|
-
}
|
|
1982
|
+
const width = this.width;
|
|
1942
1983
|
if (listPosition === 'top') {
|
|
1943
1984
|
const transform = horizontalStyle.includes('transform')
|
|
1944
1985
|
? 'transform: translateX(-100%) translateY(-100%);'
|
|
1945
1986
|
: 'transform: translateY(-100%);';
|
|
1987
|
+
const elementTop = this.elementTop;
|
|
1988
|
+
const availableSpace = this.elementTop;
|
|
1989
|
+
this.availableSpace = availableSpace;
|
|
1946
1990
|
return `
|
|
1947
|
-
top: ${
|
|
1991
|
+
--top-position: ${elementTop}px;
|
|
1948
1992
|
${horizontalStyle}
|
|
1949
|
-
width: ${
|
|
1950
|
-
${transform}
|
|
1993
|
+
--list-width: ${width}px;
|
|
1994
|
+
${transform};
|
|
1995
|
+
--availableSpace: ${availableSpace}px;
|
|
1951
1996
|
`;
|
|
1952
1997
|
}
|
|
1998
|
+
const elementBottom = this.elementBottom;
|
|
1999
|
+
const availableSpace = window.innerHeight - elementBottom;
|
|
2000
|
+
this.availableSpace = availableSpace;
|
|
1953
2001
|
return `
|
|
1954
|
-
top: ${
|
|
2002
|
+
--top-position: ${elementBottom}px;
|
|
1955
2003
|
${horizontalStyle}
|
|
1956
|
-
width: ${
|
|
2004
|
+
--list-width: ${width}px;
|
|
2005
|
+
--availableSpace: ${availableSpace}px;
|
|
1957
2006
|
`;
|
|
1958
2007
|
}
|
|
1959
2008
|
/* }}} */
|
|
@@ -1996,7 +2045,10 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
|
|
|
1996
2045
|
const state = store.state;
|
|
1997
2046
|
const isGroup = state.groups.size > 0 &&
|
|
1998
2047
|
state.totalFilteredOptions > store.data.itemsPerPage;
|
|
1999
|
-
return (vtyx.h("div", { style: this.positionStyle, class:
|
|
2048
|
+
return (vtyx.h("div", { style: this.positionStyle, class: [
|
|
2049
|
+
'selectic selectic__extended-list',
|
|
2050
|
+
`selectic-position-${this.position}`,
|
|
2051
|
+
] },
|
|
2000
2052
|
!state.hideFilter && (vtyx.h(Filter, { store: this.store })),
|
|
2001
2053
|
isGroup && (vtyx.h("span", { class: "selectic-item selectic-item--header selectic-item__is-group" }, this.topGroup)),
|
|
2002
2054
|
vtyx.h(List$1, { store: store, on: {
|
|
@@ -2463,6 +2515,7 @@ let Selectic = class Selectic extends vtyx.Vue {
|
|
|
2463
2515
|
pageSize: this.params.pageSize || 100,
|
|
2464
2516
|
hideFilter: (_b = this.params.hideFilter) !== null && _b !== void 0 ? _b : 'auto',
|
|
2465
2517
|
allowRevert: this.params.allowRevert,
|
|
2518
|
+
forceSelectAll: this.params.forceSelectAll || 'auto',
|
|
2466
2519
|
allowClearSelection: this.params.allowClearSelection || false,
|
|
2467
2520
|
autoSelect: this.params.autoSelect === undefined
|
|
2468
2521
|
? !this.multiple && !this.params.fetchCallback
|
package/dist/selectic.esm.js
CHANGED
|
@@ -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 z-index: 2000;\n height: auto;\n background-color: var(--selectic-bg, #ffffff);\n box-shadow: 2px 5px 12px 0px #888888;\n border-radius: 0 0 4px 4px;\n padding: 0;\n min-width: 200px;\n}\n.selectic__extended-list__list-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.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/* }}} */\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\n.selectic-item__disabled {\n color: var(--selectic-color-disabled);\n background-color: var(--selectic-bg-disabled);\n}\n\n.selectic-item__is-in-group {\n padding-left: 2em;\n}\n\n.selectic-item__is-group {\n font-weight: bold;\n cursor: default;\n}\n\n.selectic-item.selected {\n color: var(--selectic-selected-item-color);\n}\n.selectic-search-scope {\n color: #e0e0e0;\n left: auto;\n right: 10px;\n}\n.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/* }}} */\n";
|
|
32
32
|
styleInject(css_248z);
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -97,11 +97,12 @@ function assignObject(obj, ...sourceObjects) {
|
|
|
97
97
|
const result = obj;
|
|
98
98
|
for (const source of sourceObjects) {
|
|
99
99
|
for (const key of Object.keys(source)) {
|
|
100
|
-
const
|
|
100
|
+
const typedKey = key;
|
|
101
|
+
const value = source[typedKey];
|
|
101
102
|
if (value === undefined) {
|
|
102
103
|
continue;
|
|
103
104
|
}
|
|
104
|
-
result[
|
|
105
|
+
result[typedKey] = value;
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
return result;
|
|
@@ -178,6 +179,7 @@ class SelecticStore {
|
|
|
178
179
|
isOpen: false,
|
|
179
180
|
searchText: '',
|
|
180
181
|
selectionIsExcluded: false,
|
|
182
|
+
forceSelectAll: 'auto',
|
|
181
183
|
allOptions: [],
|
|
182
184
|
dynOptions: [],
|
|
183
185
|
filteredOptions: [],
|
|
@@ -1401,7 +1403,7 @@ let MainInput = class MainInput extends Vue {
|
|
|
1401
1403
|
}
|
|
1402
1404
|
/* }}} */
|
|
1403
1405
|
render() {
|
|
1404
|
-
return (h("div", { class: "has-feedback", on: {
|
|
1406
|
+
return (h("div", { class: "selectic-container has-feedback", on: {
|
|
1405
1407
|
'click.prevent.stop': () => this.toggleFocus(),
|
|
1406
1408
|
} },
|
|
1407
1409
|
h("div", { id: this.selecticId, class: ['selectic-input form-control',
|
|
@@ -1473,14 +1475,31 @@ let FilterPanel = class FilterPanel extends Vue {
|
|
|
1473
1475
|
get selectionIsExcluded() {
|
|
1474
1476
|
return this.store.state.selectionIsExcluded;
|
|
1475
1477
|
}
|
|
1478
|
+
/* {{{ select all */
|
|
1479
|
+
get hasNotAllItems() {
|
|
1480
|
+
return !unref(this.store.hasAllItems);
|
|
1481
|
+
}
|
|
1482
|
+
get disabledPartialData() {
|
|
1483
|
+
const state = this.store.state;
|
|
1484
|
+
const autoDisplay = state.forceSelectAll === 'auto';
|
|
1485
|
+
return this.hasNotAllItems && !this.enableRevert && autoDisplay;
|
|
1486
|
+
}
|
|
1476
1487
|
get disableSelectAll() {
|
|
1477
1488
|
const store = this.store;
|
|
1478
1489
|
const state = store.state;
|
|
1479
1490
|
const isMultiple = state.multiple;
|
|
1480
|
-
const
|
|
1481
|
-
const canNotSelect =
|
|
1482
|
-
|
|
1491
|
+
const hasNoItems = state.filteredOptions.length === 0;
|
|
1492
|
+
const canNotSelect = this.hasNotAllItems && !!state.searchText;
|
|
1493
|
+
const partialDataDsbld = this.disabledPartialData;
|
|
1494
|
+
return !isMultiple || hasNoItems || canNotSelect || partialDataDsbld;
|
|
1495
|
+
}
|
|
1496
|
+
get titleSelectAll() {
|
|
1497
|
+
if (this.disableSelectAll && this.disabledPartialData) {
|
|
1498
|
+
return this.store.data.labels.cannotSelectAllRevertItems;
|
|
1499
|
+
}
|
|
1500
|
+
return '';
|
|
1483
1501
|
}
|
|
1502
|
+
/* }}} */
|
|
1484
1503
|
get disableRevert() {
|
|
1485
1504
|
const store = this.store;
|
|
1486
1505
|
return !store.state.multiple || !unref(store.hasFetchedAllItems);
|
|
@@ -1568,7 +1587,7 @@ let FilterPanel = class FilterPanel extends Vue {
|
|
|
1568
1587
|
h("label", { class: ['control-label', {
|
|
1569
1588
|
'selectic__label-disabled': this.disableSelectAll,
|
|
1570
1589
|
}] },
|
|
1571
|
-
h("input", { type: "checkbox", checked: state.status.areAllSelected, disabled: this.disableSelectAll, on: {
|
|
1590
|
+
h("input", { type: "checkbox", checked: state.status.areAllSelected, disabled: this.disableSelectAll, title: this.titleSelectAll, on: {
|
|
1572
1591
|
change: this.onSelectAll,
|
|
1573
1592
|
} }),
|
|
1574
1593
|
labels.selectAll))),
|
|
@@ -1828,6 +1847,10 @@ var __decorate$1 = (this && this.__decorate) || function (decorators, target, ke
|
|
|
1828
1847
|
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;
|
|
1829
1848
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1830
1849
|
};
|
|
1850
|
+
/* list estimation height
|
|
1851
|
+
* 30px × 10 + 20px (for panel header)
|
|
1852
|
+
*/
|
|
1853
|
+
const DEFAULT_LIST_HEIGHT = 320;
|
|
1831
1854
|
let ExtendedList = class ExtendedList extends Vue {
|
|
1832
1855
|
constructor() {
|
|
1833
1856
|
/* {{{ props */
|
|
@@ -1835,11 +1858,18 @@ let ExtendedList = class ExtendedList extends Vue {
|
|
|
1835
1858
|
/* }}} */
|
|
1836
1859
|
/* {{{ data */
|
|
1837
1860
|
this.topGroup = ' ';
|
|
1838
|
-
this.listHeight =
|
|
1861
|
+
this.listHeight = 0;
|
|
1839
1862
|
this.listWidth = 200;
|
|
1863
|
+
this.availableSpace = 0;
|
|
1840
1864
|
}
|
|
1841
1865
|
/* }}} */
|
|
1842
1866
|
/* {{{ computed */
|
|
1867
|
+
/** check if the height of the box has been completely estimated. */
|
|
1868
|
+
get isFullyEstimated() {
|
|
1869
|
+
const listHeight = this.listHeight;
|
|
1870
|
+
const availableSpace = this.availableSpace;
|
|
1871
|
+
return listHeight !== 0 && listHeight < availableSpace;
|
|
1872
|
+
}
|
|
1843
1873
|
get searchingLabel() {
|
|
1844
1874
|
return this.store.data.labels.searching;
|
|
1845
1875
|
}
|
|
@@ -1900,17 +1930,30 @@ let ExtendedList = class ExtendedList extends Vue {
|
|
|
1900
1930
|
}
|
|
1901
1931
|
get bestPosition() {
|
|
1902
1932
|
const windowHeight = window.innerHeight;
|
|
1903
|
-
const
|
|
1933
|
+
const isFullyEstimated = this.isFullyEstimated;
|
|
1934
|
+
/* XXX: The max() is because if listHeight is greater than default,
|
|
1935
|
+
* it means that the value is more accurate than the default. */
|
|
1936
|
+
const listHeight = isFullyEstimated ? this.listHeight
|
|
1937
|
+
: Math.max(DEFAULT_LIST_HEIGHT, this.listHeight);
|
|
1904
1938
|
const inputTop = this.elementTop;
|
|
1905
1939
|
const inputBottom = this.elementBottom;
|
|
1906
|
-
|
|
1940
|
+
const availableTop = inputTop;
|
|
1941
|
+
const availableBottom = windowHeight - inputBottom;
|
|
1942
|
+
if (listHeight < availableBottom) {
|
|
1907
1943
|
return 'bottom';
|
|
1908
1944
|
}
|
|
1909
|
-
if (listHeight <
|
|
1945
|
+
if (listHeight < availableTop) {
|
|
1910
1946
|
return 'top';
|
|
1911
1947
|
}
|
|
1912
1948
|
/* There are not enough space neither at bottom nor at top */
|
|
1913
|
-
return
|
|
1949
|
+
return availableBottom < availableTop ? 'top' : 'bottom';
|
|
1950
|
+
}
|
|
1951
|
+
get position() {
|
|
1952
|
+
const listPosition = this.store.state.listPosition;
|
|
1953
|
+
if (listPosition === 'auto') {
|
|
1954
|
+
return this.bestPosition;
|
|
1955
|
+
}
|
|
1956
|
+
return listPosition;
|
|
1914
1957
|
}
|
|
1915
1958
|
get horizontalStyle() {
|
|
1916
1959
|
const windowWidth = window.innerWidth;
|
|
@@ -1930,26 +1973,32 @@ let ExtendedList = class ExtendedList extends Vue {
|
|
|
1930
1973
|
return `left: ${inputLeft}px; min-width: unset;`;
|
|
1931
1974
|
}
|
|
1932
1975
|
get positionStyle() {
|
|
1933
|
-
|
|
1976
|
+
const listPosition = this.position;
|
|
1934
1977
|
const horizontalStyle = this.horizontalStyle;
|
|
1935
|
-
|
|
1936
|
-
listPosition = this.bestPosition;
|
|
1937
|
-
}
|
|
1978
|
+
const width = this.width;
|
|
1938
1979
|
if (listPosition === 'top') {
|
|
1939
1980
|
const transform = horizontalStyle.includes('transform')
|
|
1940
1981
|
? 'transform: translateX(-100%) translateY(-100%);'
|
|
1941
1982
|
: 'transform: translateY(-100%);';
|
|
1983
|
+
const elementTop = this.elementTop;
|
|
1984
|
+
const availableSpace = this.elementTop;
|
|
1985
|
+
this.availableSpace = availableSpace;
|
|
1942
1986
|
return `
|
|
1943
|
-
top: ${
|
|
1987
|
+
--top-position: ${elementTop}px;
|
|
1944
1988
|
${horizontalStyle}
|
|
1945
|
-
width: ${
|
|
1946
|
-
${transform}
|
|
1989
|
+
--list-width: ${width}px;
|
|
1990
|
+
${transform};
|
|
1991
|
+
--availableSpace: ${availableSpace}px;
|
|
1947
1992
|
`;
|
|
1948
1993
|
}
|
|
1994
|
+
const elementBottom = this.elementBottom;
|
|
1995
|
+
const availableSpace = window.innerHeight - elementBottom;
|
|
1996
|
+
this.availableSpace = availableSpace;
|
|
1949
1997
|
return `
|
|
1950
|
-
top: ${
|
|
1998
|
+
--top-position: ${elementBottom}px;
|
|
1951
1999
|
${horizontalStyle}
|
|
1952
|
-
width: ${
|
|
2000
|
+
--list-width: ${width}px;
|
|
2001
|
+
--availableSpace: ${availableSpace}px;
|
|
1953
2002
|
`;
|
|
1954
2003
|
}
|
|
1955
2004
|
/* }}} */
|
|
@@ -1992,7 +2041,10 @@ let ExtendedList = class ExtendedList extends Vue {
|
|
|
1992
2041
|
const state = store.state;
|
|
1993
2042
|
const isGroup = state.groups.size > 0 &&
|
|
1994
2043
|
state.totalFilteredOptions > store.data.itemsPerPage;
|
|
1995
|
-
return (h("div", { style: this.positionStyle, class:
|
|
2044
|
+
return (h("div", { style: this.positionStyle, class: [
|
|
2045
|
+
'selectic selectic__extended-list',
|
|
2046
|
+
`selectic-position-${this.position}`,
|
|
2047
|
+
] },
|
|
1996
2048
|
!state.hideFilter && (h(Filter, { store: this.store })),
|
|
1997
2049
|
isGroup && (h("span", { class: "selectic-item selectic-item--header selectic-item__is-group" }, this.topGroup)),
|
|
1998
2050
|
h(List$1, { store: store, on: {
|
|
@@ -2459,6 +2511,7 @@ let Selectic = class Selectic extends Vue {
|
|
|
2459
2511
|
pageSize: this.params.pageSize || 100,
|
|
2460
2512
|
hideFilter: (_b = this.params.hideFilter) !== null && _b !== void 0 ? _b : 'auto',
|
|
2461
2513
|
allowRevert: this.params.allowRevert,
|
|
2514
|
+
forceSelectAll: this.params.forceSelectAll || 'auto',
|
|
2462
2515
|
allowClearSelection: this.params.allowClearSelection || false,
|
|
2463
2516
|
autoSelect: this.params.autoSelect === undefined
|
|
2464
2517
|
? !this.multiple && !this.params.fetchCallback
|
package/doc/params.md
CHANGED
|
@@ -278,6 +278,32 @@ Read [the extended properties documentation](extendedProperties.md) for more inf
|
|
|
278
278
|
/>
|
|
279
279
|
```
|
|
280
280
|
|
|
281
|
+
## forceSelectAll
|
|
282
|
+
|
|
283
|
+
Type: `'auto' | 'visible'`
|
|
284
|
+
|
|
285
|
+
Default value: `'auto'`
|
|
286
|
+
|
|
287
|
+
In _multiple_ mode, there is a "select all" action.
|
|
288
|
+
If the selection inversion is not available and all data are not fetched (in
|
|
289
|
+
_dynamic_ mode) it will not be possible to select all items. So this action
|
|
290
|
+
will be disabled.
|
|
291
|
+
|
|
292
|
+
This option allows you to change the behavior.
|
|
293
|
+
|
|
294
|
+
* `'auto'`: The action is disabled when not possible.
|
|
295
|
+
* `'visible'`: The action is displayed even when all data are not fetched.
|
|
296
|
+
|
|
297
|
+
```html
|
|
298
|
+
<selectic
|
|
299
|
+
params={{
|
|
300
|
+
forceSelectAll: 'auto',
|
|
301
|
+
}}
|
|
302
|
+
options={optionList}
|
|
303
|
+
/>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
|
|
281
307
|
## listPosition
|
|
282
308
|
|
|
283
309
|
Type: `'auto' | 'bottom' | 'top'`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "selectic",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.19",
|
|
4
4
|
"description": "Smart Select for VueJS 3.x",
|
|
5
5
|
"main": "dist/selectic.common.js",
|
|
6
6
|
"module": "dist/selectic.esm.js",
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"vtyx": "4.0.5"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@babel/types": "^7.
|
|
44
|
+
"@babel/types": "^7.21.2",
|
|
45
45
|
"rollup": "^2.79.1",
|
|
46
|
-
"rollup-plugin-postcss": "^
|
|
47
|
-
"tape": "^4.16.
|
|
48
|
-
"typescript": "~4.
|
|
46
|
+
"rollup-plugin-postcss": "^4.0.2",
|
|
47
|
+
"tape": "^4.16.2",
|
|
48
|
+
"typescript": "~4.8"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/src/ExtendedList.tsx
CHANGED
|
@@ -20,6 +20,11 @@ export interface Props {
|
|
|
20
20
|
elementRight: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/* list estimation height
|
|
24
|
+
* 30px × 10 + 20px (for panel header)
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_LIST_HEIGHT = 320;
|
|
27
|
+
|
|
23
28
|
@Component
|
|
24
29
|
export default class ExtendedList extends Vue<Props> {
|
|
25
30
|
/* {{{ props */
|
|
@@ -46,12 +51,21 @@ export default class ExtendedList extends Vue<Props> {
|
|
|
46
51
|
/* {{{ data */
|
|
47
52
|
|
|
48
53
|
private topGroup = ' ';
|
|
49
|
-
private listHeight =
|
|
54
|
+
private listHeight = 0;
|
|
50
55
|
private listWidth = 200;
|
|
56
|
+
private availableSpace = 0;
|
|
51
57
|
|
|
52
58
|
/* }}} */
|
|
53
59
|
/* {{{ computed */
|
|
54
60
|
|
|
61
|
+
/** check if the height of the box has been completely estimated. */
|
|
62
|
+
get isFullyEstimated(): boolean {
|
|
63
|
+
const listHeight = this.listHeight;
|
|
64
|
+
const availableSpace = this.availableSpace;
|
|
65
|
+
|
|
66
|
+
return listHeight !== 0 && listHeight < availableSpace;
|
|
67
|
+
}
|
|
68
|
+
|
|
55
69
|
get searchingLabel() {
|
|
56
70
|
return this.store.data.labels.searching;
|
|
57
71
|
}
|
|
@@ -124,20 +138,36 @@ export default class ExtendedList extends Vue<Props> {
|
|
|
124
138
|
|
|
125
139
|
get bestPosition(): 'top' | 'bottom' {
|
|
126
140
|
const windowHeight = window.innerHeight;
|
|
127
|
-
const
|
|
141
|
+
const isFullyEstimated = this.isFullyEstimated;
|
|
142
|
+
/* XXX: The max() is because if listHeight is greater than default,
|
|
143
|
+
* it means that the value is more accurate than the default. */
|
|
144
|
+
const listHeight = isFullyEstimated ? this.listHeight
|
|
145
|
+
: Math.max(DEFAULT_LIST_HEIGHT, this.listHeight);
|
|
128
146
|
const inputTop = this.elementTop;
|
|
129
147
|
const inputBottom = this.elementBottom;
|
|
148
|
+
const availableTop = inputTop;
|
|
149
|
+
const availableBottom = windowHeight - inputBottom;
|
|
130
150
|
|
|
131
|
-
if (
|
|
151
|
+
if (listHeight < availableBottom) {
|
|
132
152
|
return 'bottom';
|
|
133
153
|
}
|
|
134
154
|
|
|
135
|
-
if (listHeight <
|
|
155
|
+
if (listHeight < availableTop) {
|
|
136
156
|
return 'top';
|
|
137
157
|
}
|
|
138
158
|
|
|
139
159
|
/* There are not enough space neither at bottom nor at top */
|
|
140
|
-
return
|
|
160
|
+
return availableBottom < availableTop ? 'top' : 'bottom';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get position(): 'top' | 'bottom' {
|
|
164
|
+
const listPosition = this.store.state.listPosition;
|
|
165
|
+
|
|
166
|
+
if (listPosition === 'auto') {
|
|
167
|
+
return this.bestPosition;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return listPosition;
|
|
141
171
|
}
|
|
142
172
|
|
|
143
173
|
get horizontalStyle(): string {
|
|
@@ -162,30 +192,35 @@ export default class ExtendedList extends Vue<Props> {
|
|
|
162
192
|
}
|
|
163
193
|
|
|
164
194
|
get positionStyle() {
|
|
165
|
-
|
|
195
|
+
const listPosition = this.position;
|
|
166
196
|
const horizontalStyle = this.horizontalStyle;
|
|
167
|
-
|
|
168
|
-
if (listPosition === 'auto') {
|
|
169
|
-
listPosition = this.bestPosition;
|
|
170
|
-
}
|
|
197
|
+
const width = this.width;
|
|
171
198
|
|
|
172
199
|
if (listPosition === 'top') {
|
|
173
200
|
const transform = horizontalStyle.includes('transform')
|
|
174
201
|
? 'transform: translateX(-100%) translateY(-100%);'
|
|
175
202
|
: 'transform: translateY(-100%);';
|
|
203
|
+
const elementTop = this.elementTop;
|
|
204
|
+
const availableSpace = this.elementTop;
|
|
205
|
+
this.availableSpace = availableSpace;
|
|
176
206
|
|
|
177
207
|
return `
|
|
178
|
-
top: ${
|
|
208
|
+
--top-position: ${elementTop}px;
|
|
179
209
|
${horizontalStyle}
|
|
180
|
-
width: ${
|
|
181
|
-
${transform}
|
|
210
|
+
--list-width: ${width}px;
|
|
211
|
+
${transform};
|
|
212
|
+
--availableSpace: ${availableSpace}px;
|
|
182
213
|
`;
|
|
183
214
|
}
|
|
215
|
+
const elementBottom = this.elementBottom;
|
|
216
|
+
const availableSpace = window.innerHeight - elementBottom;
|
|
217
|
+
this.availableSpace = availableSpace;
|
|
184
218
|
|
|
185
219
|
return `
|
|
186
|
-
top: ${
|
|
220
|
+
--top-position: ${elementBottom}px;
|
|
187
221
|
${horizontalStyle}
|
|
188
|
-
width: ${
|
|
222
|
+
--list-width: ${width}px;
|
|
223
|
+
--availableSpace: ${availableSpace}px;
|
|
189
224
|
`;
|
|
190
225
|
}
|
|
191
226
|
|
|
@@ -248,7 +283,10 @@ export default class ExtendedList extends Vue<Props> {
|
|
|
248
283
|
return (
|
|
249
284
|
<div
|
|
250
285
|
style={this.positionStyle}
|
|
251
|
-
class=
|
|
286
|
+
class={[
|
|
287
|
+
'selectic selectic__extended-list',
|
|
288
|
+
`selectic-position-${this.position}`,
|
|
289
|
+
]}
|
|
252
290
|
>
|
|
253
291
|
{!state.hideFilter && (
|
|
254
292
|
<Filter
|
package/src/Filter.tsx
CHANGED
|
@@ -30,31 +30,53 @@ export default class FilterPanel extends Vue<Props> {
|
|
|
30
30
|
/* }}} */
|
|
31
31
|
/* {{{ computed */
|
|
32
32
|
|
|
33
|
-
get searchPlaceholder() {
|
|
33
|
+
get searchPlaceholder(): string {
|
|
34
34
|
return this.store.data.labels.searchPlaceholder;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
get selectionIsExcluded() {
|
|
37
|
+
get selectionIsExcluded(): boolean {
|
|
38
38
|
return this.store.state.selectionIsExcluded;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
/* {{{ select all */
|
|
42
|
+
|
|
43
|
+
get hasNotAllItems(): boolean {
|
|
44
|
+
return !unref(this.store.hasAllItems);
|
|
45
|
+
}
|
|
46
|
+
get disabledPartialData(): boolean {
|
|
47
|
+
const state = this.store.state;
|
|
48
|
+
const autoDisplay = state.forceSelectAll === 'auto';
|
|
49
|
+
return this.hasNotAllItems && !this.enableRevert && autoDisplay;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get disableSelectAll(): boolean {
|
|
42
53
|
const store = this.store;
|
|
43
54
|
const state = store.state;
|
|
44
55
|
const isMultiple = state.multiple;
|
|
45
|
-
const
|
|
46
|
-
const canNotSelect =
|
|
56
|
+
const hasNoItems = state.filteredOptions.length === 0;
|
|
57
|
+
const canNotSelect = this.hasNotAllItems && !!state.searchText;
|
|
58
|
+
const partialDataDsbld = this.disabledPartialData;
|
|
47
59
|
|
|
48
|
-
return !isMultiple ||
|
|
60
|
+
return !isMultiple || hasNoItems || canNotSelect || partialDataDsbld;
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
get
|
|
63
|
+
get titleSelectAll(): string {
|
|
64
|
+
if (this.disableSelectAll && this.disabledPartialData) {
|
|
65
|
+
return this.store.data.labels.cannotSelectAllRevertItems;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* }}} */
|
|
72
|
+
|
|
73
|
+
get disableRevert(): boolean {
|
|
52
74
|
const store = this.store;
|
|
53
75
|
|
|
54
76
|
return !store.state.multiple || !unref(store.hasFetchedAllItems);
|
|
55
77
|
}
|
|
56
78
|
|
|
57
|
-
get enableRevert() {
|
|
79
|
+
get enableRevert(): boolean {
|
|
58
80
|
const state = this.store.state;
|
|
59
81
|
|
|
60
82
|
return state.multiple && state.allowRevert !== false;
|
|
@@ -178,6 +200,7 @@ export default class FilterPanel extends Vue<Props> {
|
|
|
178
200
|
type="checkbox"
|
|
179
201
|
checked={state.status.areAllSelected}
|
|
180
202
|
disabled={this.disableSelectAll}
|
|
203
|
+
title={this.titleSelectAll}
|
|
181
204
|
on={{
|
|
182
205
|
change: this.onSelectAll,
|
|
183
206
|
}}
|
package/src/MainInput.tsx
CHANGED
package/src/Store.tsx
CHANGED
|
@@ -83,6 +83,12 @@ export type HideFilter =
|
|
|
83
83
|
/* The panel filter is always open */
|
|
84
84
|
| 'open';
|
|
85
85
|
|
|
86
|
+
export type SelectAllOption =
|
|
87
|
+
/* Display the "select all" only when data are all fetched or allowRevert */
|
|
88
|
+
'auto'
|
|
89
|
+
/* Always display the "select all" in mulitple mode. */
|
|
90
|
+
| 'visible';
|
|
91
|
+
|
|
86
92
|
export interface SelecticStoreStateParams {
|
|
87
93
|
/* Equivalent of <select>'s "multiple" attribute */
|
|
88
94
|
multiple?: boolean;
|
|
@@ -101,6 +107,9 @@ export interface SelecticStoreStateParams {
|
|
|
101
107
|
*/
|
|
102
108
|
allowRevert?: boolean;
|
|
103
109
|
|
|
110
|
+
/* Force the availability of the "select all" even if all data is not fetched yet. */
|
|
111
|
+
forceSelectAll?: SelectAllOption;
|
|
112
|
+
|
|
104
113
|
/* Allow user to clear current selection */
|
|
105
114
|
allowClearSelection?: boolean;
|
|
106
115
|
|
|
@@ -295,6 +304,9 @@ export interface SelecticStoreState {
|
|
|
295
304
|
/* Indicate where the list should be deployed */
|
|
296
305
|
listPosition: ListPosition;
|
|
297
306
|
|
|
307
|
+
/* If true, the "select All" is still available even if all data are not fetched yet. */
|
|
308
|
+
forceSelectAll: SelectAllOption;
|
|
309
|
+
|
|
298
310
|
/* Inner status which should be modified only by store */
|
|
299
311
|
status: {
|
|
300
312
|
/* If true, a search is currently done */
|
|
@@ -442,6 +454,7 @@ export default class SelecticStore {
|
|
|
442
454
|
isOpen: false,
|
|
443
455
|
searchText: '',
|
|
444
456
|
selectionIsExcluded: false,
|
|
457
|
+
forceSelectAll: 'auto',
|
|
445
458
|
allOptions: [],
|
|
446
459
|
dynOptions: [],
|
|
447
460
|
filteredOptions: [],
|
package/src/css/selectic.css
CHANGED
|
@@ -184,13 +184,21 @@
|
|
|
184
184
|
|
|
185
185
|
.selectic__extended-list {
|
|
186
186
|
position: fixed;
|
|
187
|
+
top: var(--top-position, 0);
|
|
187
188
|
z-index: 2000;
|
|
188
189
|
height: auto;
|
|
190
|
+
max-height: var(--availableSpace);
|
|
189
191
|
background-color: var(--selectic-bg, #ffffff);
|
|
190
192
|
box-shadow: 2px 5px 12px 0px #888888;
|
|
191
193
|
border-radius: 0 0 4px 4px;
|
|
192
194
|
padding: 0;
|
|
195
|
+
width: var(--list-width, 200px);
|
|
193
196
|
min-width: 200px;
|
|
197
|
+
display: grid;
|
|
198
|
+
grid-template-rows: minmax(0, max-content) 1fr;
|
|
199
|
+
}
|
|
200
|
+
.selectic__extended-list.selectic-position-top {
|
|
201
|
+
box-shadow: 2px -3px 12px 0px #888888;
|
|
194
202
|
}
|
|
195
203
|
.selectic__extended-list__list-container{
|
|
196
204
|
overflow: auto;
|
package/src/index.tsx
CHANGED
|
@@ -40,6 +40,7 @@ import Store, {
|
|
|
40
40
|
SelectionOverflow,
|
|
41
41
|
ListPosition,
|
|
42
42
|
HideFilter,
|
|
43
|
+
SelectAllOption,
|
|
43
44
|
} from './Store';
|
|
44
45
|
import MainInput from './MainInput';
|
|
45
46
|
import ExtendedList from './ExtendedList';
|
|
@@ -98,6 +99,9 @@ export interface ParamProps {
|
|
|
98
99
|
*/
|
|
99
100
|
allowRevert?: boolean;
|
|
100
101
|
|
|
102
|
+
/* If true, the "select All" is still available even if all data are not fetched yet. */
|
|
103
|
+
forceSelectAll?: SelectAllOption;
|
|
104
|
+
|
|
101
105
|
/* Allow user to clear the current selection */
|
|
102
106
|
allowClearSelection?: boolean;
|
|
103
107
|
|
|
@@ -794,6 +798,7 @@ export default class Selectic extends Vue<Props> {
|
|
|
794
798
|
pageSize: this.params.pageSize || 100,
|
|
795
799
|
hideFilter: this.params.hideFilter ?? 'auto',
|
|
796
800
|
allowRevert: this.params.allowRevert, /* it can be undefined */
|
|
801
|
+
forceSelectAll: this.params.forceSelectAll || 'auto',
|
|
797
802
|
allowClearSelection: this.params.allowClearSelection || false,
|
|
798
803
|
autoSelect: this.params.autoSelect === undefined
|
|
799
804
|
? !this.multiple && !this.params.fetchCallback
|
package/src/tools.ts
CHANGED
|
@@ -76,11 +76,12 @@ export function assignObject<T>(obj: Partial<T>, ...sourceObjects: Array<Partial
|
|
|
76
76
|
const result = obj;
|
|
77
77
|
for (const source of sourceObjects) {
|
|
78
78
|
for (const key of Object.keys(source)) {
|
|
79
|
-
const
|
|
79
|
+
const typedKey = key as keyof T;
|
|
80
|
+
const value = source[typedKey];
|
|
80
81
|
if (value === undefined) {
|
|
81
82
|
continue;
|
|
82
83
|
}
|
|
83
|
-
result[
|
|
84
|
+
result[typedKey] = value!;
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
return result as T;
|
package/test/helper.js
CHANGED
package/types/ExtendedList.d.ts
CHANGED
|
@@ -18,12 +18,16 @@ export default class ExtendedList extends Vue<Props> {
|
|
|
18
18
|
private topGroup;
|
|
19
19
|
private listHeight;
|
|
20
20
|
private listWidth;
|
|
21
|
+
private availableSpace;
|
|
22
|
+
/** check if the height of the box has been completely estimated. */
|
|
23
|
+
get isFullyEstimated(): boolean;
|
|
21
24
|
get searchingLabel(): string;
|
|
22
25
|
get searching(): boolean;
|
|
23
26
|
get errorMessage(): string;
|
|
24
27
|
get infoMessage(): string;
|
|
25
28
|
get onKeyDown(): (evt: KeyboardEvent) => void;
|
|
26
29
|
get bestPosition(): 'top' | 'bottom';
|
|
30
|
+
get position(): 'top' | 'bottom';
|
|
27
31
|
get horizontalStyle(): string;
|
|
28
32
|
get positionStyle(): string;
|
|
29
33
|
onFilteredOptionsChange(): void;
|
package/types/Filter.d.ts
CHANGED
|
@@ -11,7 +11,10 @@ export default class FilterPanel extends Vue<Props> {
|
|
|
11
11
|
private closed;
|
|
12
12
|
get searchPlaceholder(): string;
|
|
13
13
|
get selectionIsExcluded(): boolean;
|
|
14
|
+
get hasNotAllItems(): boolean;
|
|
15
|
+
get disabledPartialData(): boolean;
|
|
14
16
|
get disableSelectAll(): boolean;
|
|
17
|
+
get titleSelectAll(): string;
|
|
15
18
|
get disableRevert(): boolean;
|
|
16
19
|
get enableRevert(): boolean;
|
|
17
20
|
get onKeyPressed(): (evt: KeyboardEvent) => void;
|
package/types/Store.d.ts
CHANGED
|
@@ -39,11 +39,13 @@ export declare type FormatCallback = (_option: OptionItem) => OptionItem;
|
|
|
39
39
|
export declare type SelectionOverflow = 'collapsed' | 'multiline';
|
|
40
40
|
export declare type ListPosition = 'bottom' | 'top' | 'auto';
|
|
41
41
|
export declare type HideFilter = boolean | 'auto' | 'open';
|
|
42
|
+
export declare type SelectAllOption = 'auto' | 'visible';
|
|
42
43
|
export interface SelecticStoreStateParams {
|
|
43
44
|
multiple?: boolean;
|
|
44
45
|
placeholder?: string;
|
|
45
46
|
hideFilter?: HideFilter;
|
|
46
47
|
allowRevert?: boolean;
|
|
48
|
+
forceSelectAll?: SelectAllOption;
|
|
47
49
|
allowClearSelection?: boolean;
|
|
48
50
|
pageSize?: number;
|
|
49
51
|
autoSelect?: boolean;
|
|
@@ -110,6 +112,7 @@ export interface SelecticStoreState {
|
|
|
110
112
|
optionBehaviorOperation: OptionBehaviorOperation;
|
|
111
113
|
optionBehaviorOrder: OptionBehaviorOrder[];
|
|
112
114
|
listPosition: ListPosition;
|
|
115
|
+
forceSelectAll: SelectAllOption;
|
|
113
116
|
status: {
|
|
114
117
|
searching: boolean;
|
|
115
118
|
errorMessage: string;
|
package/types/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Vue, h } from 'vtyx';
|
|
2
2
|
import './css/selectic.css';
|
|
3
|
-
import { OptionProp, OptionId, StrictOptionId, GroupValue, SelectedValue, FetchCallback, GetCallback, PartialMessages, OptionValue, OptionItem, FormatCallback, SelectionOverflow, ListPosition, HideFilter } from './Store';
|
|
3
|
+
import { OptionProp, OptionId, StrictOptionId, GroupValue, SelectedValue, FetchCallback, GetCallback, PartialMessages, OptionValue, OptionItem, FormatCallback, SelectionOverflow, ListPosition, HideFilter, SelectAllOption } from './Store';
|
|
4
4
|
import MainInput from './MainInput';
|
|
5
5
|
import ExtendedList from './ExtendedList';
|
|
6
6
|
export { GroupValue, OptionValue, OptionItem, OptionProp, OptionId, StrictOptionId, SelectedValue, PartialMessages, GetCallback, FetchCallback, FormatCallback, SelectionOverflow, ListPosition, HideFilter, };
|
|
@@ -19,6 +19,7 @@ export interface ParamProps {
|
|
|
19
19
|
pageSize?: number;
|
|
20
20
|
hideFilter?: HideFilter;
|
|
21
21
|
allowRevert?: boolean;
|
|
22
|
+
forceSelectAll?: SelectAllOption;
|
|
22
23
|
allowClearSelection?: boolean;
|
|
23
24
|
autoSelect?: boolean;
|
|
24
25
|
autoDisabled?: boolean;
|