vueless 0.0.185 → 0.0.186

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "0.0.185",
3
+ "version": "0.0.186",
4
4
  "license": "MIT",
5
5
  "description": "Vue Styleless Component Framework.",
6
6
  "homepage": "https://vueless.com",
@@ -1,17 +1,32 @@
1
- import { ref, computed } from "vue";
1
+ import { ref, computed, toValue } from "vue";
2
2
 
3
- export default function usePointer(options, optionHeight, listWrapperElement) {
3
+ export default function usePointer(options, optionElements, listWrapperElement) {
4
4
  const pointer = ref(null);
5
5
  const pointerDirty = ref(false);
6
6
 
7
+ const activeElementHeight = computed(() => {
8
+ const isGroupLabel = toValue(optionElements).at(pointer.value).dataset.groupLabel;
9
+ const groupIndex = 2;
10
+
11
+ if (isGroupLabel) {
12
+ return toValue(optionElements)
13
+ .at(pointer.value - groupIndex)
14
+ .getBoundingClientRect().height;
15
+ }
16
+
17
+ return toValue(optionElements).at(pointer.value).getBoundingClientRect().height;
18
+ });
19
+
7
20
  const pointerPosition = computed(() => {
8
- return pointer.value * optionHeight || 0;
21
+ return pointer.value * activeElementHeight.value || 0;
9
22
  });
10
23
 
11
24
  function scrollWrapperToElement() {
12
- const visibleElements = listWrapperElement.value.getBoundingClientRect().height / optionHeight;
25
+ const visibleElements =
26
+ listWrapperElement.value.getBoundingClientRect().height / activeElementHeight.value;
13
27
  const currentElement = visibleElements - 1;
14
- const currentPointerPosition = pointerPosition.value - currentElement * optionHeight;
28
+ const currentPointerPosition =
29
+ pointerPosition.value - currentElement * activeElementHeight.value;
15
30
 
16
31
  if (listWrapperElement.value.scrollTop <= currentPointerPosition) {
17
32
  listWrapperElement.value.scrollTop = currentPointerPosition;
@@ -22,10 +37,7 @@ export default function usePointer(options, optionHeight, listWrapperElement) {
22
37
  if (pointer.value < options.length - 1) {
23
38
  pointer.value++;
24
39
 
25
- const isGroup =
26
- options[pointer.value].$isLabel ||
27
- options[pointer.value].isSubGroup ||
28
- options[pointer.value].$groupLabel;
40
+ const isGroup = options[pointer.value].isSubGroup || options[pointer.value].groupLabel;
29
41
 
30
42
  if (listWrapperElement) scrollWrapperToElement();
31
43
 
@@ -41,10 +53,7 @@ export default function usePointer(options, optionHeight, listWrapperElement) {
41
53
  if (pointer.value > 0) {
42
54
  pointer.value--;
43
55
 
44
- const isGroup =
45
- options[pointer.value].$isLabel ||
46
- options[pointer.value].isSubGroup ||
47
- options[pointer.value].$groupLabel;
56
+ const isGroup = options[pointer.value].isSubGroup || options[pointer.value].groupLabel;
48
57
 
49
58
  if (listWrapperElement && listWrapperElement.value.scrollTop >= pointerPosition.value) {
50
59
  listWrapperElement.value.scrollTop = pointerPosition.value;
@@ -54,7 +63,7 @@ export default function usePointer(options, optionHeight, listWrapperElement) {
54
63
  pointerBackward();
55
64
  }
56
65
  } else {
57
- if (options[pointer.value] && options[0].$isLabel) {
66
+ if (options[pointer.value] && options[0].groupLabel) {
58
67
  pointerForward();
59
68
  }
60
69
  }
@@ -66,7 +66,7 @@ export default /*tw*/ {
66
66
  labelKey: "label",
67
67
  valueKey: "id",
68
68
  optionHeight: 40,
69
- maxHeight: undefined,
69
+ visibleOptions: undefined,
70
70
  disabled: false,
71
71
  addOption: false,
72
72
  },
@@ -13,15 +13,14 @@
13
13
  v-for="(option, index) of options"
14
14
  :id="`${id}-${index}`"
15
15
  :key="index"
16
- :role="!(option && (option.$isLabel || option.$isDisabled)) ? 'option' : null"
17
16
  v-bind="optionItemAttrs"
17
+ ref="optionsRef"
18
+ :role="!(option && option.groupLabel) ? 'option' : null"
19
+ :data-group-label="Boolean(option.groupLabel)"
18
20
  >
19
21
  <!-- option title -->
20
22
  <span
21
- v-if="
22
- !(option && (option.$isLabel || option.$isDisabled || option.isSubGroup)) &&
23
- !option.isHidden
24
- "
23
+ v-if="!(option && (option.groupLabel || option.isSubGroup)) && !option.isHidden"
25
24
  v-bind="optionAttrs(optionHighlight(index, option))"
26
25
  @click.stop="select(option)"
27
26
  @mouseenter.self="pointerSet(index)"
@@ -42,14 +41,8 @@
42
41
  </span>
43
42
 
44
43
  <!-- group title -->
45
- <div
46
- v-if="
47
- option &&
48
- (option.$isLabel || option.$isDisabled || option.isSubGroup) &&
49
- !option.isHidden
50
- "
51
- >
52
- <div v-if="option.$groupLabel" v-bind="groupLabelAttrs" v-text="option.$groupLabel" />
44
+ <div v-if="option && (option.groupLabel || option.isSubGroup) && !option.isHidden">
45
+ <div v-if="option.groupLabel" v-bind="groupLabelAttrs" v-text="option.groupLabel" />
53
46
 
54
47
  <div
55
48
  v-else-if="option.isSubGroup"
@@ -180,19 +173,11 @@ const props = defineProps({
180
173
  },
181
174
 
182
175
  /**
183
- * List max height in pixels.
176
+ * Amount of options you can see without scroll.
184
177
  */
185
- maxHeight: {
178
+ visibleOptions: {
186
179
  type: Number,
187
- default: UIService.get(defaultConfig, UDropdownList).default.maxHeight,
188
- },
189
-
190
- /**
191
- * List option height in pixels.
192
- */
193
- optionHeight: {
194
- type: Number,
195
- default: UIService.get(defaultConfig, UDropdownList).default.optionHeight,
180
+ default: UIService.get(defaultConfig, UDropdownList).default.visibleOptions,
196
181
  },
197
182
 
198
183
  /**
@@ -216,9 +201,10 @@ const props = defineProps({
216
201
  const emit = defineEmits(["update:modelValue", "addOption"]);
217
202
 
218
203
  const wrapperRef = ref(null);
204
+ const optionsRef = ref([]);
219
205
 
220
206
  const { pointer, pointerDirty, pointerSet, pointerBackward, pointerForward, pointerReset } =
221
- usePointer(props.options, props.optionHeight, wrapperRef);
207
+ usePointer(props.options, optionsRef, wrapperRef);
222
208
 
223
209
  const {
224
210
  config,
@@ -240,7 +226,15 @@ const {
240
226
 
241
227
  const { tm } = useLocale();
242
228
 
243
- defineExpose({ pointerSet, pointerBackward, pointerForward, pointerReset, addPointerElement });
229
+ defineExpose({
230
+ pointerSet,
231
+ pointerBackward,
232
+ pointerForward,
233
+ pointerReset,
234
+ addPointerElement,
235
+ optionsRef,
236
+ wrapperRef,
237
+ });
244
238
 
245
239
  const i18nGlobal = tm(UDropdownList);
246
240
  const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
@@ -249,16 +243,21 @@ const addOptionKeyCombination = computed(() => {
249
243
  return isMac ? "(⌘ + Enter)" : "(Ctrl + Enter)";
250
244
  });
251
245
 
252
- const wrapperHeight = computed(() =>
253
- props.maxHeight === undefined ? "auto" : `${props.maxHeight}px`,
254
- );
246
+ const wrapperHeight = computed(() => {
247
+ const maxHeight = optionsRef.value
248
+ .slice(0, props.visibleOptions)
249
+ .map((el) => el.getBoundingClientRect().height)
250
+ .reduce((acc, cur) => acc + cur, 0);
251
+
252
+ return props.visibleOptions === undefined ? "auto" : `${maxHeight}px`;
253
+ });
255
254
 
256
255
  function onClickAddOption() {
257
256
  emit("addOption");
258
257
  }
259
258
 
260
259
  function select(option, key) {
261
- if (props.disabled || option.$isDisabled || option.$isLabel) {
260
+ if (props.disabled || option.groupLabel) {
262
261
  return;
263
262
  }
264
263
 
@@ -123,9 +123,8 @@ export default /*tw*/ {
123
123
  openDirection: "auto",
124
124
  labelKey: "label",
125
125
  valueKey: "id",
126
- optionsLimit: 100,
127
- maxHeight: 300,
128
- optionHeight: 40,
126
+ optionsLimit: 0,
127
+ visibleOptions: 8,
129
128
  multiple: false,
130
129
  disabled: false,
131
130
  searchable: true,
@@ -222,12 +222,12 @@ Multiple.args = { modelValue: [] };
222
222
  export const openDirection = OpenDirectionsTemplate.bind({});
223
223
  openDirection.args = {};
224
224
 
225
- export const groupValues = GroupValuesTemplate.bind({});
226
- groupValues.args = {
225
+ export const groupValue = GroupValuesTemplate.bind({});
226
+ groupValue.args = {
227
227
  modelValue: "",
228
228
  modelValueMultiple: [],
229
- groupValues: "libs",
230
- groupLabel: "language",
229
+ groupValueKey: "libs",
230
+ groupLabelKey: "language",
231
231
  labelKey: "name",
232
232
  valueKey: "name",
233
233
  options: [
@@ -117,7 +117,6 @@
117
117
  :id="id"
118
118
  ref="searchInputRef"
119
119
  v-model="search"
120
- :name="name"
121
120
  type="text"
122
121
  autocomplete="off"
123
122
  :spellcheck="false"
@@ -159,14 +158,13 @@
159
158
  </div>
160
159
 
161
160
  <UDropdownList
162
- v-show="isOpen"
161
+ v-if="isOpen"
163
162
  ref="dropdownListRef"
164
163
  v-model="dropdownValue"
165
164
  :options="filteredOptions"
166
165
  :disabled="disabled"
167
166
  :size="size"
168
- :max-height="optimizedHeight"
169
- :option-height="optionHeight"
167
+ :visible-options="visibleOptions"
170
168
  :value-key="valueKey"
171
169
  :label-key="labelKey"
172
170
  :add-option="addOption"
@@ -318,8 +316,7 @@ const props = defineProps({
318
316
  /**
319
317
  * Set a name of the property containing the group label.
320
318
  */
321
- // TODO: groupLabelKey ???
322
- groupLabel: {
319
+ groupLabelKey: {
323
320
  type: String,
324
321
  default: "label",
325
322
  },
@@ -327,17 +324,7 @@ const props = defineProps({
327
324
  /**
328
325
  * Set a name of the property containing the group values.
329
326
  */
330
- // TODO: groupValueKey ???
331
- groupValues: {
332
- type: String,
333
- default: "",
334
- },
335
-
336
- /**
337
- * name attribute to match optional label element.
338
- */
339
- // TODO: what is it?
340
- name: {
327
+ groupValueKey: {
341
328
  type: String,
342
329
  default: "",
343
330
  },
@@ -351,18 +338,11 @@ const props = defineProps({
351
338
  },
352
339
 
353
340
  /**
354
- * Sets maxHeight style value of the dropdown
341
+ * Amount of options you can see without scroll.
355
342
  */
356
- // TODO: Should be in option amount not in pixels (option may have be different height)
357
- maxHeight: {
358
- type: Number,
359
- default: UIService.get(defaultConfig, USelect).default.maxHeight,
360
- },
361
-
362
- // TODO: there is no desc
363
- optionHeight: {
343
+ visibleOptions: {
364
344
  type: Number,
365
- default: UIService.get(defaultConfig, USelect).default.optionHeight,
345
+ default: UIService.get(defaultConfig, USelect).default.visibleOptions,
366
346
  },
367
347
 
368
348
  /**
@@ -448,7 +428,6 @@ const { tm } = useLocale();
448
428
  const isOpen = ref(false);
449
429
  const preferredOpenDirection = ref(DIRECTION.bottom);
450
430
  const search = ref("");
451
- const optimizedHeight = ref(props.maxHeight);
452
431
 
453
432
  const dropdownListRef = ref(null);
454
433
  const wrapperRef = ref(null);
@@ -543,17 +522,17 @@ const filteredOptions = computed(() => {
543
522
  let options = props.multiple
544
523
  ? SelectService.removeSelectedValues(
545
524
  props.options,
546
- props.groupValues,
525
+ props.groupValueKey,
547
526
  props.valueKey,
548
527
  props.modelValue,
549
528
  )
550
529
  : [...props.options];
551
530
 
552
- options = props.groupValues
531
+ options = props.groupValueKey
553
532
  ? filterAndFlat(options, normalizedSearch, props.labelKey)
554
533
  : SelectService.filterOptions(options, normalizedSearch, props.labelKey);
555
534
 
556
- return options.slice(0, props.optionsLimit);
535
+ return options.slice(0, props.optionsLimit || options.length);
557
536
  });
558
537
 
559
538
  const localValue = computed(() => {
@@ -561,14 +540,14 @@ const localValue = computed(() => {
561
540
  return SelectService.getCurrentOption(
562
541
  props.modelValue,
563
542
  props.options,
564
- props.groupValues,
543
+ props.groupValueKey,
565
544
  props.valueKey,
566
545
  );
567
546
  }
568
547
 
569
548
  return props.modelValue
570
549
  ? props.modelValue.map((item) =>
571
- SelectService.getCurrentOption(item, props.options, props.groupValues, props.valueKey),
550
+ SelectService.getCurrentOption(item, props.options, props.groupValueKey, props.valueKey),
572
551
  )
573
552
  : [];
574
553
  });
@@ -610,7 +589,6 @@ const onSearchChange = debounce(async function (query) {
610
589
 
611
590
  function getOptionLabel(option) {
612
591
  if (!option) return "";
613
- if (option.$isLabel) return option.$groupLabel;
614
592
 
615
593
  return option[props.labelKey] || "";
616
594
  }
@@ -640,11 +618,11 @@ function filterAndFlat(options, search, label) {
640
618
  options,
641
619
  search,
642
620
  label,
643
- props.groupValues,
644
- props.groupLabel,
621
+ props.groupValueKey,
622
+ props.groupLabelKey,
645
623
  );
646
624
 
647
- return SelectService.flattenOptions(filteredGroups, props.groupValues, props.groupLabel);
625
+ return SelectService.flattenOptions(filteredGroups, props.groupValueKey, props.groupLabelKey);
648
626
  }
649
627
 
650
628
  function toggle() {
@@ -681,25 +659,22 @@ function activate() {
681
659
  }
682
660
 
683
661
  function adjustPosition() {
684
- if (typeof window === "undefined") return;
662
+ if (typeof window === "undefined" || !dropdownListRef.value) return;
685
663
 
664
+ const dropdownHeight = dropdownListRef.value.wrapperRef.getBoundingClientRect().height;
686
665
  const spaceAbove = wrapperRef.value.getBoundingClientRect().top;
687
666
  const spaceBelow = window.innerHeight - wrapperRef.value.getBoundingClientRect().bottom;
688
- const hasEnoughSpaceBelow = spaceBelow > props.maxHeight;
667
+ const hasEnoughSpaceBelow = spaceBelow > dropdownHeight;
689
668
 
690
669
  if (hasEnoughSpaceBelow || spaceBelow > spaceAbove || props.openDirection === DIRECTION.bottom) {
691
670
  preferredOpenDirection.value = DIRECTION.bottom;
692
- // TODO: magic numbers
693
- optimizedHeight.value = Math.min(spaceBelow - 40, props.maxHeight);
694
671
  } else {
695
672
  preferredOpenDirection.value = DIRECTION.top;
696
- optimizedHeight.value = Math.min(spaceAbove - 40, props.maxHeight);
697
673
  }
698
674
  }
699
675
 
700
676
  function removeElement(option, shouldClose = true) {
701
677
  if (props.disabled) return;
702
- if (option.$isDisabled) return;
703
678
 
704
679
  if (props.noClear && !props.multiple) {
705
680
  deactivate();
@@ -4,12 +4,15 @@ export default class SelectService {
4
4
  ? options
5
5
  .filter((option) => this.interpolateLabel(option, label).includes(search))
6
6
  .sort((a, b) => {
7
- return this.interpolateLabel(a, label).length - this.interpolateLabel(b, label).length;
7
+ return (
8
+ this.interpolateLabel(a, label).indexOf(search) -
9
+ this.interpolateLabel(b, label).indexOf(search)
10
+ );
8
11
  })
9
12
  : options;
10
13
  }
11
14
 
12
- static filterGroups(options, search, label, values, groupLabel) {
15
+ static filterGroups(options, search, label, values, groupLabelKey) {
13
16
  return options.map((group) => {
14
17
  if (!group[values]) {
15
18
  // eslint-disable-next-line no-console
@@ -22,20 +25,17 @@ export default class SelectService {
22
25
 
23
26
  return groupOptions.length
24
27
  ? {
25
- [groupLabel]: group[groupLabel],
28
+ [groupLabelKey]: group[groupLabelKey],
26
29
  [values]: groupOptions,
27
30
  }
28
31
  : [];
29
32
  });
30
33
  }
31
34
 
32
- static flattenOptions(options, values, label) {
35
+ static flattenOptions(options, values, groupLabelKey) {
33
36
  return options.reduce((prev, curr) => {
34
37
  if (curr[values] && curr[values].length) {
35
- prev.push({
36
- $groupLabel: curr[label],
37
- $isLabel: true,
38
- });
38
+ prev.push({ groupLabel: curr[groupLabelKey] });
39
39
 
40
40
  return prev.concat(curr[values]);
41
41
  }
@@ -51,25 +51,27 @@ export default class SelectService {
51
51
  return !opt;
52
52
  }
53
53
 
54
- static removeEmptyGroups(options, groupValues) {
54
+ static removeEmptyGroups(options, groupValueKey) {
55
55
  return options.filter((group) => {
56
- return Boolean(group[groupValues].filter((item) => !item.isSubGroup).length);
56
+ return Boolean(group[groupValueKey].filter((item) => !item.isSubGroup).length);
57
57
  });
58
58
  }
59
59
 
60
- static removeSelectedValues(options, groupValues, valueKey, modelValue) {
61
- if (!groupValues) {
60
+ static removeSelectedValues(options, groupValueKey, valueKey, modelValue) {
61
+ if (!groupValueKey) {
62
62
  return options.filter((option) => !modelValue.includes(option[valueKey]));
63
63
  }
64
64
 
65
65
  const availableValues = options.map((group) => {
66
66
  return {
67
67
  ...group,
68
- [groupValues]: group[groupValues].filter((item) => !modelValue.includes(item[valueKey])),
68
+ [groupValueKey]: group[groupValueKey].filter(
69
+ (item) => !modelValue.includes(item[valueKey]),
70
+ ),
69
71
  };
70
72
  });
71
73
 
72
- return this.removeEmptyGroups(availableValues, groupValues);
74
+ return this.removeEmptyGroups(availableValues, groupValueKey);
73
75
  }
74
76
 
75
77
  static interpolateLabel(option, label) {
@@ -78,17 +80,17 @@ export default class SelectService {
78
80
  return label ? option[label] : option;
79
81
  }
80
82
 
81
- static getGroupOption(item, options, groupValues, valueKey) {
83
+ static getGroupOption(item, options, groupValueKey, valueKey) {
82
84
  const groupOptions = options.find((option) =>
83
- option[groupValues]?.find((value) => value[valueKey] === item),
85
+ option[groupValueKey]?.find((value) => value[valueKey] === item),
84
86
  );
85
87
 
86
- return groupOptions?.[groupValues].find((value) => value[valueKey] === item);
88
+ return groupOptions?.[groupValueKey].find((value) => value[valueKey] === item);
87
89
  }
88
90
 
89
- static getCurrentOption(item, options, groupValues, valueKey) {
90
- if (groupValues) {
91
- return this.getGroupOption(item, options, groupValues, valueKey);
91
+ static getCurrentOption(item, options, groupValueKey, valueKey) {
92
+ if (groupValueKey) {
93
+ return this.getGroupOption(item, options, groupValueKey, valueKey);
92
94
  }
93
95
 
94
96
  const value = item[valueKey] || item;
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "vueless",
4
- "version": "0.0.185",
4
+ "version": "0.0.186",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",
@@ -2848,22 +2848,13 @@
2848
2848
  "default": "md"
2849
2849
  },
2850
2850
  {
2851
- "name": "maxHeight",
2852
- "description": "List max height in pixels.",
2851
+ "name": "visibleOptions",
2852
+ "description": "Amount of options you can see without scroll.",
2853
2853
  "value": {
2854
2854
  "kind": "expression",
2855
2855
  "type": "number"
2856
2856
  }
2857
2857
  },
2858
- {
2859
- "name": "optionHeight",
2860
- "description": "List option height in pixels.",
2861
- "value": {
2862
- "kind": "expression",
2863
- "type": "number"
2864
- },
2865
- "default": "40"
2866
- },
2867
2858
  {
2868
2859
  "name": "id",
2869
2860
  "description": "@ignore: Generates unique element id.",
@@ -6211,7 +6202,7 @@
6211
6202
  "default": "id"
6212
6203
  },
6213
6204
  {
6214
- "name": "groupLabel",
6205
+ "name": "groupLabelKey",
6215
6206
  "description": "Set a name of the property containing the group label.",
6216
6207
  "value": {
6217
6208
  "kind": "expression",
@@ -6220,7 +6211,7 @@
6220
6211
  "default": "\"label\""
6221
6212
  },
6222
6213
  {
6223
- "name": "groupValues",
6214
+ "name": "groupValueKey",
6224
6215
  "description": "Set a name of the property containing the group values.",
6225
6216
  "value": {
6226
6217
  "kind": "expression",
@@ -6228,15 +6219,6 @@
6228
6219
  },
6229
6220
  "default": "\"\""
6230
6221
  },
6231
- {
6232
- "name": "name",
6233
- "description": "name attribute to match optional label element.",
6234
- "value": {
6235
- "kind": "expression",
6236
- "type": "string"
6237
- },
6238
- "default": "\"\""
6239
- },
6240
6222
  {
6241
6223
  "name": "optionsLimit",
6242
6224
  "description": "Number of options displayed in the dropdown.",
@@ -6244,24 +6226,16 @@
6244
6226
  "kind": "expression",
6245
6227
  "type": "number"
6246
6228
  },
6247
- "default": "100"
6248
- },
6249
- {
6250
- "name": "maxHeight",
6251
- "description": "Sets maxHeight style value of the dropdown",
6252
- "value": {
6253
- "kind": "expression",
6254
- "type": "number"
6255
- },
6256
- "default": "300"
6229
+ "default": "0"
6257
6230
  },
6258
6231
  {
6259
- "name": "optionHeight",
6232
+ "name": "visibleOptions",
6233
+ "description": "Amount of options you can see without scroll.",
6260
6234
  "value": {
6261
6235
  "kind": "expression",
6262
6236
  "type": "number"
6263
6237
  },
6264
- "default": "40"
6238
+ "default": "8"
6265
6239
  },
6266
6240
  {
6267
6241
  "name": "noClear",