@sewadah/selaras 0.3.0 → 0.3.1

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 (141) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +14 -1
  3. package/dist/runtime/components/Autocomplete.vue +3 -1
  4. package/dist/runtime/components/Calendar.d.vue.ts +69 -0
  5. package/dist/runtime/components/Calendar.vue +163 -0
  6. package/dist/runtime/components/Calendar.vue.d.ts +69 -0
  7. package/dist/runtime/components/Callout.vue +12 -1
  8. package/dist/runtime/components/CodeGroup.vue +13 -3
  9. package/dist/runtime/components/CodeTree.d.vue.ts +2 -0
  10. package/dist/runtime/components/CodeTree.vue +52 -1
  11. package/dist/runtime/components/CodeTree.vue.d.ts +2 -0
  12. package/dist/runtime/components/ColorPicker.d.vue.ts +8 -0
  13. package/dist/runtime/components/ColorPicker.vue +66 -45
  14. package/dist/runtime/components/ColorPicker.vue.d.ts +8 -0
  15. package/dist/runtime/components/Column.js +1 -1
  16. package/dist/runtime/components/CommandPalette.d.vue.ts +3 -1
  17. package/dist/runtime/components/CommandPalette.vue +5 -0
  18. package/dist/runtime/components/CommandPalette.vue.d.ts +3 -1
  19. package/dist/runtime/components/ContentNavigation.d.vue.ts +8 -1
  20. package/dist/runtime/components/ContentNavigation.vue +9 -2
  21. package/dist/runtime/components/ContentNavigation.vue.d.ts +8 -1
  22. package/dist/runtime/components/ContextMenu.d.vue.ts +8 -1
  23. package/dist/runtime/components/ContextMenu.vue +16 -4
  24. package/dist/runtime/components/ContextMenu.vue.d.ts +8 -1
  25. package/dist/runtime/components/DatePicker.d.vue.ts +10 -3
  26. package/dist/runtime/components/DatePicker.vue +24 -16
  27. package/dist/runtime/components/DatePicker.vue.d.ts +10 -3
  28. package/dist/runtime/components/Dropdown.d.vue.ts +33 -6
  29. package/dist/runtime/components/Dropdown.vue +143 -11
  30. package/dist/runtime/components/Dropdown.vue.d.ts +33 -6
  31. package/dist/runtime/components/FileTree.d.vue.ts +2 -0
  32. package/dist/runtime/components/FileTree.vue +8 -4
  33. package/dist/runtime/components/FileTree.vue.d.ts +2 -0
  34. package/dist/runtime/components/FormField.d.vue.ts +4 -0
  35. package/dist/runtime/components/FormField.vue +19 -4
  36. package/dist/runtime/components/FormField.vue.d.ts +4 -0
  37. package/dist/runtime/components/Input.vue +3 -2
  38. package/dist/runtime/components/Kbd.vue +12 -2
  39. package/dist/runtime/components/NavigationMenu.d.vue.ts +10 -10
  40. package/dist/runtime/components/NavigationMenu.vue +13 -9
  41. package/dist/runtime/components/NavigationMenu.vue.d.ts +10 -10
  42. package/dist/runtime/components/NavigationMenuAccordionItem.vue +5 -1
  43. package/dist/runtime/components/NavigationMenuFlyoutList.vue +7 -4
  44. package/dist/runtime/components/Popover.d.vue.ts +8 -2
  45. package/dist/runtime/components/Popover.vue +9 -4
  46. package/dist/runtime/components/Popover.vue.d.ts +8 -2
  47. package/dist/runtime/components/ProsePre.d.vue.ts +2 -0
  48. package/dist/runtime/components/ProsePre.vue +2 -1
  49. package/dist/runtime/components/ProsePre.vue.d.ts +2 -0
  50. package/dist/runtime/components/Select.vue +3 -1
  51. package/dist/runtime/components/SplitterPanel.d.vue.ts +2 -2
  52. package/dist/runtime/components/SplitterPanel.vue.d.ts +2 -2
  53. package/dist/runtime/components/Table.vue +95 -26
  54. package/dist/runtime/components/Tabs.d.vue.ts +44 -4
  55. package/dist/runtime/components/Tabs.vue +171 -18
  56. package/dist/runtime/components/Tabs.vue.d.ts +44 -4
  57. package/dist/runtime/components/Textarea.vue +3 -2
  58. package/dist/runtime/components/Timeline.d.vue.ts +19 -0
  59. package/dist/runtime/components/Timeline.vue +99 -0
  60. package/dist/runtime/components/Timeline.vue.d.ts +19 -0
  61. package/dist/runtime/components/Toast.d.vue.ts +10 -1
  62. package/dist/runtime/components/Toast.vue +94 -8
  63. package/dist/runtime/components/Toast.vue.d.ts +10 -1
  64. package/dist/runtime/components/Tooltip.d.vue.ts +8 -2
  65. package/dist/runtime/components/Tooltip.vue +9 -4
  66. package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
  67. package/dist/runtime/composables/use-form-field.d.ts +1 -0
  68. package/dist/runtime/composables/use-table.d.ts +3 -0
  69. package/dist/runtime/composables/use-table.js +10 -4
  70. package/dist/runtime/composables/use-toast.js +5 -1
  71. package/dist/runtime/directives/hotkey.d.ts +13 -0
  72. package/dist/runtime/directives/hotkey.js +107 -0
  73. package/dist/runtime/directives/index.d.ts +2 -0
  74. package/dist/runtime/directives/index.js +2 -0
  75. package/dist/runtime/directives/mask.d.ts +30 -0
  76. package/dist/runtime/directives/mask.js +165 -0
  77. package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +29 -25
  78. package/dist/runtime/internal/ComboboxSelectBase.vue +103 -19
  79. package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +29 -25
  80. package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +16 -11
  81. package/dist/runtime/internal/ComboboxSelectBody.vue +49 -10
  82. package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +16 -11
  83. package/dist/runtime/internal/DatePickerCalendarBody.vue +1 -1
  84. package/dist/runtime/internal/DatePickerRangeCalendarBody.vue +1 -1
  85. package/dist/runtime/internal/ShortcutHint.d.vue.ts +6 -0
  86. package/dist/runtime/internal/ShortcutHint.vue +13 -0
  87. package/dist/runtime/internal/ShortcutHint.vue.d.ts +6 -0
  88. package/dist/runtime/internal/ToastItemRenderer.d.vue.ts +9 -1
  89. package/dist/runtime/internal/ToastItemRenderer.vue +55 -8
  90. package/dist/runtime/internal/ToastItemRenderer.vue.d.ts +9 -1
  91. package/dist/runtime/internal/combobox-select.js +2 -1
  92. package/dist/runtime/internal/programmatic-services.d.ts +2 -0
  93. package/dist/runtime/internal/programmatic-services.js +8 -0
  94. package/dist/runtime/internal/toast-stack.d.ts +36 -0
  95. package/dist/runtime/internal/toast-stack.js +36 -0
  96. package/dist/runtime/structural.css +1 -1
  97. package/dist/runtime/table.d.ts +1 -1
  98. package/dist/runtime/theme/alert-dialog.js +1 -1
  99. package/dist/runtime/theme/calendar.d.ts +86 -0
  100. package/dist/runtime/theme/calendar.js +19 -0
  101. package/dist/runtime/theme/callout.d.ts +0 -12
  102. package/dist/runtime/theme/callout.js +8 -8
  103. package/dist/runtime/theme/code-tree.js +1 -1
  104. package/dist/runtime/theme/command-palette.js +1 -1
  105. package/dist/runtime/theme/content-navigation.d.ts +3 -0
  106. package/dist/runtime/theme/content-navigation.js +1 -0
  107. package/dist/runtime/theme/drawer.js +1 -1
  108. package/dist/runtime/theme/dropdown.d.ts +21 -0
  109. package/dist/runtime/theme/dropdown.js +15 -2
  110. package/dist/runtime/theme/file-tree.js +1 -1
  111. package/dist/runtime/theme/form-field.d.ts +18 -0
  112. package/dist/runtime/theme/form-field.js +6 -1
  113. package/dist/runtime/theme/modal.js +1 -1
  114. package/dist/runtime/theme/slideover.js +1 -1
  115. package/dist/runtime/theme/table.d.ts +27 -0
  116. package/dist/runtime/theme/table.js +11 -2
  117. package/dist/runtime/theme/tabs.d.ts +87 -3
  118. package/dist/runtime/theme/tabs.js +46 -22
  119. package/dist/runtime/theme/timeline.d.ts +251 -0
  120. package/dist/runtime/theme/timeline.js +84 -0
  121. package/dist/runtime/theme/toast.d.ts +92 -0
  122. package/dist/runtime/theme/toast.js +29 -7
  123. package/dist/runtime/theme-config.d.ts +12 -1
  124. package/dist/runtime/types/index.d.ts +6 -1
  125. package/dist/runtime/utils/arrow.d.ts +21 -0
  126. package/dist/runtime/utils/arrow.js +10 -0
  127. package/dist/runtime/utils/file-icons.js +2 -0
  128. package/dist/runtime/utils/icons.d.ts +2 -0
  129. package/dist/runtime/utils/icons.js +2 -0
  130. package/dist/runtime/utils/messages.d.ts +6 -0
  131. package/dist/runtime/utils/messages.js +6 -0
  132. package/dist/runtime/utils/navigation-menu.d.ts +7 -0
  133. package/dist/runtime/utils/navigation-menu.js +3 -0
  134. package/dist/runtime/utils/overlay.d.ts +17 -0
  135. package/dist/runtime/utils/overlay.js +7 -0
  136. package/dist/runtime/utils/select-contracts.d.ts +7 -1
  137. package/dist/runtime/utils/table-columns.js +1 -1
  138. package/dist/runtime/utils/table-contracts.d.ts +9 -1
  139. package/dist/runtime/utils/timeline-contracts.d.ts +42 -0
  140. package/dist/runtime/utils/timeline-contracts.js +0 -0
  141. package/package.json +2 -1
@@ -27,7 +27,9 @@ import { useIcons } from "../composables/use-icons";
27
27
  import { useIsMobile } from "../composables/use-media-query";
28
28
  import { useMessages } from "../composables/use-messages";
29
29
  import { selectTheme } from "../theme/select";
30
+ import { arrowContentProps, arrowElementProps } from "../utils/arrow";
30
31
  import { isNativeInputA11yAttr, isNativeInputAttr, isNativeInputEvent } from "../utils/native-input";
32
+ import { overlayPortalProps } from "../utils/overlay";
31
33
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
32
34
  import { resolveSlot, useComponentTheme, useFallthroughAttrs, useRootProps, useThemeBindings } from "../utils/ui";
33
35
  import { useComboboxSelect } from "./combobox-select";
@@ -63,7 +65,9 @@ const props = defineProps({
63
65
  searchTerm: { type: String, required: false },
64
66
  resetSearchTermOnBlur: { type: Boolean, required: false, default: true },
65
67
  resetSearchTermOnSelect: { type: Boolean, required: false, default: true },
66
- arrow: { type: Boolean, required: false, default: false },
68
+ arrow: { type: [Boolean, Object], required: false, default: false },
69
+ positioning: { type: Object, required: false },
70
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: void 0 },
67
71
  adaptive: { type: Boolean, required: false },
68
72
  ui: { type: Object, required: false }
69
73
  });
@@ -210,11 +214,13 @@ function onTriggerKeydown(event) {
210
214
  }
211
215
  const internalSearchText = ref(props.searchTerm ?? (props.creatable && !props.multiple && props.resetSearchTermOnSelect ? selectedOptions.value[0]?.label ?? "" : ""));
212
216
  const modeSearchLock = ref(false);
217
+ const keyboardNavigatedOptions = ref(false);
213
218
  const searchText = computed({
214
219
  get: () => props.searchTerm ?? internalSearchText.value,
215
220
  set: (value) => {
216
221
  if (modeSearchLock.value)
217
222
  return;
223
+ keyboardNavigatedOptions.value = false;
218
224
  internalSearchText.value = value;
219
225
  emit("update:searchTerm", value);
220
226
  }
@@ -235,22 +241,77 @@ function revertUnmatchedText() {
235
241
  return true;
236
242
  }
237
243
  function onSearchKeydown(event) {
238
- if (event.key !== "Enter" || event.isComposing || isSearchComposing.value || event.keyCode === 229)
244
+ if (event.target !== editableInput.value)
239
245
  return;
246
+ if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key) && !event.isComposing && !isSearchComposing.value) {
247
+ keyboardNavigatedOptions.value = true;
248
+ return;
249
+ }
250
+ if (event.key === "Backspace" && props.creatable && props.multiple && props.displayMode === "comma" && !event.isComposing && !isSearchComposing.value && event.keyCode !== 229 && !searchText.value && !editableInput.value.value && selectedValues.value.length) {
251
+ removeValue(selectedValues.value.at(-1));
252
+ event.preventDefault();
253
+ return;
254
+ }
255
+ if (event.key !== "Enter")
256
+ return;
257
+ if (event.isComposing || isSearchComposing.value || event.keyCode === 229) {
258
+ event.stopPropagation();
259
+ return;
260
+ }
240
261
  if (revertUnmatchedText()) {
241
262
  event.preventDefault();
263
+ event.stopPropagation();
242
264
  return;
243
265
  }
244
- if (commitCreatableText(searchText.value)) {
245
- searchText.value = "";
266
+ if (props.forceSelection)
267
+ return;
268
+ if (props.creatable && !searchText.value.trim()) {
269
+ const activeId2 = editableInput.value?.getAttribute("aria-activedescendant");
270
+ const activeOption2 = activeId2 ? document.getElementById(activeId2) : null;
271
+ if (keyboardNavigatedOptions.value && activeOption2?.getAttribute("aria-selected") === "false")
272
+ return;
273
+ event.stopPropagation();
274
+ return;
275
+ }
276
+ const activeId = editableInput.value?.getAttribute("aria-activedescendant");
277
+ const activeOption = activeId ? document.getElementById(activeId) : null;
278
+ if ((!hasMatchingOption(searchText.value) || !activeOption || activeOption.hasAttribute("data-selaras-create-option")) && commitCreatableText(searchText.value)) {
279
+ if (props.multiple)
280
+ searchText.value = "";
246
281
  event.preventDefault();
282
+ event.stopPropagation();
247
283
  }
248
284
  }
285
+ const pointerInSuggestionContent = ref(false);
286
+ function endSuggestionPointer() {
287
+ pointerInSuggestionContent.value = false;
288
+ window.removeEventListener("pointerup", endSuggestionPointer);
289
+ window.removeEventListener("pointercancel", endSuggestionPointer);
290
+ }
291
+ function startSuggestionPointer() {
292
+ pointerInSuggestionContent.value = true;
293
+ window.addEventListener("pointerup", endSuggestionPointer);
294
+ window.addEventListener("pointercancel", endSuggestionPointer);
295
+ }
296
+ onUnmounted(() => {
297
+ if (typeof window !== "undefined")
298
+ endSuggestionPointer();
299
+ });
249
300
  function onSearchBlur() {
301
+ if (pointerInSuggestionContent.value)
302
+ return;
250
303
  if (revertUnmatchedText())
251
304
  return;
252
- if (commitCreatableText(searchText.value))
305
+ if (props.forceSelection)
306
+ return;
307
+ if (commitCreatableText(searchText.value) && props.multiple)
308
+ searchText.value = "";
309
+ }
310
+ function onComboboxValueUpdate(value) {
311
+ setValue(value ?? (props.multiple ? [] : void 0));
312
+ if (props.creatable && props.multiple) {
253
313
  searchText.value = "";
314
+ }
254
315
  }
255
316
  function onDropdownClick() {
256
317
  searchText.value = "";
@@ -332,6 +393,11 @@ const field = useFormField();
332
393
  const selectId = computed(() => props.id ?? field?.id);
333
394
  const selectInvalid = computed(() => props.invalid || (field?.invalid.value ?? false));
334
395
  const describedBy = computed(() => field?.describedBy.value);
396
+ const selectedSummaryId = `selaras-autocomplete-selection-${useId()}`;
397
+ const searchDescribedBy = computed(() => [
398
+ describedBy.value,
399
+ props.creatable && props.multiple && props.displayMode === "comma" && selectedOptions.value.length ? selectedSummaryId : void 0
400
+ ].filter(Boolean).join(" ") || void 0);
335
401
  const effectiveSize = computed(() => props.size ?? field?.size ?? "md");
336
402
  const clearSize = computed(() => ({ sm: "sm", md: "sm", lg: "md" })[effectiveSize.value]);
337
403
  const icons = useIcons();
@@ -367,8 +433,9 @@ const searchInputProps = computed(() => mergeProps(
367
433
  nativeSearchInputAttrs.value,
368
434
  { onCompositionstart: onSearchCompositionStart, onCompositionend: onSearchCompositionEnd }
369
435
  ));
370
- const contentProps = computed(() => resolveSlot(ui.value.content, props.ui?.content));
371
- const arrowProps = computed(() => resolveSlot(ui.value.arrow, props.ui?.arrow));
436
+ const contentProps = computed(() => ({ ...resolveSlot(ui.value.content, props.ui?.content), ...arrowContentProps(props.arrow), ...props.positioning }));
437
+ const portalProps = computed(() => overlayPortalProps(props.portal));
438
+ const arrowProps = computed(() => ({ ...resolveSlot(ui.value.arrow, props.ui?.arrow), ...arrowElementProps(props.arrow) }));
372
439
  const viewportProps = computed(() => resolveSlot(ui.value.viewport, props.ui?.viewport));
373
440
  const groupProps = computed(() => resolveSlot(ui.value.group, props.ui?.group));
374
441
  const itemProps = computed(() => resolveSlot(ui.value.item, props.ui?.item));
@@ -461,6 +528,9 @@ const adaptiveUi = computed(() => ({
461
528
  const bodyProps = computed(() => ({
462
529
  searchable: props.searchable,
463
530
  creatable: props.creatable,
531
+ multiple: props.multiple,
532
+ forceSelection: props.forceSelection,
533
+ selectedValues: selectedValues.value,
464
534
  searchText: searchText.value,
465
535
  displayValue,
466
536
  items: props.items,
@@ -488,13 +558,13 @@ const bodyProps = computed(() => ({
488
558
  :model-value="rekaSelection"
489
559
  :multiple="multiple"
490
560
  :disabled="disabled"
491
- :ignore-filter="!searchable"
561
+ :ignore-filter="!searchable || creatable && multiple"
492
562
  :reset-search-term-on-blur="resetSearchTermOnBlur"
493
563
  :reset-search-term-on-select="resetSearchTermOnSelect"
494
564
  :data-selaras-color="colorRoleMarker"
495
565
  v-bind="rootProps"
496
566
  @update:open="updateOpen($event)"
497
- @update:model-value="(value2) => setValue(value2 == null ? multiple ? [] : void 0 : value2)"
567
+ @update:model-value="(value2) => onComboboxValueUpdate(value2)"
498
568
  >
499
569
  <ComboboxAnchor>
500
570
  <!--
@@ -506,7 +576,10 @@ const bodyProps = computed(() => ({
506
576
  <div
507
577
  v-if="creatable"
508
578
  data-ui-group-item
579
+ :data-selaras-field-filled="selectedValues.length ? '' : void 0"
580
+ :data-selaras-field-active="open ? '' : void 0"
509
581
  v-bind="triggerProps"
582
+ @keydown.capture="onSearchKeydown"
510
583
  >
511
584
  <!--
512
585
  TagsInputRoot doesn't own selection truth here - it only reads
@@ -561,6 +634,9 @@ const bodyProps = computed(() => ({
561
634
  </template>
562
635
  </Chip>
563
636
  </TagsInputItem>
637
+ <Tooltip v-if="overflowOptions.length" :text="overflowOptions.map((o) => o.label).join(', ')">
638
+ <span v-bind="chipOverflowProps">{{ messages.moreItems(overflowOptions.length) }}</span>
639
+ </Tooltip>
564
640
  <ComboboxInput v-model="searchText" as-child>
565
641
  <TagsInputInput
566
642
  :id="selectId"
@@ -568,31 +644,35 @@ const bodyProps = computed(() => ({
568
644
  :display-value="displayValue"
569
645
  :placeholder="placeholder"
570
646
  :aria-invalid="selectInvalid || void 0"
571
- :aria-describedby="describedBy"
647
+ :aria-describedby="searchDescribedBy"
572
648
  :aria-busy="loading || void 0"
573
649
  v-bind="searchInputProps"
574
- @keydown="onSearchKeydown"
575
650
  @blur="onSearchBlur"
576
651
  />
577
652
  </ComboboxInput>
578
653
  </TagsInputRoot>
654
+ <span v-if="multiple && displayMode === 'comma' && selectedOptions.length" :id="selectedSummaryId" class="sr-only">
655
+ {{ selectedOptions.map((o) => o.label).join(", ") }}
656
+ </span>
657
+ <span v-if="multiple && displayMode === 'comma' && selectedOptions.length" aria-hidden="true" v-bind="commaValueProps">
658
+ {{ commaText }}
659
+ </span>
660
+ <Tooltip v-if="multiple && displayMode === 'comma' && overflowOptions.length" :text="overflowOptions.map((o) => o.label).join(', ')">
661
+ <span v-bind="chipOverflowProps">{{ messages.moreItems(overflowOptions.length) }}</span>
662
+ </Tooltip>
579
663
  <ComboboxInput
580
- v-else
664
+ v-if="!(multiple && displayMode === 'chip')"
581
665
  :id="selectId"
582
666
  :ref="setEditableInput"
583
667
  v-model="searchText"
584
668
  :display-value="displayValue"
585
- :placeholder="placeholder"
669
+ :placeholder="multiple && displayMode === 'comma' && selectedOptions.length ? void 0 : placeholder"
586
670
  :aria-invalid="selectInvalid || void 0"
587
- :aria-describedby="describedBy"
671
+ :aria-describedby="searchDescribedBy"
588
672
  :aria-busy="loading || void 0"
589
673
  v-bind="searchInputProps"
590
- @keydown="onSearchKeydown"
591
674
  @blur="onSearchBlur"
592
675
  />
593
- <Tooltip v-if="overflowOptions.length" :text="overflowOptions.map((o) => o.label).join(', ')">
594
- <span v-bind="chipOverflowProps">{{ messages.moreItems(overflowOptions.length) }}</span>
595
- </Tooltip>
596
676
  <!--
597
677
  A plain Button, not ComboboxCancel as-child - this trigger is a
598
678
  <div>, so unlike the non-creatable branch below there's no real
@@ -635,6 +715,8 @@ const bodyProps = computed(() => ({
635
715
  :id="selectId"
636
716
  ref="selectTrigger"
637
717
  data-ui-group-item
718
+ :data-selaras-field-filled="selectedValues.length ? '' : void 0"
719
+ :data-selaras-field-active="open ? '' : void 0"
638
720
  :aria-invalid="selectInvalid || void 0"
639
721
  :aria-describedby="describedBy"
640
722
  :aria-busy="loading || void 0"
@@ -740,11 +822,13 @@ const bodyProps = computed(() => ({
740
822
  </ComboboxTrigger>
741
823
  </ComboboxAnchor>
742
824
 
743
- <ComboboxPortal v-if="!showMobileSelectModal">
825
+ <ComboboxPortal v-if="!showMobileSelectModal" v-bind="portalProps">
744
826
  <ComboboxContent
745
827
  position="popper" :side-offset="4"
828
+ :hide-when-empty="creatable && !multiple && !forceSelection"
746
829
  :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" :data-selaras-color="colorRoleMarker"
747
830
  v-bind="showMobileAutocompletePanel ? mergeProps(contentProps, mobilePanelProps) : contentProps"
831
+ @pointerdown.capture="startSuggestionPointer"
748
832
  >
749
833
  <ComboboxSelectBody v-bind="bodyProps" @update:search-text="searchText = $event">
750
834
  <template #header>
@@ -1,6 +1,8 @@
1
1
  import type { VariantProps } from 'tailwind-variants';
2
2
  import type { SelectThemeSlots } from '../theme/select.js';
3
+ import type { RoundedArrowConfig } from '../utils/arrow.js';
3
4
  import type { ColorRole } from '../utils/color-registry.js';
5
+ import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
4
6
  import type { UiProp } from '../utils/ui.js';
5
7
  import type { SelectItems, SelectOption } from './combobox-select.js';
6
8
  import { selectTheme } from '../theme/select.js';
@@ -43,7 +45,9 @@ export interface ComboboxSelectBaseProps {
43
45
  resetSearchTermOnBlur?: boolean;
44
46
  resetSearchTermOnSelect?: boolean;
45
47
  /** Shows a small pointer triangle connecting the panel to its trigger. */
46
- arrow?: boolean;
48
+ arrow?: boolean | RoundedArrowConfig;
49
+ positioning?: OverlayPositioning;
50
+ portal?: OverlayPortal;
47
51
  /** Opts into the component's accessible small-screen presentation. */
48
52
  adaptive?: boolean;
49
53
  ui?: UiProp<SelectThemeSlots>;
@@ -55,57 +59,57 @@ export interface ComboboxSelectBaseEmits {
55
59
  }
56
60
  declare var __VLS_43: {
57
61
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
58
- }, __VLS_94: {}, __VLS_101: {}, __VLS_116: {}, __VLS_140: {
62
+ }, __VLS_98: {}, __VLS_105: {}, __VLS_120: {}, __VLS_144: {
59
63
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
60
- }, __VLS_160: {
64
+ }, __VLS_164: {
61
65
  selected: import("./combobox-select.js").ResolvedOption | undefined;
62
- }, __VLS_171: {}, __VLS_178: {}, __VLS_185: {}, __VLS_213: {}, __VLS_216: {}, __VLS_219: {}, __VLS_222: {}, __VLS_225: {
66
+ }, __VLS_175: {}, __VLS_182: {}, __VLS_189: {}, __VLS_219: {}, __VLS_222: {}, __VLS_225: {}, __VLS_228: {}, __VLS_231: {
63
67
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
64
- }, __VLS_228: {
68
+ }, __VLS_234: {
65
69
  group: import("./combobox-select.js").SelectOptionGroup<import("../utils/select-contracts.js").SelectValue>;
66
- }, __VLS_231: {}, __VLS_267: string, __VLS_268: {}, __VLS_271: {
70
+ }, __VLS_237: {}, __VLS_273: string, __VLS_274: {}, __VLS_277: {
67
71
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
68
- }, __VLS_274: {
72
+ }, __VLS_280: {
69
73
  group: import("./combobox-select.js").SelectOptionGroup<import("../utils/select-contracts.js").SelectValue>;
70
74
  };
71
75
  type __VLS_Slots = {} & {
72
- [K in NonNullable<typeof __VLS_267>]?: (props: typeof __VLS_268) => any;
76
+ [K in NonNullable<typeof __VLS_273>]?: (props: typeof __VLS_274) => any;
73
77
  } & {
74
78
  item?: (props: typeof __VLS_43) => any;
75
79
  } & {
76
- 'clear-icon'?: (props: typeof __VLS_94) => any;
80
+ 'clear-icon'?: (props: typeof __VLS_98) => any;
77
81
  } & {
78
- 'loading-icon'?: (props: typeof __VLS_101) => any;
82
+ 'loading-icon'?: (props: typeof __VLS_105) => any;
79
83
  } & {
80
- 'dropdown-icon'?: (props: typeof __VLS_116) => any;
84
+ 'dropdown-icon'?: (props: typeof __VLS_120) => any;
81
85
  } & {
82
- item?: (props: typeof __VLS_140) => any;
86
+ item?: (props: typeof __VLS_144) => any;
83
87
  } & {
84
- value?: (props: typeof __VLS_160) => any;
88
+ value?: (props: typeof __VLS_164) => any;
85
89
  } & {
86
- 'clear-icon'?: (props: typeof __VLS_171) => any;
90
+ 'clear-icon'?: (props: typeof __VLS_175) => any;
87
91
  } & {
88
- 'loading-icon'?: (props: typeof __VLS_178) => any;
92
+ 'loading-icon'?: (props: typeof __VLS_182) => any;
89
93
  } & {
90
- 'dropdown-icon'?: (props: typeof __VLS_185) => any;
94
+ 'dropdown-icon'?: (props: typeof __VLS_189) => any;
91
95
  } & {
92
- header?: (props: typeof __VLS_213) => any;
96
+ header?: (props: typeof __VLS_219) => any;
93
97
  } & {
94
- 'filter-icon'?: (props: typeof __VLS_216) => any;
98
+ 'filter-icon'?: (props: typeof __VLS_222) => any;
95
99
  } & {
96
- empty?: (props: typeof __VLS_219) => any;
100
+ empty?: (props: typeof __VLS_225) => any;
97
101
  } & {
98
- 'empty-filter'?: (props: typeof __VLS_222) => any;
102
+ 'empty-filter'?: (props: typeof __VLS_228) => any;
99
103
  } & {
100
- item?: (props: typeof __VLS_225) => any;
104
+ item?: (props: typeof __VLS_231) => any;
101
105
  } & {
102
- group?: (props: typeof __VLS_228) => any;
106
+ group?: (props: typeof __VLS_234) => any;
103
107
  } & {
104
- footer?: (props: typeof __VLS_231) => any;
108
+ footer?: (props: typeof __VLS_237) => any;
105
109
  } & {
106
- item?: (props: typeof __VLS_271) => any;
110
+ item?: (props: typeof __VLS_277) => any;
107
111
  } & {
108
- group?: (props: typeof __VLS_274) => any;
112
+ group?: (props: typeof __VLS_280) => any;
109
113
  };
110
114
  declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBaseProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
111
115
  "update:modelValue": (value: string | number | (string | number)[] | undefined) => any;
@@ -3,6 +3,9 @@ export interface ComboboxSelectBodyProps {
3
3
  listbox?: boolean;
4
4
  searchable?: boolean;
5
5
  creatable?: boolean;
6
+ multiple?: boolean;
7
+ forceSelection?: boolean;
8
+ selectedValues?: (string | number)[];
6
9
  searchText: string;
7
10
  displayValue: (value: unknown) => string;
8
11
  items: SelectItems;
@@ -30,15 +33,15 @@ export interface ComboboxSelectBodyEmits {
30
33
  'update:searchText': [value: string];
31
34
  }
32
35
  declare function focusSearch(): boolean;
33
- declare var __VLS_1: {}, __VLS_3: {}, __VLS_21: {}, __VLS_29: {}, __VLS_37: {}, __VLS_57: {
36
+ declare var __VLS_1: {}, __VLS_3: {}, __VLS_21: {}, __VLS_29: {}, __VLS_31: {}, __VLS_39: {}, __VLS_61: {
34
37
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
35
- }, __VLS_82: {
38
+ }, __VLS_92: {
36
39
  group: import("./combobox-select.js").SelectOptionGroup<import("../utils/select-contracts.js").SelectValue>;
37
- }, __VLS_90: {
40
+ }, __VLS_102: {
38
41
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
39
- }, __VLS_109: {
42
+ }, __VLS_123: {
40
43
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
41
- }, __VLS_122: {};
44
+ }, __VLS_136: {};
42
45
  type __VLS_Slots = {} & {
43
46
  header?: (props: typeof __VLS_1) => any;
44
47
  } & {
@@ -48,17 +51,19 @@ type __VLS_Slots = {} & {
48
51
  } & {
49
52
  'empty-filter'?: (props: typeof __VLS_29) => any;
50
53
  } & {
51
- 'empty-filter'?: (props: typeof __VLS_37) => any;
54
+ 'empty-filter'?: (props: typeof __VLS_31) => any;
52
55
  } & {
53
- item?: (props: typeof __VLS_57) => any;
56
+ 'empty-filter'?: (props: typeof __VLS_39) => any;
54
57
  } & {
55
- group?: (props: typeof __VLS_82) => any;
58
+ item?: (props: typeof __VLS_61) => any;
56
59
  } & {
57
- item?: (props: typeof __VLS_90) => any;
60
+ group?: (props: typeof __VLS_92) => any;
58
61
  } & {
59
- item?: (props: typeof __VLS_109) => any;
62
+ item?: (props: typeof __VLS_102) => any;
60
63
  } & {
61
- footer?: (props: typeof __VLS_122) => any;
64
+ item?: (props: typeof __VLS_123) => any;
65
+ } & {
66
+ footer?: (props: typeof __VLS_136) => any;
62
67
  };
63
68
  declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBodyProps, {
64
69
  focusSearch: typeof focusSearch;
@@ -27,6 +27,9 @@ const props = defineProps({
27
27
  listbox: { type: Boolean, required: false },
28
28
  searchable: { type: Boolean, required: false },
29
29
  creatable: { type: Boolean, required: false },
30
+ multiple: { type: Boolean, required: false },
31
+ forceSelection: { type: Boolean, required: false },
32
+ selectedValues: { type: Array, required: false },
30
33
  searchText: { type: String, required: true },
31
34
  displayValue: { type: Function, required: true },
32
35
  items: { type: null, required: true },
@@ -53,10 +56,29 @@ const searchInput = ref();
53
56
  const { contains } = useFilter({ sensitivity: "base" });
54
57
  const primitives = computed(() => props.listbox ? { input: ListboxFilter, viewport: ListboxContent, group: ListboxGroup, label: ListboxGroupLabel, item: ListboxItem, indicator: ListboxItemIndicator, virtualizer: ListboxVirtualizer } : { input: ComboboxInput, viewport: ComboboxViewport, group: ComboboxGroup, label: ComboboxLabel, item: ComboboxItem, indicator: ComboboxItemIndicator, virtualizer: ComboboxVirtualizer });
55
58
  function matches(option) {
56
- return !props.listbox || !props.searchable || contains(option.label, props.searchText);
59
+ return !(props.listbox || props.creatable && props.multiple) || !props.searchable || contains(option.label, props.searchText);
57
60
  }
58
61
  const visibleOptions = computed(() => props.flatOptions.filter(matches));
59
- const virtualOptions = computed(() => props.listbox ? visibleOptions.value : props.virtualizedOptions);
62
+ const createText = computed(() => {
63
+ if (!props.creatable || !props.multiple || props.forceSelection)
64
+ return "";
65
+ const text = props.searchText.trim();
66
+ const lower = text.toLowerCase();
67
+ if (!text || props.flatOptions.some((option) => String(option.value).toLowerCase() === lower || option.label.toLowerCase() === lower) || props.selectedValues?.some((value) => typeof value === "string" && value.toLowerCase() === lower)) {
68
+ return "";
69
+ }
70
+ return text;
71
+ });
72
+ const virtualOptions = computed(() => {
73
+ const options = props.listbox ? visibleOptions.value : props.virtualizedOptions;
74
+ return createText.value ? [{ value: createText.value, label: messages.value.createOption(createText.value), disabled: false, raw: void 0 }, ...options] : options;
75
+ });
76
+ function onItemSelect(event, value) {
77
+ if (!props.creatable || !props.multiple || !props.selectedValues?.includes(value))
78
+ return;
79
+ event.preventDefault();
80
+ emit("update:searchText", "");
81
+ }
60
82
  const filterInputProps = computed(() => mergeProps(
61
83
  props.listbox ? { "role": "combobox", "aria-autocomplete": "list", "aria-expanded": true, "aria-controls": listboxId, "aria-label": messages.value.search } : { displayValue: props.displayValue },
62
84
  props.searchInputProps ?? {}
@@ -90,18 +112,23 @@ defineExpose({ focusSearch });
90
112
  />
91
113
  </div>
92
114
 
93
- <div v-if="flatOptions.length === 0" v-bind="emptyProps">
115
+ <div v-if="flatOptions.length === 0 && !createText" v-bind="emptyProps">
94
116
  <slot name="empty">
95
117
  {{ messages.noOptions }}
96
118
  </slot>
97
119
  </div>
98
120
  <component :is="primitives.viewport" v-else :id="listbox ? listboxId : void 0" v-bind="viewportProps">
99
- <div v-if="listbox && visibleOptions.length === 0" v-bind="emptyProps">
121
+ <div v-if="listbox && visibleOptions.length === 0 && !createText" v-bind="emptyProps">
100
122
  <slot name="empty-filter">
101
123
  {{ messages.noResultsFound }}
102
124
  </slot>
103
125
  </div>
104
- <ComboboxEmpty v-else-if="!listbox" v-bind="emptyProps">
126
+ <div v-else-if="creatable && multiple && visibleOptions.length === 0 && !createText" v-bind="emptyProps">
127
+ <slot name="empty-filter">
128
+ {{ messages.noResultsFound }}
129
+ </slot>
130
+ </div>
131
+ <ComboboxEmpty v-else-if="!listbox && !(creatable && multiple)" v-bind="emptyProps">
105
132
  <slot name="empty-filter">
106
133
  {{ messages.noResultsFound }}
107
134
  </slot>
@@ -121,20 +148,30 @@ defineExpose({ focusSearch });
121
148
  :key="String(virtualItem.key)"
122
149
  :value="option.value"
123
150
  :disabled="option.disabled"
151
+ :data-selaras-create-option="!option.raw || void 0"
124
152
  :style="{ position: 'absolute', top: 0, left: 0, right: 0, transform: `translateY(${virtualItem.start}px)`, height: `${virtualItem.size}px` }"
125
153
  v-bind="itemProps"
154
+ @select="onItemSelect($event, option.value)"
126
155
  >
127
- <slot name="item" :item="option.raw">
156
+ <template v-if="!option.raw">
128
157
  {{ option.label }}
129
- </slot>
130
- <component :is="primitives.indicator" v-bind="itemIndicatorProps">
131
- <Icon :name="icons.check" class="size-4" />
132
- </component>
158
+ </template>
159
+ <template v-else>
160
+ <slot name="item" :item="option.raw">
161
+ {{ option.label }}
162
+ </slot>
163
+ <component :is="primitives.indicator" v-bind="itemIndicatorProps">
164
+ <Icon :name="icons.check" class="size-4" />
165
+ </component>
166
+ </template>
133
167
  </component>
134
168
  </component>
135
169
  </ClientOnly>
136
170
 
137
171
  <template v-else>
172
+ <component :is="primitives.item" v-if="createText" :value="createText" data-selaras-create-option v-bind="itemProps">
173
+ {{ messages.createOption(createText) }}
174
+ </component>
138
175
  <template v-for="(entry, index) in items" :key="index">
139
176
  <component :is="primitives.group" v-if="isOptionGroup(entry) && groupOptions(entry).some(matches)">
140
177
  <component :is="primitives.label" v-bind="groupProps">
@@ -149,6 +186,7 @@ defineExpose({ focusSearch });
149
186
  :value="option.value"
150
187
  :disabled="option.disabled"
151
188
  v-bind="itemProps"
189
+ @select="onItemSelect($event, option.value)"
152
190
  >
153
191
  <slot name="item" :item="option.raw">
154
192
  {{ option.label }}
@@ -164,6 +202,7 @@ defineExpose({ focusSearch });
164
202
  :value="toOption(entry).value"
165
203
  :disabled="toOption(entry).disabled"
166
204
  v-bind="itemProps"
205
+ @select="onItemSelect($event, toOption(entry).value)"
167
206
  >
168
207
  <slot name="item" :item="entry">
169
208
  {{ toOption(entry).label }}
@@ -3,6 +3,9 @@ export interface ComboboxSelectBodyProps {
3
3
  listbox?: boolean;
4
4
  searchable?: boolean;
5
5
  creatable?: boolean;
6
+ multiple?: boolean;
7
+ forceSelection?: boolean;
8
+ selectedValues?: (string | number)[];
6
9
  searchText: string;
7
10
  displayValue: (value: unknown) => string;
8
11
  items: SelectItems;
@@ -30,15 +33,15 @@ export interface ComboboxSelectBodyEmits {
30
33
  'update:searchText': [value: string];
31
34
  }
32
35
  declare function focusSearch(): boolean;
33
- declare var __VLS_1: {}, __VLS_3: {}, __VLS_21: {}, __VLS_29: {}, __VLS_37: {}, __VLS_57: {
36
+ declare var __VLS_1: {}, __VLS_3: {}, __VLS_21: {}, __VLS_29: {}, __VLS_31: {}, __VLS_39: {}, __VLS_61: {
34
37
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
35
- }, __VLS_82: {
38
+ }, __VLS_92: {
36
39
  group: import("./combobox-select.js").SelectOptionGroup<import("../utils/select-contracts.js").SelectValue>;
37
- }, __VLS_90: {
40
+ }, __VLS_102: {
38
41
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
39
- }, __VLS_109: {
42
+ }, __VLS_123: {
40
43
  item: SelectOption<import("../utils/select-contracts.js").SelectValue>;
41
- }, __VLS_122: {};
44
+ }, __VLS_136: {};
42
45
  type __VLS_Slots = {} & {
43
46
  header?: (props: typeof __VLS_1) => any;
44
47
  } & {
@@ -48,17 +51,19 @@ type __VLS_Slots = {} & {
48
51
  } & {
49
52
  'empty-filter'?: (props: typeof __VLS_29) => any;
50
53
  } & {
51
- 'empty-filter'?: (props: typeof __VLS_37) => any;
54
+ 'empty-filter'?: (props: typeof __VLS_31) => any;
52
55
  } & {
53
- item?: (props: typeof __VLS_57) => any;
56
+ 'empty-filter'?: (props: typeof __VLS_39) => any;
54
57
  } & {
55
- group?: (props: typeof __VLS_82) => any;
58
+ item?: (props: typeof __VLS_61) => any;
56
59
  } & {
57
- item?: (props: typeof __VLS_90) => any;
60
+ group?: (props: typeof __VLS_92) => any;
58
61
  } & {
59
- item?: (props: typeof __VLS_109) => any;
62
+ item?: (props: typeof __VLS_102) => any;
60
63
  } & {
61
- footer?: (props: typeof __VLS_122) => any;
64
+ item?: (props: typeof __VLS_123) => any;
65
+ } & {
66
+ footer?: (props: typeof __VLS_136) => any;
62
67
  };
63
68
  declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBodyProps, {
64
69
  focusSearch: typeof focusSearch;
@@ -120,7 +120,7 @@ const cellTriggerUi = {
120
120
  <DatePickerGrid v-for="month in grid" :key="month.value.toString()" v-bind="gridProps">
121
121
  <DatePickerGridHead v-bind="gridHeadProps">
122
122
  <DatePickerGridRow>
123
- <DatePickerHeadCell v-for="day in weekDays" :key="day" v-bind="headCellProps">
123
+ <DatePickerHeadCell v-for="(day, index) in weekDays" :key="index" v-bind="headCellProps">
124
124
  {{ day }}
125
125
  </DatePickerHeadCell>
126
126
  </DatePickerGridRow>
@@ -51,7 +51,7 @@ const rangeCellTriggerUi = {
51
51
  <DateRangePickerGrid v-for="month in grid" :key="month.value.toString()" v-bind="gridProps">
52
52
  <DateRangePickerGridHead v-bind="gridHeadProps">
53
53
  <DateRangePickerGridRow>
54
- <DateRangePickerHeadCell v-for="day in weekDays" :key="day" v-bind="headCellProps">
54
+ <DateRangePickerHeadCell v-for="(day, index) in weekDays" :key="index" v-bind="headCellProps">
55
55
  {{ day }}
56
56
  </DateRangePickerHeadCell>
57
57
  </DateRangePickerGridRow>
@@ -0,0 +1,6 @@
1
+ type __VLS_Props = {
2
+ shortcut?: string;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const _default: typeof __VLS_export;
6
+ export default _default;
@@ -0,0 +1,13 @@
1
+ <script setup>
2
+ import Kbd from "../components/Kbd.vue";
3
+ import { applyClassPrefix } from "../utils/ui";
4
+ defineProps({
5
+ shortcut: { type: String, required: false }
6
+ });
7
+ </script>
8
+
9
+ <template>
10
+ <span v-if="shortcut" :class="applyClassPrefix('ms-auto inline-flex items-center gap-1')" aria-hidden="true">
11
+ <Kbd v-for="key in shortcut.split('+')" :key="key" :value="key.trim()" size="sm" />
12
+ </span>
13
+ </template>
@@ -0,0 +1,6 @@
1
+ type __VLS_Props = {
2
+ shortcut?: string;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const _default: typeof __VLS_export;
6
+ export default _default;