goodteditor-ui 1.0.21 → 1.0.23

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.
Files changed (114) hide show
  1. package/.eslintrc.js +7 -7
  2. package/.prettierrc +14 -14
  3. package/README.md +35 -35
  4. package/babel.config.js +5 -5
  5. package/index.js +53 -53
  6. package/jsconfig.json +13 -13
  7. package/package.json +66 -70
  8. package/src/App.vue +36 -36
  9. package/src/components/ui/Avatar.md +68 -68
  10. package/src/components/ui/Avatar.vue +180 -177
  11. package/src/components/ui/Badge.md +20 -20
  12. package/src/components/ui/Badge.vue +75 -75
  13. package/src/components/ui/Collapse.md +90 -90
  14. package/src/components/ui/Collapse.vue +86 -86
  15. package/src/components/ui/ColorPicker/Alpha.vue +114 -114
  16. package/src/components/ui/ColorPicker/Colors.vue +117 -117
  17. package/src/components/ui/ColorPicker/Hue.vue +113 -113
  18. package/src/components/ui/ColorPicker/Preview.vue +55 -55
  19. package/src/components/ui/ColorPicker/Saturation.vue +123 -123
  20. package/src/components/ui/ColorPicker/mixin.js +105 -105
  21. package/src/components/ui/ColorPicker.md +17 -17
  22. package/src/components/ui/ColorPicker.vue +314 -314
  23. package/src/components/ui/Datalist.md +41 -41
  24. package/src/components/ui/Datalist.vue +157 -157
  25. package/src/components/ui/DatePicker.md +168 -168
  26. package/src/components/ui/DatePicker.vue +527 -527
  27. package/src/components/ui/FileSelector.md +105 -105
  28. package/src/components/ui/FileSelector.vue +82 -82
  29. package/src/components/ui/Grid.md +130 -130
  30. package/src/components/ui/Grid.vue +92 -92
  31. package/src/components/ui/Image.md +59 -59
  32. package/src/components/ui/Image.vue +57 -57
  33. package/src/components/ui/InputAutocomplete.md +115 -115
  34. package/src/components/ui/InputAutocomplete.vue +341 -341
  35. package/src/components/ui/InputColorPicker.md +51 -51
  36. package/src/components/ui/InputColorPicker.vue +151 -151
  37. package/src/components/ui/InputDatePicker.md +121 -121
  38. package/src/components/ui/InputDatePicker.vue +326 -326
  39. package/src/components/ui/InputTags.md +51 -51
  40. package/src/components/ui/InputTags.vue +184 -184
  41. package/src/components/ui/InputTimePicker.md +25 -25
  42. package/src/components/ui/InputTimePicker.vue +253 -253
  43. package/src/components/ui/InputUnits.md +20 -20
  44. package/src/components/ui/InputUnits.vue +257 -257
  45. package/src/components/ui/Lazy.md +37 -37
  46. package/src/components/ui/Lazy.vue +92 -92
  47. package/src/components/ui/Pagination.md +74 -74
  48. package/src/components/ui/Pagination.vue +138 -138
  49. package/src/components/ui/Paginator.md +34 -34
  50. package/src/components/ui/Paginator.vue +83 -83
  51. package/src/components/ui/Popover.md +34 -34
  52. package/src/components/ui/Popover.vue +274 -274
  53. package/src/components/ui/Popup.md +59 -59
  54. package/src/components/ui/Popup.vue +150 -150
  55. package/src/components/ui/ResponsiveContainer.md +58 -58
  56. package/src/components/ui/ResponsiveContainer.vue +99 -99
  57. package/src/components/ui/Select.md +187 -187
  58. package/src/components/ui/Select.vue +421 -421
  59. package/src/components/ui/TimePicker.md +50 -50
  60. package/src/components/ui/TimePicker.vue +252 -252
  61. package/src/components/ui/Tooltip.md +54 -114
  62. package/src/components/ui/Tooltip.vue +113 -113
  63. package/src/components/ui/WysiwygEditor/WysiwygEditor.d.ts +119 -119
  64. package/src/components/ui/WysiwygEditor/constants.js +264 -264
  65. package/src/components/ui/WysiwygEditor/extensions/blockquote.js +15 -15
  66. package/src/components/ui/WysiwygEditor/extensions/bold.js +15 -15
  67. package/src/components/ui/WysiwygEditor/extensions/bullet-list.js +15 -15
  68. package/src/components/ui/WysiwygEditor/extensions/code-block.js +13 -13
  69. package/src/components/ui/WysiwygEditor/extensions/code.js +13 -13
  70. package/src/components/ui/WysiwygEditor/extensions/font-size.js +34 -34
  71. package/src/components/ui/WysiwygEditor/extensions/formatting.js +14 -14
  72. package/src/components/ui/WysiwygEditor/extensions/heading.js +13 -13
  73. package/src/components/ui/WysiwygEditor/extensions/horizontal-rule.js +15 -15
  74. package/src/components/ui/WysiwygEditor/extensions/image.js +19 -19
  75. package/src/components/ui/WysiwygEditor/extensions/index.d.ts +32 -32
  76. package/src/components/ui/WysiwygEditor/extensions/index.js +32 -32
  77. package/src/components/ui/WysiwygEditor/extensions/italic.js +15 -15
  78. package/src/components/ui/WysiwygEditor/extensions/link.js +16 -16
  79. package/src/components/ui/WysiwygEditor/extensions/list-item.js +15 -15
  80. package/src/components/ui/WysiwygEditor/extensions/ordered-list.js +15 -15
  81. package/src/components/ui/WysiwygEditor/extensions/paragraph.js +23 -23
  82. package/src/components/ui/WysiwygEditor/extensions/strike.js +15 -15
  83. package/src/components/ui/WysiwygEditor/extensions/table-cell.js +13 -13
  84. package/src/components/ui/WysiwygEditor/extensions/table-header.js +15 -15
  85. package/src/components/ui/WysiwygEditor/extensions/table-row.js +15 -15
  86. package/src/components/ui/WysiwygEditor/extensions/table.js +29 -29
  87. package/src/components/ui/WysiwygEditor/extensions/text-align.js +6 -6
  88. package/src/components/ui/WysiwygEditor/extensions/text-style.js +15 -15
  89. package/src/components/ui/WysiwygEditor/extensions/underline.js +15 -15
  90. package/src/components/ui/WysiwygEditor/index.d.ts +4 -4
  91. package/src/components/ui/WysiwygEditor/index.js +4 -4
  92. package/src/components/ui/WysiwygEditor/renders/Button.vue +28 -28
  93. package/src/components/ui/WysiwygEditor/renders/ColorPicker.vue +41 -41
  94. package/src/components/ui/WysiwygEditor/renders/InputAuto.vue +34 -34
  95. package/src/components/ui/WysiwygEditor/renders/InputBrowse.vue +35 -35
  96. package/src/components/ui/WysiwygEditor/renders/InputUnits.vue +38 -38
  97. package/src/components/ui/WysiwygEditor/renders/Link.vue +87 -87
  98. package/src/components/ui/WysiwygEditor/renders/Select.vue +47 -47
  99. package/src/components/ui/WysiwygEditor/renders/ToolbarPopover.vue +49 -49
  100. package/src/components/ui/WysiwygEditor/renders/index.d.ts +8 -8
  101. package/src/components/ui/WysiwygEditor/renders/index.js +8 -8
  102. package/src/components/ui/WysiwygEditor/renders/mixins/RenderMixin.js +39 -39
  103. package/src/components/ui/WysiwygEditor/renders/mixins/index.js +1 -1
  104. package/src/components/ui/WysiwygEditor/tools-and-commands.js +704 -704
  105. package/src/components/ui/WysiwygEditor/utils.js +72 -72
  106. package/src/components/ui/WysiwygEditor.md +18 -18
  107. package/src/components/ui/WysiwygEditor.vue +271 -271
  108. package/src/components/ui/utils/FormComponent.js +107 -107
  109. package/src/components/ui/utils/Helpers.js +84 -84
  110. package/src/components/ui/utils/WithPopover.js +81 -99
  111. package/src/main.js +8 -8
  112. package/styleguide.config.js +37 -37
  113. package/vue.config.js +8 -8
  114. package/dist/js.png +0 -0
@@ -1,421 +1,421 @@
1
- <template>
2
- <div
3
- class="ui-select form-elem"
4
- :class="cssClassExt"
5
- @keydown.prevent="onKeyDown"
6
- @click="onClick"
7
- tabindex="0"
8
- :data-popover="popoverTargetId"
9
- >
10
- <div class="ui-select-label u-select-none">
11
- <template v-if="multiple">
12
- <template v-if="optionsSelected.length">
13
- <!--
14
- @slot Label slot for multiple mode
15
- @binding {Object} option option
16
- @binding {any} value option's value
17
- @binding {String} label option's label
18
- @binding {Function} deselectOption deselects option function(option:Object)
19
- -->
20
- <slot
21
- name="label-multiple"
22
- v-for="(option, index) in optionsSelected"
23
- v-bind="{
24
- option,
25
- value: getOptionValue(option),
26
- label: getOptionLabel(option),
27
- deselectOption,
28
- }"
29
- >
30
- <ui-badge
31
- class="mar-none mar-right-2"
32
- theme="primary"
33
- size="small"
34
- :key="index"
35
- removable
36
- @click.native.stop
37
- @remove="deselectOption(option)"
38
- >
39
- <span>{{ getOptionLabel(option) }}</span>
40
- </ui-badge>
41
- </slot>
42
- </template>
43
- <div class="ui-select-placeholder events-none" v-else>
44
- <!--
45
- @slot Placeholder slot
46
- -->
47
- <slot name="placeholder">
48
- <input class="w-100" :placeholder="placeholder" />
49
- </slot>
50
- </div>
51
- </template>
52
-
53
- <template v-else>
54
- <!--
55
- @slot Label slot for single mode
56
- @binding {Object} option option
57
- @binding {any} value option's value
58
- @binding {String} label option's label
59
- -->
60
- <slot
61
- name="label"
62
- v-bind="{
63
- option: optionsSelected[0],
64
- value: getOptionValue(optionsSelected[0]),
65
- label: getOptionLabel(optionsSelected[0]),
66
- }"
67
- v-if="optionsSelected.length"
68
- >
69
- {{ getOptionLabel(optionsSelected[0]) }}
70
- </slot>
71
- <div class="ui-select-placeholder events-none" v-else>
72
- <!--
73
- @slot Placeholder slot
74
- -->
75
- <slot name="placeholder">
76
- <input class="w-100" :placeholder="placeholder" />
77
- </slot>
78
- </div>
79
- </template>
80
- </div>
81
- <!--
82
- @slot Open state icon slot
83
- -->
84
- <slot name="icon-open" v-if="popoverShow">
85
- <div class="icon w-auto h-auto mar-left-2 events-none">
86
- <i class="mdi mdi-chevron-up"></i>
87
- </div>
88
- </slot>
89
- <!--
90
- @slot Close state icon slot
91
- -->
92
- <slot name="icon-close" v-else>
93
- <div class="icon w-auto h-auto mar-left-2 events-none">
94
- <i class="mdi mdi-chevron-down"></i>
95
- </div>
96
- </slot>
97
-
98
- <ui-popover :show.sync="popoverShow" v-bind="popoverOptions">
99
- <ui-datalist
100
- class="w-100 pull-left"
101
- @click.native.stop
102
- @select-option="onDatalistSelectOption"
103
- v-bind="{ size, options }"
104
- :cursorIndex.sync="dataListCursorIndex"
105
- ref="datalist"
106
- >
107
- <template #header>
108
- <!--
109
- @slot Dropdown header slot
110
- -->
111
- <slot name="dropdown-header"></slot>
112
- </template>
113
- <template #option="{ option, index, cursorIndex }">
114
- <!--
115
- @slot Label slot for single mode
116
- @binding {Object} option option
117
- @binding {any} value option's value
118
- @binding {String} label option's label
119
- @binding {Number} index option's index
120
- @binding {Boolean} isSelected option selection status
121
- @binding {Number} cursorIndex current cursor index
122
- @binding {Function} selectOption function that selects option
123
- @binding {Function} deselectOption function that deselects option
124
- @binding {Function} toggleOption function that toggles option selection
125
- @binding {Number} optionIndex option's index
126
- @binding {Boolean} isOptionSelected option selection status
127
- -->
128
- <slot
129
- name="option"
130
- v-bind="{
131
- option,
132
- value: getOptionValue(option),
133
- label: getOptionLabel(option),
134
- index,
135
- isSelected: isOptionSelected(option),
136
- cursorIndex,
137
- selectOption,
138
- deselectOption,
139
- toggleOption,
140
- // legacy
141
- optionIndex: index,
142
- isOptionSelected: isOptionSelected(option),
143
- }"
144
- >
145
- <li
146
- :class="{
147
- active: isOptionSelected(option),
148
- 'bg-grey-lighter': index == cursorIndex,
149
- }"
150
- :key="index"
151
- :title="getOptionLabel(option)"
152
- @click="toggleOption(option)"
153
- >
154
- <div class="row row-collapse" v-if="multiple">
155
- <div class="col col-vmid text-truncate">
156
- <div
157
- class="text-truncate"
158
- style="min-height: calc(var(--line-height)*1em)"
159
- >
160
- {{ getOptionLabel(option) }}
161
- </div>
162
- </div>
163
- <div class="col col-auto col-vmid" v-if="isOptionSelected(option)">
164
- <i class="mdi mdi-check" style="line-height:1"></i>
165
- </div>
166
- </div>
167
- <div
168
- class="text-truncate"
169
- style="min-height: calc(var(--line-height)*1rem)"
170
- v-else
171
- >
172
- {{ getOptionLabel(option) }}
173
- </div>
174
- </li>
175
- </slot>
176
- </template>
177
- <template #footer>
178
- <!--
179
- @slot Dropdown footer slot
180
- -->
181
- <slot name="dropdown-footer"></slot>
182
- </template>
183
- </ui-datalist>
184
- </ui-popover>
185
- </div>
186
- </template>
187
- <style lang="less" scoped>
188
- .ui-select {
189
- display: inline-flex;
190
- align-items: center;
191
- &-label {
192
- white-space: nowrap;
193
- text-overflow: ellipsis;
194
- overflow: hidden;
195
- flex: 1 0 0;
196
- }
197
- &-placeholder {
198
- input {
199
- border: none;
200
- margin: 0;
201
- padding: 0;
202
- color: inherit;
203
- background: transparent;
204
- }
205
- }
206
- }
207
- </style>
208
- <script>
209
- import UiBadge from './Badge.vue';
210
- import UiDatalist from './Datalist.vue';
211
- import UiPopover from './Popover.vue';
212
- import FormComponent from './utils/FormComponent';
213
- import WithPopover from './utils/WithPopover';
214
- import { Key } from './utils/Helpers';
215
-
216
- export default {
217
- components: {
218
- UiBadge,
219
- UiDatalist,
220
- UiPopover,
221
- },
222
- mixins: [FormComponent, WithPopover],
223
- props: {
224
- /**
225
- * @model
226
- */
227
- value: {
228
- default() {
229
- return null;
230
- },
231
- },
232
- /**
233
- * Options. Array of Objects (option objects)
234
- */
235
- options: {
236
- type: Array,
237
- default() {
238
- return [];
239
- },
240
- },
241
- /**
242
- * Allow multiple selection
243
- */
244
- multiple: {
245
- type: Boolean,
246
- default: false,
247
- },
248
- /**
249
- * Defines whether 'value' is the option value field or option object
250
- * @see options
251
- */
252
- valueObjects: {
253
- type: Boolean,
254
- default: false,
255
- },
256
- /**
257
- * Defines the 'value' field of the option Object
258
- */
259
- valueField: {
260
- type: String,
261
- default: 'value',
262
- },
263
- /**
264
- * Defines the 'label' field of the option Object
265
- */
266
- labelField: {
267
- type: String,
268
- default: 'label',
269
- },
270
- autoWidth: {
271
- default: true,
272
- },
273
- },
274
- data() {
275
- return {
276
- optionsSelected: [],
277
- dataListCursorIndex: -1,
278
- };
279
- },
280
- computed: {
281
- /**
282
- * @return {object}
283
- */
284
- cssClassExt() {
285
- let obj = { 'u-select-none': this.readonly };
286
- return { ...this.cssClass, ...obj };
287
- },
288
- },
289
- watch: {
290
- value: {
291
- handler(model) {
292
- this.importModel(model);
293
- },
294
- immediate: true,
295
- },
296
- options() {
297
- this.importModel(this.value);
298
- },
299
- },
300
- methods: {
301
- importModel(model) {
302
- let ci = -1;
303
- let tmp = [];
304
- model = this.multiple ? (Array.isArray(model) ? model : [model]) : [model];
305
- model.forEach(modelItem => {
306
- let modelItemValue = this.valueObjects ? this.getOptionValue(modelItem) : modelItem;
307
- let optionIndex = this.options.findIndex(optionItem => {
308
- let optionItemValue = this.getOptionValue(optionItem);
309
- return optionItemValue === modelItemValue;
310
- });
311
- if (optionIndex >= 0) {
312
- ci = ci < 0 ? optionIndex : ci;
313
- tmp.push(this.options[optionIndex]);
314
- }
315
- });
316
- this.dataListCursorIndex = ci;
317
- this.optionsSelected = tmp;
318
- },
319
- exportModel() {
320
- let model = this.optionsSelected.map(option =>
321
- this.valueObjects ? option : this.getOptionValue(option)
322
- );
323
- if (this.multiple) {
324
- return model;
325
- }
326
- return model && model.length ? model[0] : null;
327
- },
328
- triggerModelChange() {
329
- let value = this.exportModel();
330
- /**
331
- * Input event
332
- * @property {any} value
333
- */
334
- this.$emit('input', value);
335
- /**
336
- * Change event
337
- * @property {any} model
338
- * @property {Array} meta
339
- */
340
- this.$emit('change', value);
341
- },
342
- getOptionLabel(option) {
343
- let label = option ? option[this.labelField] : null;
344
- return label == null ? option : label;
345
- },
346
- getOptionValue(option) {
347
- let value = option ? option[this.valueField] : null;
348
- // @NOTE option.value might be 'null'
349
- return value === undefined ? option : value;
350
- },
351
- getOptionIndex(option) {
352
- return this.options.findIndex(
353
- o => this.getOptionValue(o) === this.getOptionValue(option)
354
- );
355
- },
356
- isOptionSelected(option) {
357
- return !!this.optionsSelected.find(
358
- o => this.getOptionValue(o) === this.getOptionValue(option)
359
- );
360
- },
361
- selectOption(option) {
362
- if (this.isOptionSelected(option)) {
363
- return;
364
- }
365
- if (this.multiple) {
366
- this.optionsSelected.push(option);
367
- } else {
368
- this.optionsSelected = [option];
369
- this.popoverShow = false;
370
- }
371
- this.triggerModelChange();
372
- },
373
- deselectOption(option) {
374
- if (!this.multiple) {
375
- this.popoverShow = false;
376
- return;
377
- }
378
- this.optionsSelected = this.optionsSelected.filter(
379
- o => this.getOptionValue(o) !== this.getOptionValue(option)
380
- );
381
- this.triggerModelChange();
382
- },
383
- toggleOption(option) {
384
- if (!this.isOptionSelected(option)) {
385
- this.selectOption(option);
386
- } else {
387
- this.deselectOption(option);
388
- }
389
- },
390
- getDatalistRef() {
391
- return this.$refs.datalist;
392
- },
393
- onClick(e) {
394
- this.togglePopover();
395
- this.rootHasFocus = true;
396
- this.$el.focus();
397
- if (this.popoverShow && this.optionsSelected.length) {
398
- this.dataListCursorIndex = this.getOptionIndex(this.optionsSelected[0]);
399
- }
400
- },
401
- onDatalistSelectOption({ option }) {
402
- this.toggleOption(option);
403
- },
404
- onKeyDown(e) {
405
- let list = this.getDatalistRef();
406
- if (e.key === Key.ESC) {
407
- this.popoverShow = false;
408
- return;
409
- }
410
- if (e.key === Key.ENTER) {
411
- if (!this.popoverShow) {
412
- this.popoverShow = true;
413
- }
414
- }
415
- if (this.popoverShow) {
416
- list && list.onKeyDown(e);
417
- }
418
- },
419
- },
420
- };
421
- </script>
1
+ <template>
2
+ <div
3
+ class="ui-select form-elem"
4
+ :class="cssClassExt"
5
+ @keydown.prevent="onKeyDown"
6
+ @click="onClick"
7
+ tabindex="0"
8
+ :data-popover="popoverTargetId"
9
+ >
10
+ <div class="ui-select-label u-select-none">
11
+ <template v-if="multiple">
12
+ <template v-if="optionsSelected.length">
13
+ <!--
14
+ @slot Label slot for multiple mode
15
+ @binding {Object} option option
16
+ @binding {any} value option's value
17
+ @binding {String} label option's label
18
+ @binding {Function} deselectOption deselects option function(option:Object)
19
+ -->
20
+ <slot
21
+ name="label-multiple"
22
+ v-for="(option, index) in optionsSelected"
23
+ v-bind="{
24
+ option,
25
+ value: getOptionValue(option),
26
+ label: getOptionLabel(option),
27
+ deselectOption,
28
+ }"
29
+ >
30
+ <ui-badge
31
+ class="mar-none mar-right-2"
32
+ theme="primary"
33
+ size="small"
34
+ :key="index"
35
+ removable
36
+ @click.native.stop
37
+ @remove="deselectOption(option)"
38
+ >
39
+ <span>{{ getOptionLabel(option) }}</span>
40
+ </ui-badge>
41
+ </slot>
42
+ </template>
43
+ <div class="ui-select-placeholder events-none" v-else>
44
+ <!--
45
+ @slot Placeholder slot
46
+ -->
47
+ <slot name="placeholder">
48
+ <input class="w-100" :placeholder="placeholder" />
49
+ </slot>
50
+ </div>
51
+ </template>
52
+
53
+ <template v-else>
54
+ <!--
55
+ @slot Label slot for single mode
56
+ @binding {Object} option option
57
+ @binding {any} value option's value
58
+ @binding {String} label option's label
59
+ -->
60
+ <slot
61
+ name="label"
62
+ v-bind="{
63
+ option: optionsSelected[0],
64
+ value: getOptionValue(optionsSelected[0]),
65
+ label: getOptionLabel(optionsSelected[0]),
66
+ }"
67
+ v-if="optionsSelected.length"
68
+ >
69
+ {{ getOptionLabel(optionsSelected[0]) }}
70
+ </slot>
71
+ <div class="ui-select-placeholder events-none" v-else>
72
+ <!--
73
+ @slot Placeholder slot
74
+ -->
75
+ <slot name="placeholder">
76
+ <input class="w-100" :placeholder="placeholder" />
77
+ </slot>
78
+ </div>
79
+ </template>
80
+ </div>
81
+ <!--
82
+ @slot Open state icon slot
83
+ -->
84
+ <slot name="icon-open" v-if="popoverShow">
85
+ <div class="icon w-auto h-auto mar-left-2 events-none">
86
+ <i class="mdi mdi-chevron-up"></i>
87
+ </div>
88
+ </slot>
89
+ <!--
90
+ @slot Close state icon slot
91
+ -->
92
+ <slot name="icon-close" v-else>
93
+ <div class="icon w-auto h-auto mar-left-2 events-none">
94
+ <i class="mdi mdi-chevron-down"></i>
95
+ </div>
96
+ </slot>
97
+
98
+ <ui-popover :show.sync="popoverShow" v-bind="popoverOptions">
99
+ <ui-datalist
100
+ class="w-100 pull-left"
101
+ @click.native.stop
102
+ @select-option="onDatalistSelectOption"
103
+ v-bind="{ size, options }"
104
+ :cursorIndex.sync="dataListCursorIndex"
105
+ ref="datalist"
106
+ >
107
+ <template #header>
108
+ <!--
109
+ @slot Dropdown header slot
110
+ -->
111
+ <slot name="dropdown-header"></slot>
112
+ </template>
113
+ <template #option="{ option, index, cursorIndex }">
114
+ <!--
115
+ @slot Label slot for single mode
116
+ @binding {Object} option option
117
+ @binding {any} value option's value
118
+ @binding {String} label option's label
119
+ @binding {Number} index option's index
120
+ @binding {Boolean} isSelected option selection status
121
+ @binding {Number} cursorIndex current cursor index
122
+ @binding {Function} selectOption function that selects option
123
+ @binding {Function} deselectOption function that deselects option
124
+ @binding {Function} toggleOption function that toggles option selection
125
+ @binding {Number} optionIndex option's index
126
+ @binding {Boolean} isOptionSelected option selection status
127
+ -->
128
+ <slot
129
+ name="option"
130
+ v-bind="{
131
+ option,
132
+ value: getOptionValue(option),
133
+ label: getOptionLabel(option),
134
+ index,
135
+ isSelected: isOptionSelected(option),
136
+ cursorIndex,
137
+ selectOption,
138
+ deselectOption,
139
+ toggleOption,
140
+ // legacy
141
+ optionIndex: index,
142
+ isOptionSelected: isOptionSelected(option),
143
+ }"
144
+ >
145
+ <li
146
+ :class="{
147
+ active: isOptionSelected(option),
148
+ 'bg-grey-lighter': index == cursorIndex,
149
+ }"
150
+ :key="index"
151
+ :title="getOptionLabel(option)"
152
+ @click="toggleOption(option)"
153
+ >
154
+ <div class="row row-collapse" v-if="multiple">
155
+ <div class="col col-vmid text-truncate">
156
+ <div
157
+ class="text-truncate"
158
+ style="min-height: calc(var(--line-height)*1em)"
159
+ >
160
+ {{ getOptionLabel(option) }}
161
+ </div>
162
+ </div>
163
+ <div class="col col-auto col-vmid" v-if="isOptionSelected(option)">
164
+ <i class="mdi mdi-check" style="line-height:1"></i>
165
+ </div>
166
+ </div>
167
+ <div
168
+ class="text-truncate"
169
+ style="min-height: calc(var(--line-height)*1rem)"
170
+ v-else
171
+ >
172
+ {{ getOptionLabel(option) }}
173
+ </div>
174
+ </li>
175
+ </slot>
176
+ </template>
177
+ <template #footer>
178
+ <!--
179
+ @slot Dropdown footer slot
180
+ -->
181
+ <slot name="dropdown-footer"></slot>
182
+ </template>
183
+ </ui-datalist>
184
+ </ui-popover>
185
+ </div>
186
+ </template>
187
+ <style lang="less" scoped>
188
+ .ui-select {
189
+ display: inline-flex;
190
+ align-items: center;
191
+ &-label {
192
+ white-space: nowrap;
193
+ text-overflow: ellipsis;
194
+ overflow: hidden;
195
+ flex: 1 0 0;
196
+ }
197
+ &-placeholder {
198
+ input {
199
+ border: none;
200
+ margin: 0;
201
+ padding: 0;
202
+ color: inherit;
203
+ background: transparent;
204
+ }
205
+ }
206
+ }
207
+ </style>
208
+ <script>
209
+ import UiBadge from './Badge.vue';
210
+ import UiDatalist from './Datalist.vue';
211
+ import UiPopover from './Popover.vue';
212
+ import FormComponent from './utils/FormComponent';
213
+ import WithPopover from './utils/WithPopover';
214
+ import { Key } from './utils/Helpers';
215
+
216
+ export default {
217
+ components: {
218
+ UiBadge,
219
+ UiDatalist,
220
+ UiPopover,
221
+ },
222
+ mixins: [FormComponent, WithPopover],
223
+ props: {
224
+ /**
225
+ * @model
226
+ */
227
+ value: {
228
+ default() {
229
+ return null;
230
+ },
231
+ },
232
+ /**
233
+ * Options. Array of Objects (option objects)
234
+ */
235
+ options: {
236
+ type: Array,
237
+ default() {
238
+ return [];
239
+ },
240
+ },
241
+ /**
242
+ * Allow multiple selection
243
+ */
244
+ multiple: {
245
+ type: Boolean,
246
+ default: false,
247
+ },
248
+ /**
249
+ * Defines whether 'value' is the option value field or option object
250
+ * @see options
251
+ */
252
+ valueObjects: {
253
+ type: Boolean,
254
+ default: false,
255
+ },
256
+ /**
257
+ * Defines the 'value' field of the option Object
258
+ */
259
+ valueField: {
260
+ type: String,
261
+ default: 'value',
262
+ },
263
+ /**
264
+ * Defines the 'label' field of the option Object
265
+ */
266
+ labelField: {
267
+ type: String,
268
+ default: 'label',
269
+ },
270
+ autoWidth: {
271
+ default: true,
272
+ },
273
+ },
274
+ data() {
275
+ return {
276
+ optionsSelected: [],
277
+ dataListCursorIndex: -1,
278
+ };
279
+ },
280
+ computed: {
281
+ /**
282
+ * @return {object}
283
+ */
284
+ cssClassExt() {
285
+ let obj = { 'u-select-none': this.readonly };
286
+ return { ...this.cssClass, ...obj };
287
+ },
288
+ },
289
+ watch: {
290
+ value: {
291
+ handler(model) {
292
+ this.importModel(model);
293
+ },
294
+ immediate: true,
295
+ },
296
+ options() {
297
+ this.importModel(this.value);
298
+ },
299
+ },
300
+ methods: {
301
+ importModel(model) {
302
+ let ci = -1;
303
+ let tmp = [];
304
+ model = this.multiple ? (Array.isArray(model) ? model : [model]) : [model];
305
+ model.forEach(modelItem => {
306
+ let modelItemValue = this.valueObjects ? this.getOptionValue(modelItem) : modelItem;
307
+ let optionIndex = this.options.findIndex(optionItem => {
308
+ let optionItemValue = this.getOptionValue(optionItem);
309
+ return optionItemValue === modelItemValue;
310
+ });
311
+ if (optionIndex >= 0) {
312
+ ci = ci < 0 ? optionIndex : ci;
313
+ tmp.push(this.options[optionIndex]);
314
+ }
315
+ });
316
+ this.dataListCursorIndex = ci;
317
+ this.optionsSelected = tmp;
318
+ },
319
+ exportModel() {
320
+ let model = this.optionsSelected.map(option =>
321
+ this.valueObjects ? option : this.getOptionValue(option)
322
+ );
323
+ if (this.multiple) {
324
+ return model;
325
+ }
326
+ return model && model.length ? model[0] : null;
327
+ },
328
+ triggerModelChange() {
329
+ let value = this.exportModel();
330
+ /**
331
+ * Input event
332
+ * @property {any} value
333
+ */
334
+ this.$emit('input', value);
335
+ /**
336
+ * Change event
337
+ * @property {any} model
338
+ * @property {Array} meta
339
+ */
340
+ this.$emit('change', value);
341
+ },
342
+ getOptionLabel(option) {
343
+ let label = option ? option[this.labelField] : null;
344
+ return label == null ? option : label;
345
+ },
346
+ getOptionValue(option) {
347
+ let value = option ? option[this.valueField] : null;
348
+ // @NOTE option.value might be 'null'
349
+ return value === undefined ? option : value;
350
+ },
351
+ getOptionIndex(option) {
352
+ return this.options.findIndex(
353
+ o => this.getOptionValue(o) === this.getOptionValue(option)
354
+ );
355
+ },
356
+ isOptionSelected(option) {
357
+ return !!this.optionsSelected.find(
358
+ o => this.getOptionValue(o) === this.getOptionValue(option)
359
+ );
360
+ },
361
+ selectOption(option) {
362
+ if (this.isOptionSelected(option)) {
363
+ return;
364
+ }
365
+ if (this.multiple) {
366
+ this.optionsSelected.push(option);
367
+ } else {
368
+ this.optionsSelected = [option];
369
+ this.popoverShow = false;
370
+ }
371
+ this.triggerModelChange();
372
+ },
373
+ deselectOption(option) {
374
+ if (!this.multiple) {
375
+ this.popoverShow = false;
376
+ return;
377
+ }
378
+ this.optionsSelected = this.optionsSelected.filter(
379
+ o => this.getOptionValue(o) !== this.getOptionValue(option)
380
+ );
381
+ this.triggerModelChange();
382
+ },
383
+ toggleOption(option) {
384
+ if (!this.isOptionSelected(option)) {
385
+ this.selectOption(option);
386
+ } else {
387
+ this.deselectOption(option);
388
+ }
389
+ },
390
+ getDatalistRef() {
391
+ return this.$refs.datalist;
392
+ },
393
+ onClick(e) {
394
+ this.togglePopover();
395
+ this.rootHasFocus = true;
396
+ this.$el.focus();
397
+ if (this.popoverShow && this.optionsSelected.length) {
398
+ this.dataListCursorIndex = this.getOptionIndex(this.optionsSelected[0]);
399
+ }
400
+ },
401
+ onDatalistSelectOption({ option }) {
402
+ this.toggleOption(option);
403
+ },
404
+ onKeyDown(e) {
405
+ let list = this.getDatalistRef();
406
+ if (e.key === Key.ESC) {
407
+ this.popoverShow = false;
408
+ return;
409
+ }
410
+ if (e.key === Key.ENTER) {
411
+ if (!this.popoverShow) {
412
+ this.popoverShow = true;
413
+ }
414
+ }
415
+ if (this.popoverShow) {
416
+ list && list.onKeyDown(e);
417
+ }
418
+ },
419
+ },
420
+ };
421
+ </script>