selectic 3.0.18 → 3.0.20

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/.tool-versions ADDED
@@ -0,0 +1,5 @@
1
+ # ASDF configuration file for mmsx repository.
2
+ #
3
+ # https://asdf-vm.com/manage/configuration.html
4
+
5
+ nodejs 12.18.4 system
package/README.md CHANGED
@@ -12,51 +12,66 @@ reverse selection, and many other possibilities.
12
12
 
13
13
  It integrates well with VueJS and is reactive to option changes.
14
14
 
15
+ Typescript types are provided.
16
+
15
17
  There are very few dependencies and code stays very small (~90kB).
16
18
 
19
+ ![example of a Selectic component](./doc/images/selectic_example.png)
20
+
17
21
  ## Example
18
22
 
19
23
  ```html
20
24
  <Selectic
21
- :options="['first choice', 'second choice', 'third choice']"
22
- v-model="selection"
25
+ :options="['first choice', 'second choice', 'third choice']"
26
+ v-model="selection"
23
27
  />
24
28
 
25
29
  <Selectic
26
- value="item2"
27
- :options="[{
28
- id: 'item1',
29
- text: 'The first item',
30
- icon: 'fa fa-thumbs-o-up',
31
- }, {
32
- id: 'item2',
33
- text: 'Another item',
34
- title: 'second choice',
35
- }, {
36
- id: 'item3',
37
- text: 'Disabled item',
38
- disabled: true,
39
- }]"
40
- multiple
41
-
42
- @input="onChange"
30
+ multiple
31
+ value="item2"
32
+ :options="[{
33
+ id: 'item1',
34
+ text: 'The first item',
35
+ icon: 'fa fa-thumbs-o-up',
36
+ }, {
37
+ id: 'item2',
38
+ text: 'Another item',
39
+ title: 'second choice',
40
+ }, {
41
+ id: 'item3',
42
+ text: 'Disabled item',
43
+ disabled: true,
44
+ }]"
45
+
46
+ @input="onChange"
43
47
  />
44
48
 
45
- <Selectic>
46
- <optgroup label="Animals">
47
- <option>Cat</option>
48
- <option value="dog">Dog</option>
49
- <option :value="42">42 goldfishes</option>
50
- </optgroup>
51
- </Selectic>
49
+ <Selectic
50
+ :options="[{
51
+ id: 'animals',
52
+ text: 'Animals',
53
+ options: [{
54
+ id: 'cat',
55
+ text: 'Cat',
56
+ }, {
57
+ id: 'dog',
58
+ text: 'A dog',
59
+ }, {
60
+ id: 42,
61
+ text: '42 goldfishes',
62
+ }],
63
+ }]"
64
+ />
52
65
  ```
53
66
 
67
+ [Full documentation](./doc/main.md)
68
+
54
69
  ## Features
55
70
 
56
71
  * List of items (either string array or object array).
57
72
  * Can load dynamically list from a server and the list can be paginate (with a
58
73
  cache system to avoid reloading previous requests).
59
- * Slots: options may be added from Vue template (by writing explicit `<option>` or `<optgroup>`) in a reactive way _(currently disabled in 3.0.0)_.
74
+ * ~~Slots: options may be added from Vue template (by writing explicit `<option>` or `<optgroup>`) in a reactive way~~ _(currently disabled in 3.0.0+)_.
60
75
  * Multi-sources: Possibility to combine options from different sources (static, dynamic or slots) or to use the other as fallback (if the list is empty).
61
76
  * Supports basic Select properties like `multiple`, `disabled`, `title`
62
77
  * Supports group element (equivalent of optGroup), even for dynamic list.
@@ -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
  /**
@@ -111,6 +111,33 @@ function assignObject(obj, ...sourceObjects) {
111
111
  }
112
112
  return result;
113
113
  }
114
+ /** Compare 2 list of options.
115
+ * @returns true if there are no difference
116
+ */
117
+ function compareOptions(oldOptions, newOptions) {
118
+ if (oldOptions.length !== newOptions.length) {
119
+ return false;
120
+ }
121
+ return oldOptions.every((oldOption, idx) => {
122
+ const newOption = newOptions[idx];
123
+ const keys = Object.keys(oldOption);
124
+ if (keys.length !== Object.keys(newOption).length) {
125
+ return false;
126
+ }
127
+ return keys.every((optionKey) => {
128
+ const key = optionKey;
129
+ const oldValue = oldOption[key];
130
+ const newValue = newOption[key];
131
+ if (key === 'options') {
132
+ return compareOptions(oldValue, newValue);
133
+ }
134
+ if (key === 'data') {
135
+ return JSON.stringify(oldValue) === JSON.stringify(newValue);
136
+ }
137
+ return oldValue === newValue;
138
+ });
139
+ });
140
+ }
114
141
 
115
142
  /* File Purpose:
116
143
  * It keeps and computes all states at a single place.
@@ -140,6 +167,7 @@ let messages = {
140
167
  moreSelectedItem: '+1 other',
141
168
  moreSelectedItems: '+%d others',
142
169
  unknownPropertyValue: 'property "%s" has incorrect values.',
170
+ wrongQueryResult: 'Query did not return all results.',
143
171
  };
144
172
  let closePreviousSelectic;
145
173
  /* }}} */
@@ -767,7 +795,7 @@ class SelecticStore {
767
795
  });
768
796
  return childOptions;
769
797
  }
770
- buildAllOptions(keepFetched = false) {
798
+ buildAllOptions(keepFetched = false, stopFetch = false) {
771
799
  const allOptions = [];
772
800
  let listOptions = [];
773
801
  let elementOptions = [];
@@ -838,12 +866,26 @@ class SelecticStore {
838
866
  this.state.totalAllOptions = allOptions.length;
839
867
  }
840
868
  }
841
- this.state.filteredOptions = [];
842
- this.state.totalFilteredOptions = Infinity;
843
- this.buildFilteredOptions().then(() => {
844
- /* XXX: To recompute for strict mode and auto-select */
845
- this.assertCorrectValue();
846
- });
869
+ if (!stopFetch) {
870
+ this.state.filteredOptions = [];
871
+ this.state.totalFilteredOptions = Infinity;
872
+ this.buildFilteredOptions().then(() => {
873
+ /* XXX: To recompute for strict mode and auto-select */
874
+ this.assertCorrectValue();
875
+ });
876
+ }
877
+ else {
878
+ /* Do not fetch again just build filteredOptions */
879
+ const search = this.state.searchText;
880
+ if (!search) {
881
+ this.state.filteredOptions = this.buildGroupItems(allOptions);
882
+ this.state.totalFilteredOptions = this.state.filteredOptions.length;
883
+ return;
884
+ }
885
+ const options = this.filterOptions(allOptions, search);
886
+ this.state.filteredOptions = options;
887
+ this.state.totalFilteredOptions = this.state.filteredOptions.length;
888
+ }
847
889
  }
848
890
  async buildFilteredOptions() {
849
891
  if (!this.state.isOpen) {
@@ -975,6 +1017,7 @@ class SelecticStore {
975
1017
  const requestId = ++this.requestId;
976
1018
  const { total: rTotal, result } = await fetchCallback(search, offset, limit);
977
1019
  let total = rTotal;
1020
+ let errorMessage = '';
978
1021
  /* Assert result is correctly formatted */
979
1022
  if (!Array.isArray(result)) {
980
1023
  throw new Error(labels.wrongFormattedData);
@@ -990,8 +1033,17 @@ class SelecticStore {
990
1033
  if (!search) {
991
1034
  /* update cache */
992
1035
  state.totalDynOptions = total;
993
- state.dynOptions.splice(offset, limit, ...result);
994
- setTimeout(() => this.buildAllOptions(true), 0);
1036
+ const old = state.dynOptions.splice(offset, limit, ...result);
1037
+ if (compareOptions(old, result)) {
1038
+ /* Added options are the same as previous ones.
1039
+ * Stop fetching to avoid infinite loop
1040
+ */
1041
+ errorMessage = labels.wrongQueryResult;
1042
+ setTimeout(() => this.buildAllOptions(true, true), 0);
1043
+ }
1044
+ else {
1045
+ setTimeout(() => this.buildAllOptions(true), 0);
1046
+ }
995
1047
  }
996
1048
  /* Check request is not obsolete */
997
1049
  if (requestId !== this.requestId) {
@@ -1014,13 +1066,13 @@ class SelecticStore {
1014
1066
  if (search && state.totalFilteredOptions <= state.filteredOptions.length) {
1015
1067
  this.addStaticFilteredOptions(true);
1016
1068
  }
1017
- state.status.errorMessage = '';
1069
+ state.status.errorMessage = errorMessage;
1018
1070
  }
1019
1071
  catch (e) {
1020
1072
  state.status.errorMessage = e.message;
1021
1073
  if (!search) {
1022
1074
  state.totalDynOptions = 0;
1023
- this.buildAllOptions(true);
1075
+ this.buildAllOptions(true, true);
1024
1076
  }
1025
1077
  }
1026
1078
  this.state.status.searching = false;
@@ -1220,6 +1272,8 @@ let MainInput = class MainInput extends vtyx.Vue {
1220
1272
  /* }}} */
1221
1273
  /* {{{ data */
1222
1274
  this.nbHiddenItems = 0;
1275
+ /* reactivity non needed */
1276
+ this.domObserver = null;
1223
1277
  }
1224
1278
  /* }}} */
1225
1279
  /* {{{ computed */
@@ -1265,8 +1319,18 @@ let MainInput = class MainInput extends vtyx.Vue {
1265
1319
  get singleSelectedItem() {
1266
1320
  const state = this.store.state;
1267
1321
  const isMultiple = state.multiple;
1268
- const selected = this.selectedOptions;
1269
- return !isMultiple && !!selected && selected.text;
1322
+ if (isMultiple) {
1323
+ return;
1324
+ }
1325
+ return this.selectedOptions;
1326
+ }
1327
+ get singleSelectedItemText() {
1328
+ const item = this.singleSelectedItem;
1329
+ return (item === null || item === void 0 ? void 0 : item.text) || '';
1330
+ }
1331
+ get singleSelectedItemTitle() {
1332
+ const item = this.singleSelectedItem;
1333
+ return (item === null || item === void 0 ? void 0 : item.title) || (item === null || item === void 0 ? void 0 : item.text) || '';
1270
1334
  }
1271
1335
  get singleStyle() {
1272
1336
  const selected = this.selectedOptions;
@@ -1364,6 +1428,11 @@ let MainInput = class MainInput extends vtyx.Vue {
1364
1428
  * currently shown */
1365
1429
  const el = this.$refs.selectedItems;
1366
1430
  const parentEl = el.parentElement;
1431
+ if (!document.contains(parentEl)) {
1432
+ /* The element is currently not in DOM */
1433
+ this.createObserver(parentEl);
1434
+ return;
1435
+ }
1367
1436
  const parentPadding = parseInt(getComputedStyle(parentEl).getPropertyValue('padding-right'), 10);
1368
1437
  const clearEl = parentEl.querySelector('.selectic-input__clear-icon');
1369
1438
  const clearWidth = clearEl ? clearEl.offsetWidth : 0;
@@ -1395,6 +1464,33 @@ let MainInput = class MainInput extends vtyx.Vue {
1395
1464
  idx--;
1396
1465
  this.nbHiddenItems = selectedOptions.length - idx;
1397
1466
  }
1467
+ closeObserver() {
1468
+ const observer = this.domObserver;
1469
+ if (observer) {
1470
+ observer.disconnect();
1471
+ }
1472
+ this.domObserver = null;
1473
+ }
1474
+ createObserver(el) {
1475
+ this.closeObserver();
1476
+ const observer = new MutationObserver((mutationsList) => {
1477
+ for (const mutation of mutationsList) {
1478
+ if (mutation.type === 'childList') {
1479
+ for (const elMutated of Array.from(mutation.addedNodes)) {
1480
+ /* Check that element has been added to DOM */
1481
+ if (elMutated.contains(el)) {
1482
+ this.closeObserver();
1483
+ this.computeSize();
1484
+ return;
1485
+ }
1486
+ }
1487
+ }
1488
+ }
1489
+ });
1490
+ const config = { childList: true, subtree: true };
1491
+ observer.observe(document, config);
1492
+ this.domObserver = observer;
1493
+ }
1398
1494
  /* }}} */
1399
1495
  /* {{{ watch */
1400
1496
  onInternalChange() {
@@ -1405,6 +1501,9 @@ let MainInput = class MainInput extends vtyx.Vue {
1405
1501
  updated() {
1406
1502
  this.computeSize();
1407
1503
  }
1504
+ beforeUnmount() {
1505
+ this.closeObserver();
1506
+ }
1408
1507
  /* }}} */
1409
1508
  render() {
1410
1509
  return (vtyx.h("div", { class: "selectic-container has-feedback", on: {
@@ -1415,14 +1514,14 @@ let MainInput = class MainInput extends vtyx.Vue {
1415
1514
  focused: this.store.state.isOpen,
1416
1515
  disabled: this.store.state.disabled,
1417
1516
  }] },
1418
- this.hasValue && !this.store.state.multiple && (vtyx.h("span", { class: "selectic-item_text", style: this.singleStyle, title: this.singleSelectedItem || '' }, this.singleSelectedItem)),
1517
+ this.hasValue && !this.store.state.multiple && (vtyx.h("span", { class: "selectic-item_text", style: this.singleStyle, title: this.singleSelectedItemTitle }, this.singleSelectedItemText)),
1419
1518
  this.displayPlaceholder && (vtyx.h("span", { class: [
1420
1519
  'selectic-input__selected-items__placeholder',
1421
1520
  'selectic-item_text',
1422
1521
  ], title: this.store.state.placeholder }, this.store.state.placeholder)),
1423
1522
  this.store.state.multiple && (vtyx.h("div", { class: "selectic-input__selected-items", ref: "selectedItems" },
1424
1523
  this.isSelectionReversed && (vtyx.h("span", { class: "fa fa-strikethrough selectic-input__reverse-icon", title: this.reverseSelectionLabel })),
1425
- this.showSelectedOptions.map((item) => (vtyx.h("div", { class: "single-value", style: item.style, title: item.text, on: {
1524
+ this.showSelectedOptions.map((item) => (vtyx.h("div", { class: "single-value", style: item.style, title: item.title || item.text, on: {
1426
1525
  click: () => this.$emit('item:click', item.id),
1427
1526
  } },
1428
1527
  vtyx.h("span", { class: "selectic-input__selected-items__value" }, item.text),
@@ -1851,6 +1950,10 @@ var __decorate$1 = (this && this.__decorate) || function (decorators, target, ke
1851
1950
  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;
1852
1951
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1853
1952
  };
1953
+ /* list estimation height
1954
+ * 30px × 10 + 20px (for panel header)
1955
+ */
1956
+ const DEFAULT_LIST_HEIGHT = 320;
1854
1957
  let ExtendedList = class ExtendedList extends vtyx.Vue {
1855
1958
  constructor() {
1856
1959
  /* {{{ props */
@@ -1858,11 +1961,18 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
1858
1961
  /* }}} */
1859
1962
  /* {{{ data */
1860
1963
  this.topGroup = ' ';
1861
- this.listHeight = 120;
1964
+ this.listHeight = 0;
1862
1965
  this.listWidth = 200;
1966
+ this.availableSpace = 0;
1863
1967
  }
1864
1968
  /* }}} */
1865
1969
  /* {{{ computed */
1970
+ /** check if the height of the box has been completely estimated. */
1971
+ get isFullyEstimated() {
1972
+ const listHeight = this.listHeight;
1973
+ const availableSpace = this.availableSpace;
1974
+ return listHeight !== 0 && listHeight < availableSpace;
1975
+ }
1866
1976
  get searchingLabel() {
1867
1977
  return this.store.data.labels.searching;
1868
1978
  }
@@ -1923,17 +2033,30 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
1923
2033
  }
1924
2034
  get bestPosition() {
1925
2035
  const windowHeight = window.innerHeight;
1926
- const listHeight = this.listHeight;
2036
+ const isFullyEstimated = this.isFullyEstimated;
2037
+ /* XXX: The max() is because if listHeight is greater than default,
2038
+ * it means that the value is more accurate than the default. */
2039
+ const listHeight = isFullyEstimated ? this.listHeight
2040
+ : Math.max(DEFAULT_LIST_HEIGHT, this.listHeight);
1927
2041
  const inputTop = this.elementTop;
1928
2042
  const inputBottom = this.elementBottom;
1929
- if (inputBottom + listHeight <= windowHeight) {
2043
+ const availableTop = inputTop;
2044
+ const availableBottom = windowHeight - inputBottom;
2045
+ if (listHeight < availableBottom) {
1930
2046
  return 'bottom';
1931
2047
  }
1932
- if (listHeight < inputTop) {
2048
+ if (listHeight < availableTop) {
1933
2049
  return 'top';
1934
2050
  }
1935
2051
  /* There are not enough space neither at bottom nor at top */
1936
- return (windowHeight - inputBottom) < inputTop ? 'top' : 'bottom';
2052
+ return availableBottom < availableTop ? 'top' : 'bottom';
2053
+ }
2054
+ get position() {
2055
+ const listPosition = this.store.state.listPosition;
2056
+ if (listPosition === 'auto') {
2057
+ return this.bestPosition;
2058
+ }
2059
+ return listPosition;
1937
2060
  }
1938
2061
  get horizontalStyle() {
1939
2062
  const windowWidth = window.innerWidth;
@@ -1953,26 +2076,32 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
1953
2076
  return `left: ${inputLeft}px; min-width: unset;`;
1954
2077
  }
1955
2078
  get positionStyle() {
1956
- let listPosition = this.store.state.listPosition;
2079
+ const listPosition = this.position;
1957
2080
  const horizontalStyle = this.horizontalStyle;
1958
- if (listPosition === 'auto') {
1959
- listPosition = this.bestPosition;
1960
- }
2081
+ const width = this.width;
1961
2082
  if (listPosition === 'top') {
1962
2083
  const transform = horizontalStyle.includes('transform')
1963
2084
  ? 'transform: translateX(-100%) translateY(-100%);'
1964
2085
  : 'transform: translateY(-100%);';
2086
+ const elementTop = this.elementTop;
2087
+ const availableSpace = this.elementTop;
2088
+ this.availableSpace = availableSpace;
1965
2089
  return `
1966
- top: ${this.elementTop}px;
2090
+ --top-position: ${elementTop}px;
1967
2091
  ${horizontalStyle}
1968
- width: ${this.width}px;
1969
- ${transform}
2092
+ --list-width: ${width}px;
2093
+ ${transform};
2094
+ --availableSpace: ${availableSpace}px;
1970
2095
  `;
1971
2096
  }
2097
+ const elementBottom = this.elementBottom;
2098
+ const availableSpace = window.innerHeight - elementBottom;
2099
+ this.availableSpace = availableSpace;
1972
2100
  return `
1973
- top: ${this.elementBottom}px;
2101
+ --top-position: ${elementBottom}px;
1974
2102
  ${horizontalStyle}
1975
- width: ${this.width}px;
2103
+ --list-width: ${width}px;
2104
+ --availableSpace: ${availableSpace}px;
1976
2105
  `;
1977
2106
  }
1978
2107
  /* }}} */
@@ -2015,7 +2144,10 @@ let ExtendedList = class ExtendedList extends vtyx.Vue {
2015
2144
  const state = store.state;
2016
2145
  const isGroup = state.groups.size > 0 &&
2017
2146
  state.totalFilteredOptions > store.data.itemsPerPage;
2018
- return (vtyx.h("div", { style: this.positionStyle, class: "selectic selectic__extended-list" },
2147
+ return (vtyx.h("div", { style: this.positionStyle, class: [
2148
+ 'selectic selectic__extended-list',
2149
+ `selectic-position-${this.position}`,
2150
+ ] },
2019
2151
  !state.hideFilter && (vtyx.h(Filter, { store: this.store })),
2020
2152
  isGroup && (vtyx.h("span", { class: "selectic-item selectic-item--header selectic-item__is-group" }, this.topGroup)),
2021
2153
  vtyx.h(List$1, { store: store, on: {