@sewadah/selaras 0.2.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 (176) hide show
  1. package/README.md +36 -7
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +31 -15
  4. package/dist/runtime/components/Autocomplete.vue +3 -1
  5. package/dist/runtime/components/Badge.d.vue.ts +1 -1
  6. package/dist/runtime/components/Badge.vue.d.ts +1 -1
  7. package/dist/runtime/components/Calendar.d.vue.ts +69 -0
  8. package/dist/runtime/components/Calendar.vue +163 -0
  9. package/dist/runtime/components/Calendar.vue.d.ts +69 -0
  10. package/dist/runtime/components/Callout.vue +12 -1
  11. package/dist/runtime/components/CodeGroup.vue +13 -3
  12. package/dist/runtime/components/CodeTree.d.vue.ts +2 -0
  13. package/dist/runtime/components/CodeTree.vue +52 -1
  14. package/dist/runtime/components/CodeTree.vue.d.ts +2 -0
  15. package/dist/runtime/components/ColorPicker.d.vue.ts +8 -0
  16. package/dist/runtime/components/ColorPicker.vue +66 -45
  17. package/dist/runtime/components/ColorPicker.vue.d.ts +8 -0
  18. package/dist/runtime/components/Column.js +1 -1
  19. package/dist/runtime/components/CommandPalette.d.vue.ts +3 -1
  20. package/dist/runtime/components/CommandPalette.vue +5 -0
  21. package/dist/runtime/components/CommandPalette.vue.d.ts +3 -1
  22. package/dist/runtime/components/ContentNavigation.d.vue.ts +8 -1
  23. package/dist/runtime/components/ContentNavigation.vue +9 -2
  24. package/dist/runtime/components/ContentNavigation.vue.d.ts +8 -1
  25. package/dist/runtime/components/ContentToc.vue +14 -7
  26. package/dist/runtime/components/ContextMenu.d.vue.ts +8 -1
  27. package/dist/runtime/components/ContextMenu.vue +16 -4
  28. package/dist/runtime/components/ContextMenu.vue.d.ts +8 -1
  29. package/dist/runtime/components/DatePicker.d.vue.ts +10 -3
  30. package/dist/runtime/components/DatePicker.vue +24 -16
  31. package/dist/runtime/components/DatePicker.vue.d.ts +10 -3
  32. package/dist/runtime/components/Dropdown.d.vue.ts +33 -6
  33. package/dist/runtime/components/Dropdown.vue +143 -11
  34. package/dist/runtime/components/Dropdown.vue.d.ts +33 -6
  35. package/dist/runtime/components/FileTree.d.vue.ts +2 -0
  36. package/dist/runtime/components/FileTree.vue +8 -4
  37. package/dist/runtime/components/FileTree.vue.d.ts +2 -0
  38. package/dist/runtime/components/FileUpload.vue +1 -1
  39. package/dist/runtime/components/FormField.d.vue.ts +4 -0
  40. package/dist/runtime/components/FormField.vue +19 -4
  41. package/dist/runtime/components/FormField.vue.d.ts +4 -0
  42. package/dist/runtime/components/Input.vue +3 -2
  43. package/dist/runtime/components/Kbd.vue +12 -2
  44. package/dist/runtime/components/NavigationMenu.d.vue.ts +10 -10
  45. package/dist/runtime/components/NavigationMenu.vue +13 -7
  46. package/dist/runtime/components/NavigationMenu.vue.d.ts +10 -10
  47. package/dist/runtime/components/NavigationMenuAccordionItem.vue +5 -1
  48. package/dist/runtime/components/NavigationMenuFlyoutList.vue +7 -4
  49. package/dist/runtime/components/PinInput.vue +1 -1
  50. package/dist/runtime/components/Popover.d.vue.ts +8 -2
  51. package/dist/runtime/components/Popover.vue +9 -4
  52. package/dist/runtime/components/Popover.vue.d.ts +8 -2
  53. package/dist/runtime/components/ProsePre.d.vue.ts +6 -2
  54. package/dist/runtime/components/ProsePre.vue +11 -4
  55. package/dist/runtime/components/ProsePre.vue.d.ts +6 -2
  56. package/dist/runtime/components/Select.vue +3 -1
  57. package/dist/runtime/components/SplitterPanel.d.vue.ts +2 -2
  58. package/dist/runtime/components/SplitterPanel.vue.d.ts +2 -2
  59. package/dist/runtime/components/Table.vue +95 -26
  60. package/dist/runtime/components/Tabs.d.vue.ts +44 -4
  61. package/dist/runtime/components/Tabs.vue +171 -18
  62. package/dist/runtime/components/Tabs.vue.d.ts +44 -4
  63. package/dist/runtime/components/Textarea.vue +3 -2
  64. package/dist/runtime/components/Timeline.d.vue.ts +19 -0
  65. package/dist/runtime/components/Timeline.vue +99 -0
  66. package/dist/runtime/components/Timeline.vue.d.ts +19 -0
  67. package/dist/runtime/components/Toast.d.vue.ts +10 -1
  68. package/dist/runtime/components/Toast.vue +94 -8
  69. package/dist/runtime/components/Toast.vue.d.ts +10 -1
  70. package/dist/runtime/components/Tooltip.d.vue.ts +8 -2
  71. package/dist/runtime/components/Tooltip.vue +9 -4
  72. package/dist/runtime/components/Tooltip.vue.d.ts +8 -2
  73. package/dist/runtime/composables/use-form-field.d.ts +1 -0
  74. package/dist/runtime/composables/use-table.d.ts +3 -0
  75. package/dist/runtime/composables/use-table.js +10 -4
  76. package/dist/runtime/composables/use-toast.js +5 -1
  77. package/dist/runtime/default-colors.css +1 -1
  78. package/dist/runtime/directives/hotkey.d.ts +13 -0
  79. package/dist/runtime/directives/hotkey.js +107 -0
  80. package/dist/runtime/directives/index.d.ts +2 -0
  81. package/dist/runtime/directives/index.js +2 -0
  82. package/dist/runtime/directives/mask.d.ts +30 -0
  83. package/dist/runtime/directives/mask.js +165 -0
  84. package/dist/runtime/internal/ComboboxSelectBase.d.vue.ts +29 -25
  85. package/dist/runtime/internal/ComboboxSelectBase.vue +104 -20
  86. package/dist/runtime/internal/ComboboxSelectBase.vue.d.ts +29 -25
  87. package/dist/runtime/internal/ComboboxSelectBody.d.vue.ts +16 -11
  88. package/dist/runtime/internal/ComboboxSelectBody.vue +49 -10
  89. package/dist/runtime/internal/ComboboxSelectBody.vue.d.ts +16 -11
  90. package/dist/runtime/internal/DatePickerCalendarBody.vue +1 -1
  91. package/dist/runtime/internal/DatePickerRangeCalendarBody.vue +1 -1
  92. package/dist/runtime/internal/ShortcutHint.d.vue.ts +6 -0
  93. package/dist/runtime/internal/ShortcutHint.vue +13 -0
  94. package/dist/runtime/internal/ShortcutHint.vue.d.ts +6 -0
  95. package/dist/runtime/internal/ToastItemRenderer.d.vue.ts +9 -1
  96. package/dist/runtime/internal/ToastItemRenderer.vue +55 -8
  97. package/dist/runtime/internal/ToastItemRenderer.vue.d.ts +9 -1
  98. package/dist/runtime/internal/combobox-select.js +2 -1
  99. package/dist/runtime/internal/programmatic-services.d.ts +2 -0
  100. package/dist/runtime/internal/programmatic-services.js +8 -0
  101. package/dist/runtime/internal/toast-stack.d.ts +36 -0
  102. package/dist/runtime/internal/toast-stack.js +36 -0
  103. package/dist/runtime/structural.css +1 -1
  104. package/dist/runtime/table.d.ts +1 -1
  105. package/dist/runtime/theme/alert-dialog.js +1 -1
  106. package/dist/runtime/theme/avatar.js +7 -7
  107. package/dist/runtime/theme/badge.js +2 -2
  108. package/dist/runtime/theme/calendar.d.ts +86 -0
  109. package/dist/runtime/theme/calendar.js +19 -0
  110. package/dist/runtime/theme/callout.d.ts +0 -12
  111. package/dist/runtime/theme/callout.js +8 -8
  112. package/dist/runtime/theme/checkbox.js +1 -1
  113. package/dist/runtime/theme/code-tree.js +1 -1
  114. package/dist/runtime/theme/command-palette.js +1 -1
  115. package/dist/runtime/theme/content-navigation.d.ts +3 -0
  116. package/dist/runtime/theme/content-navigation.js +1 -0
  117. package/dist/runtime/theme/date-picker.js +1 -1
  118. package/dist/runtime/theme/drawer.js +1 -1
  119. package/dist/runtime/theme/dropdown.d.ts +21 -0
  120. package/dist/runtime/theme/dropdown.js +15 -2
  121. package/dist/runtime/theme/file-tree.js +1 -1
  122. package/dist/runtime/theme/file-upload.js +2 -2
  123. package/dist/runtime/theme/form-field.d.ts +18 -0
  124. package/dist/runtime/theme/form-field.js +6 -1
  125. package/dist/runtime/theme/icon.js +1 -1
  126. package/dist/runtime/theme/input-number.js +1 -1
  127. package/dist/runtime/theme/input.js +1 -1
  128. package/dist/runtime/theme/modal.js +1 -1
  129. package/dist/runtime/theme/pin-input.js +1 -1
  130. package/dist/runtime/theme/progress.js +2 -2
  131. package/dist/runtime/theme/prose.d.ts +6 -0
  132. package/dist/runtime/theme/prose.js +2 -0
  133. package/dist/runtime/theme/radio-group.js +4 -4
  134. package/dist/runtime/theme/rating.js +1 -1
  135. package/dist/runtime/theme/select.js +2 -2
  136. package/dist/runtime/theme/separator.js +1 -1
  137. package/dist/runtime/theme/slideover.js +1 -1
  138. package/dist/runtime/theme/slider.js +2 -2
  139. package/dist/runtime/theme/splitter-resize-handle.js +1 -1
  140. package/dist/runtime/theme/switch.js +1 -1
  141. package/dist/runtime/theme/table.d.ts +27 -0
  142. package/dist/runtime/theme/table.js +11 -2
  143. package/dist/runtime/theme/tabs.d.ts +87 -3
  144. package/dist/runtime/theme/tabs.js +46 -22
  145. package/dist/runtime/theme/textarea.js +1 -1
  146. package/dist/runtime/theme/timeline.d.ts +251 -0
  147. package/dist/runtime/theme/timeline.js +84 -0
  148. package/dist/runtime/theme/toast.d.ts +92 -0
  149. package/dist/runtime/theme/toast.js +29 -7
  150. package/dist/runtime/theme-config.d.ts +12 -1
  151. package/dist/runtime/types/index.d.ts +6 -1
  152. package/dist/runtime/utils/arrow.d.ts +21 -0
  153. package/dist/runtime/utils/arrow.js +10 -0
  154. package/dist/runtime/utils/color-registry.d.ts +3 -1
  155. package/dist/runtime/utils/color-registry.js +4 -0
  156. package/dist/runtime/utils/file-icons.d.ts +8 -0
  157. package/dist/runtime/utils/file-icons.js +133 -0
  158. package/dist/runtime/utils/functional-tokens.d.ts +1 -1
  159. package/dist/runtime/utils/functional-tokens.js +1 -1
  160. package/dist/runtime/utils/icons.d.ts +2 -0
  161. package/dist/runtime/utils/icons.js +2 -0
  162. package/dist/runtime/utils/messages.d.ts +6 -0
  163. package/dist/runtime/utils/messages.js +6 -0
  164. package/dist/runtime/utils/navigation-menu.d.ts +10 -0
  165. package/dist/runtime/utils/navigation-menu.js +3 -0
  166. package/dist/runtime/utils/overlay.d.ts +17 -0
  167. package/dist/runtime/utils/overlay.js +7 -0
  168. package/dist/runtime/utils/seed-colors.d.ts +2 -1
  169. package/dist/runtime/utils/seed-colors.js +24 -16
  170. package/dist/runtime/utils/select-contracts.d.ts +7 -1
  171. package/dist/runtime/utils/table-columns.js +1 -1
  172. package/dist/runtime/utils/table-contracts.d.ts +9 -1
  173. package/dist/runtime/utils/timeline-contracts.d.ts +42 -0
  174. package/dist/runtime/utils/timeline-contracts.js +0 -0
  175. package/dist/tokens/default-colors.tokens.json +100 -100
  176. package/package.json +21 -9
@@ -34,6 +34,8 @@ import DatePickerCalendarBody from "../internal/DatePickerCalendarBody.vue";
34
34
  import DatePickerRangeCalendarBody from "../internal/DatePickerRangeCalendarBody.vue";
35
35
  import DatePickerTimeBody from "../internal/DatePickerTimeBody.vue";
36
36
  import { datePickerTheme } from "../theme/date-picker";
37
+ import { arrowContentProps, arrowElementProps } from "../utils/arrow";
38
+ import { overlayPortalProps } from "../utils/overlay";
37
39
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
38
40
  import { applyClassPrefix, resolveSlot, useComponentTheme, useRootProps, useThemeBindings } from "../utils/ui";
39
41
  import Button from "./Button.vue";
@@ -81,7 +83,9 @@ const props = defineProps({
81
83
  color: { type: null, required: false, default: "neutral" },
82
84
  activeColor: { type: null, required: false, default: "primary" },
83
85
  size: { type: null, required: false },
84
- arrow: { type: Boolean, required: false, default: false },
86
+ arrow: { type: [Boolean, Object], required: false, default: false },
87
+ positioning: { type: Object, required: false },
88
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: void 0 },
85
89
  adaptive: { type: Boolean, required: false },
86
90
  ui: { type: Object, required: false }
87
91
  });
@@ -360,8 +364,9 @@ const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
360
364
  const fieldProps = computed(() => resolveSlot(ui.value.field, props.ui?.field));
361
365
  const segmentProps = computed(() => resolveSlot(ui.value.segment, props.ui?.segment));
362
366
  const separatorProps = computed(() => resolveSlot(ui.value.separator, props.ui?.separator));
363
- const contentProps = computed(() => resolveSlot(ui.value.content, props.ui?.content));
364
- const arrowProps = computed(() => resolveSlot(ui.value.arrow, props.ui?.arrow));
367
+ const contentProps = computed(() => ({ ...resolveSlot(ui.value.content, props.ui?.content), ...arrowContentProps(props.arrow), ...props.positioning }));
368
+ const portalProps = computed(() => overlayPortalProps(props.portal));
369
+ const arrowProps = computed(() => ({ ...resolveSlot(ui.value.arrow, props.ui?.arrow), ...arrowElementProps(props.arrow) }));
365
370
  const headerProps = computed(() => resolveSlot(ui.value.header, props.ui?.header));
366
371
  const headingProps = computed(() => resolveSlot(ui.value.heading, props.ui?.heading));
367
372
  const gridsProps = computed(() => resolveSlot(ui.value.grids, props.ui?.grids));
@@ -492,7 +497,7 @@ const buttonTriggerUi = computed(() => ({
492
497
  @update:model-value="(value) => updateModelValue(value)"
493
498
  >
494
499
  <DateRangePickerAnchor as-child>
495
- <div v-if="triggerMode === 'field'" :data-selaras-color="fieldColor" :aria-invalid="datePickerInvalid || void 0" :aria-describedby="describedBy" v-bind="fieldProps" :aria-labelledby="field?.labelId">
500
+ <div v-if="triggerMode === 'field'" data-selaras-field-segmented :data-selaras-color="fieldColor" :data-selaras-field-filled="hasValue ? '' : void 0" :data-selaras-field-active="open ? '' : void 0" :aria-invalid="datePickerInvalid || void 0" :aria-describedby="describedBy" v-bind="fieldProps" :aria-labelledby="field?.labelId">
496
501
  <DateRangePickerField v-slot="{ segments }" :aria-labelledby="field?.labelId" :aria-describedby="describedBy" :aria-invalid="datePickerInvalid || void 0">
497
502
  <template v-for="segment in segments.start" :key="`start-${segment.part}`">
498
503
  <DateRangePickerInput as="span" type="start" :part="segment.part" v-bind="segmentProps">
@@ -533,9 +538,10 @@ const buttonTriggerUi = computed(() => ({
533
538
  </div>
534
539
  </div>
535
540
 
536
- <div v-else :class="applyClassPrefix('relative inline-block w-full')">
541
+ <div v-else :class="applyClassPrefix('relative inline-block w-full')" :data-selaras-field-filled="hasValue ? '' : void 0" :data-selaras-field-active="open ? '' : void 0">
537
542
  <DateRangePickerTrigger as-child>
538
543
  <Button
544
+ :id="datePickerId"
539
545
  variant="text"
540
546
  :color="color"
541
547
  :size="effectiveSize"
@@ -544,7 +550,7 @@ const buttonTriggerUi = computed(() => ({
544
550
  :aria-labelledby="field?.labelId"
545
551
  :ui="buttonTriggerUi"
546
552
  >
547
- {{ hasValue ? formattedValue : messages.pickDate }}
553
+ {{ hasValue ? formattedValue : field?.floatingLabel.value ? "" : messages.pickDate }}
548
554
  <template v-if="!(clearable && hasValue)" #trailing-icon="{ class: iconClass }">
549
555
  <slot name="trigger-icon">
550
556
  <Icon :name="icons.calendar" :class="iconClass" />
@@ -570,7 +576,7 @@ const buttonTriggerUi = computed(() => ({
570
576
  </div>
571
577
  </DateRangePickerAnchor>
572
578
 
573
- <DateRangePickerContent v-if="!showMobileModal" :side-offset="6" align="start" :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" v-bind="contentProps">
579
+ <DateRangePickerContent v-if="!showMobileModal" :side-offset="6" align="start" :portal="portalProps" :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" v-bind="contentProps">
574
580
  <DatePickerRangeCalendarBody v-bind="rangeBodyProps">
575
581
  <template #day="scope">
576
582
  <slot name="day" v-bind="scope" />
@@ -610,7 +616,7 @@ const buttonTriggerUi = computed(() => ({
610
616
 
611
617
  <PopoverRoot v-else-if="timeOnly" :open="open" :data-selaras-color="fieldColor" @update:open="onUpdateOpen">
612
618
  <PopoverAnchor as-child>
613
- <div v-if="triggerMode === 'field'" :data-selaras-color="fieldColor" :aria-invalid="datePickerInvalid || void 0" :aria-describedby="describedBy" v-bind="fieldProps" :aria-labelledby="field?.labelId">
619
+ <div v-if="triggerMode === 'field'" data-selaras-field-segmented :data-selaras-color="fieldColor" :data-selaras-field-filled="hasValue ? '' : void 0" :data-selaras-field-active="open ? '' : void 0" :aria-invalid="datePickerInvalid || void 0" :aria-describedby="describedBy" v-bind="fieldProps" :aria-labelledby="field?.labelId">
614
620
  <TimeFieldRoot
615
621
  :id="datePickerId"
616
622
  v-slot="{ segments }"
@@ -660,9 +666,10 @@ const buttonTriggerUi = computed(() => ({
660
666
  </div>
661
667
  </div>
662
668
 
663
- <div v-else :class="applyClassPrefix('relative inline-block w-full')">
669
+ <div v-else :class="applyClassPrefix('relative inline-block w-full')" :data-selaras-field-filled="hasValue ? '' : void 0" :data-selaras-field-active="open ? '' : void 0">
664
670
  <PopoverTrigger as-child>
665
671
  <Button
672
+ :id="datePickerId"
666
673
  variant="text"
667
674
  :color="color"
668
675
  :size="effectiveSize"
@@ -671,7 +678,7 @@ const buttonTriggerUi = computed(() => ({
671
678
  :aria-labelledby="field?.labelId"
672
679
  :ui="buttonTriggerUi"
673
680
  >
674
- {{ hasValue ? formattedTimeValue : messages.pickTime }}
681
+ {{ hasValue ? formattedTimeValue : field?.floatingLabel.value ? "" : messages.pickTime }}
675
682
  <template v-if="!(clearable && hasValue)" #trailing-icon="{ class: iconClass }">
676
683
  <slot name="trigger-icon">
677
684
  <Icon :name="icons.clock" :class="iconClass" />
@@ -697,7 +704,7 @@ const buttonTriggerUi = computed(() => ({
697
704
  </div>
698
705
  </PopoverAnchor>
699
706
 
700
- <PopoverPortal v-if="!showMobileModal">
707
+ <PopoverPortal v-if="!showMobileModal" v-bind="portalProps">
701
708
  <PopoverContent :side-offset="6" align="start" :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" v-bind="contentProps">
702
709
  <DatePickerTimeBody v-bind="timeBodyProps">
703
710
  <template #footer>
@@ -753,7 +760,7 @@ const buttonTriggerUi = computed(() => ({
753
760
  @update:model-value="(value) => updateModelValue(normalizeForGranularity(value))"
754
761
  >
755
762
  <DatePickerAnchor as-child>
756
- <div v-if="triggerMode === 'field'" :data-selaras-color="fieldColor" :aria-invalid="datePickerInvalid || void 0" :aria-describedby="describedBy" v-bind="fieldProps" :aria-labelledby="field?.labelId">
763
+ <div v-if="triggerMode === 'field'" data-selaras-field-segmented :data-selaras-color="fieldColor" :data-selaras-field-filled="hasValue ? '' : void 0" :data-selaras-field-active="open ? '' : void 0" :aria-invalid="datePickerInvalid || void 0" :aria-describedby="describedBy" v-bind="fieldProps" :aria-labelledby="field?.labelId">
757
764
  <DatePickerField v-slot="{ segments }" :aria-labelledby="field?.labelId" :aria-describedby="describedBy" :aria-invalid="datePickerInvalid || void 0">
758
765
  <template v-for="(segment, index) in visibleSegments(segments)" :key="`${segment.part}-${index}`">
759
766
  <DatePickerInput as="span" :part="segment.part" v-bind="segmentProps">
@@ -788,9 +795,10 @@ const buttonTriggerUi = computed(() => ({
788
795
  </div>
789
796
  </div>
790
797
 
791
- <div v-else :class="applyClassPrefix('relative inline-block w-full')">
798
+ <div v-else :class="applyClassPrefix('relative inline-block w-full')" :data-selaras-field-filled="hasValue ? '' : void 0" :data-selaras-field-active="open ? '' : void 0">
792
799
  <DatePickerTrigger as-child>
793
800
  <Button
801
+ :id="datePickerId"
794
802
  variant="text"
795
803
  :color="color"
796
804
  :size="effectiveSize"
@@ -799,7 +807,7 @@ const buttonTriggerUi = computed(() => ({
799
807
  :aria-labelledby="field?.labelId"
800
808
  :ui="buttonTriggerUi"
801
809
  >
802
- {{ hasValue ? formattedValue : messages.pickDate }}
810
+ {{ hasValue ? formattedValue : field?.floatingLabel.value ? "" : messages.pickDate }}
803
811
  <template v-if="!(clearable && hasValue)" #trailing-icon="{ class: iconClass }">
804
812
  <slot name="trigger-icon">
805
813
  <Icon :name="icons.calendar" :class="iconClass" />
@@ -832,7 +840,7 @@ const buttonTriggerUi = computed(() => ({
832
840
  center without clipping, so it silently hides instead. Centering the
833
841
  panel on the trigger only when `arrow` is on sidesteps that without
834
842
  changing the default (start-aligned) layout everyone already sees. -->
835
- <DatePickerContent v-if="!showMobileModal" :side-offset="6" :align="arrow ? 'center' : 'start'" :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" v-bind="contentProps">
843
+ <DatePickerContent v-if="!showMobileModal" :side-offset="6" :align="arrow ? 'center' : 'start'" :portal="portalProps" :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" v-bind="contentProps">
836
844
  <DatePickerCalendarBody v-bind="calendarBodyProps">
837
845
  <template #day="scope">
838
846
  <slot name="day" v-bind="scope" />
@@ -862,5 +870,5 @@ const buttonTriggerUi = computed(() => ({
862
870
  </template>
863
871
  </Modal>
864
872
  </DatePickerRoot>
865
- <input ref="formAnchor" type="hidden" :name="formName" :form="form" :value="nativeFormValue">
873
+ <input ref="formAnchor" type="hidden" :name="formName" :form="form" :value="nativeFormValue" :disabled="disabled">
866
874
  </template>
@@ -2,7 +2,9 @@ import type { DateValue } from '@internationalized/date';
2
2
  import type { DateRange } from 'reka-ui';
3
3
  import type { VariantProps } from 'tailwind-variants';
4
4
  import type { DatePickerThemeSlots } from '../theme/date-picker.js';
5
+ import type { RoundedArrowConfig } from '../utils/arrow.js';
5
6
  import type { ColorRole } from '../utils/color-registry.js';
7
+ import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
6
8
  import type { UiProp } from '../utils/ui.js';
7
9
  import { Time } from '@internationalized/date';
8
10
  import { datePickerTheme } from '../theme/date-picker.js';
@@ -73,7 +75,12 @@ export interface DatePickerProps {
73
75
  activeColor?: ColorRole;
74
76
  size?: DatePickerVariants['size'];
75
77
  /** Shows a small pointer triangle connecting the panel to its trigger. */
76
- arrow?: boolean;
78
+ /** Shows the pointer; an object configures its size, rounding, and edge clearance. */
79
+ arrow?: boolean | RoundedArrowConfig;
80
+ /** Positioning of the anchored calendar (the adaptive modal uses its own layout). */
81
+ positioning?: OverlayPositioning;
82
+ /** Teleport target for the anchored calendar, or `false` to render it inline. */
83
+ portal?: OverlayPortal;
77
84
  /** Opts into the calendar's accessible small-screen modal presentation. It is selected when the calendar opens and held until close. */
78
85
  adaptive?: boolean;
79
86
  ui?: UiProp<DatePickerThemeSlots>;
@@ -158,11 +165,11 @@ type __VLS_Slots = {} & {
158
165
  declare const __VLS_base: import("vue").DefineComponent<DatePickerProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
159
166
  "update:modelValue": (value: DateValue | DateRange | Time | undefined) => any;
160
167
  "update:open": (value: boolean) => any;
161
- "update:view": (view: "month" | "year" | "date") => any;
168
+ "update:view": (view: "date" | "month" | "year") => any;
162
169
  }, string, import("vue").PublicProps, Readonly<DatePickerProps> & Readonly<{
163
170
  "onUpdate:modelValue"?: ((value: DateValue | DateRange | Time | undefined) => any) | undefined;
164
171
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
165
- "onUpdate:view"?: ((view: "month" | "year" | "date") => any) | undefined;
172
+ "onUpdate:view"?: ((view: "date" | "month" | "year") => any) | undefined;
166
173
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
167
174
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
168
175
  declare const _default: typeof __VLS_export;
@@ -1,4 +1,6 @@
1
1
  import type { DropdownThemeSlots } from '../theme/dropdown.js';
2
+ import type { RoundedArrowConfig } from '../utils/arrow.js';
3
+ import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
2
4
  import type { UiProp } from '../utils/ui.js';
3
5
  export interface DropdownItem {
4
6
  label: string;
@@ -7,28 +9,53 @@ export interface DropdownItem {
7
9
  /** Styles this item for a delete/remove-style action (danger text, danger-tinted hover) - just this one flag rather than the full color palette, since a menu item realistically only ever needs this one special case. */
8
10
  destructive?: boolean;
9
11
  onSelect?: () => void;
10
- /** Not read by Dropdown's own default item rendering - carried purely so a custom #item slot override can display one. */
12
+ /** Displays a keyboard shortcut hint. */
11
13
  shortcut?: string;
14
+ /** Bind `shortcut` while this menu is open; activation follows the normal menu item click path. */
15
+ hotkey?: boolean;
16
+ /** Nuxt route or URL to navigate to instead of rendering an action item. */
17
+ to?: string;
18
+ /** Link target, for example `_blank`. */
19
+ target?: string;
20
+ rel?: string;
12
21
  }
13
22
  export interface DropdownProps {
14
23
  items: DropdownItem[][];
15
24
  open?: boolean;
16
25
  /** Initial visibility for an uncontrolled dropdown. Supplying `open` makes the parent authoritative. */
17
26
  defaultOpen?: boolean;
18
- /** Shows a small pointer triangle connecting the menu to its trigger. */
19
- arrow?: boolean;
27
+ /** Shows the pointer; an object configures its size, rounding, and edge clearance. */
28
+ arrow?: boolean | RoundedArrowConfig;
29
+ /** Anchored menu placement. */
30
+ positioning?: OverlayPositioning;
31
+ /** Teleport target, or `false` to render inline. Defaults to `body`. */
32
+ portal?: OverlayPortal;
33
+ /** Opens a dialog-style action list on mobile instead of the anchored menu. */
34
+ adaptive?: boolean;
20
35
  ui?: UiProp<DropdownThemeSlots>;
21
36
  }
22
37
  export interface DropdownEmits {
23
38
  'update:open': [value: boolean];
24
39
  }
25
- declare var __VLS_16: {}, __VLS_48: {
40
+ declare var __VLS_17: {}, __VLS_55: {
41
+ item: DropdownItem;
42
+ }, __VLS_67: {
43
+ item: DropdownItem;
44
+ }, __VLS_102: {
45
+ item: DropdownItem;
46
+ }, __VLS_114: {
26
47
  item: DropdownItem;
27
48
  };
28
49
  type __VLS_Slots = {} & {
29
- default?: (props: typeof __VLS_16) => any;
50
+ default?: (props: typeof __VLS_17) => any;
51
+ } & {
52
+ item?: (props: typeof __VLS_55) => any;
53
+ } & {
54
+ item?: (props: typeof __VLS_67) => any;
55
+ } & {
56
+ item?: (props: typeof __VLS_102) => any;
30
57
  } & {
31
- item?: (props: typeof __VLS_48) => any;
58
+ item?: (props: typeof __VLS_114) => any;
32
59
  };
33
60
  declare const __VLS_base: import("vue").DefineComponent<DropdownProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
34
61
  "update:open": (value: boolean) => any;
@@ -1,14 +1,25 @@
1
1
  <script setup>
2
2
  import { DropdownMenuArrow, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuTrigger } from "reka-ui";
3
- import { computed, getCurrentInstance, ref, watch } from "vue";
3
+ import { computed, getCurrentInstance, onMounted, ref, useId, watch, watchPostEffect } from "vue";
4
+ import { NuxtLink } from "#components";
5
+ import { useIsMobile } from "../composables/use-media-query";
6
+ import { useMessages } from "../composables/use-messages";
7
+ import { vHotkey } from "../directives/hotkey";
8
+ import ShortcutHint from "../internal/ShortcutHint.vue";
4
9
  import { dropdownTheme } from "../theme/dropdown";
10
+ import { arrowContentProps, arrowElementProps } from "../utils/arrow";
11
+ import { overlayPortalProps } from "../utils/overlay";
5
12
  import { resolveSlot, useComponentTheme, useThemeBindings } from "../utils/ui";
6
13
  import Icon from "./Icon.vue";
14
+ import Modal from "./Modal.vue";
7
15
  const props = defineProps({
8
16
  items: { type: Array, required: true },
9
17
  open: { type: Boolean, required: false, default: void 0 },
10
18
  defaultOpen: { type: Boolean, required: false },
11
- arrow: { type: Boolean, required: false, default: false },
19
+ arrow: { type: [Boolean, Object], required: false, default: false },
20
+ positioning: { type: Object, required: false },
21
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: void 0 },
22
+ adaptive: { type: Boolean, required: false },
12
23
  ui: { type: Object, required: false }
13
24
  });
14
25
  const emit = defineEmits(["update:open"]);
@@ -25,42 +36,163 @@ function onUpdateOpen(value) {
25
36
  }
26
37
  const theme = useComponentTheme("dropdown", dropdownTheme);
27
38
  const themeBindings = useThemeBindings();
39
+ const messages = useMessages();
40
+ const isMobile = useIsMobile();
41
+ const modalId = `selaras-dropdown-modal-${useId()}`;
42
+ const triggerElement = ref();
28
43
  const ui = computed(() => theme.value());
29
- const contentProps = computed(() => resolveSlot(ui.value.content, props.ui?.content));
30
- const arrowProps = computed(() => resolveSlot(ui.value.arrow, props.ui?.arrow));
44
+ const contentProps = computed(() => ({ ...resolveSlot(ui.value.content, props.ui?.content), ...arrowContentProps(props.arrow), ...props.positioning }));
45
+ const portalProps = computed(() => overlayPortalProps(props.portal));
46
+ const arrowProps = computed(() => ({ ...resolveSlot(ui.value.arrow, props.ui?.arrow), ...arrowElementProps(props.arrow) }));
31
47
  function itemPropsFor(item) {
32
48
  return resolveSlot(theme.value({ destructive: item.destructive }).item, props.ui?.item);
33
49
  }
50
+ function mobileItemPropsFor(item) {
51
+ return resolveSlot(theme.value({ destructive: item.destructive, mobile: true }).item, props.ui?.item);
52
+ }
53
+ const mobileListProps = computed(() => resolveSlot(ui.value.mobileList, props.ui?.mobileList));
54
+ const mobileGroupProps = computed(() => resolveSlot(ui.value.mobileGroup, props.ui?.mobileGroup));
34
55
  function iconPropsFor(item) {
35
56
  return resolveSlot(theme.value({ destructive: item.destructive }).icon, props.ui?.icon);
36
57
  }
58
+ function hotkeyFor(item) {
59
+ return item.hotkey && item.shortcut && !item.disabled ? { keys: item.shortcut, when: () => internalOpen.value } : void 0;
60
+ }
37
61
  const separatorProps = computed(() => resolveSlot(ui.value.separator, props.ui?.separator));
62
+ const mobilePresentation = ref(false);
63
+ const isOpen = computed(() => isControlled ? props.open ?? false : internalOpen.value);
64
+ watch(isOpen, (open) => {
65
+ if (open)
66
+ mobilePresentation.value = !!props.adaptive && isMobile.value;
67
+ });
68
+ onMounted(() => {
69
+ if (isOpen.value)
70
+ mobilePresentation.value = !!props.adaptive && isMobile.value;
71
+ });
72
+ function onUpdateMenuOpen(value) {
73
+ onUpdateOpen(value);
74
+ }
75
+ const desktopOpen = computed(() => isOpen.value && !mobilePresentation.value);
76
+ const mobileOpen = computed(() => isOpen.value && mobilePresentation.value);
77
+ const adaptiveUi = computed(() => ({
78
+ content: {
79
+ class: "rounded-[var(--selaras-resolved-radius-md)]",
80
+ id: modalId
81
+ }
82
+ }));
83
+ watchPostEffect(() => {
84
+ if (!props.adaptive)
85
+ return;
86
+ const element = triggerElement.value instanceof HTMLElement ? triggerElement.value : triggerElement.value?.$el;
87
+ if (!element)
88
+ return;
89
+ const dialog = isOpen.value ? mobilePresentation.value : isMobile.value;
90
+ element.setAttribute("aria-haspopup", dialog ? "dialog" : "menu");
91
+ element.setAttribute("aria-expanded", String(isOpen.value));
92
+ if (dialog && isOpen.value)
93
+ element.setAttribute("aria-controls", modalId);
94
+ else if (element.getAttribute("aria-controls") === modalId)
95
+ element.removeAttribute("aria-controls");
96
+ });
97
+ function selectMobileItem(item) {
98
+ item.onSelect?.();
99
+ onUpdateOpen(false);
100
+ }
101
+ function onModalAfterLeave() {
102
+ if (!isOpen.value)
103
+ mobilePresentation.value = false;
104
+ }
38
105
  </script>
39
106
 
40
107
  <template>
41
- <DropdownMenuRoot :open="internalOpen" @update:open="onUpdateOpen">
42
- <DropdownMenuTrigger as-child>
108
+ <DropdownMenuRoot :open="desktopOpen" @update:open="onUpdateMenuOpen">
109
+ <DropdownMenuTrigger ref="triggerElement" as-child>
43
110
  <slot />
44
111
  </DropdownMenuTrigger>
45
- <DropdownMenuPortal>
112
+ <DropdownMenuPortal v-if="!mobilePresentation" v-bind="portalProps">
46
113
  <DropdownMenuContent :side-offset="6" align="start" :data-selaras-theme="themeBindings['data-selaras-theme']" :data-selaras-mode="themeBindings['data-selaras-mode']" :style="themeBindings.style" v-bind="contentProps">
47
114
  <template v-for="(group, groupIndex) in items" :key="groupIndex">
48
115
  <DropdownMenuSeparator v-if="groupIndex > 0" v-bind="separatorProps" />
49
116
  <DropdownMenuItem
50
117
  v-for="(item, itemIndex) in group"
51
118
  :key="itemIndex"
119
+ v-hotkey="hotkeyFor(item)"
52
120
  :disabled="item.disabled"
121
+ :as-child="Boolean(item.to)"
53
122
  v-bind="itemPropsFor(item)"
54
123
  @select="item.onSelect?.()"
55
124
  >
56
- <Icon v-if="item.icon" :name="item.icon" v-bind="iconPropsFor(item)" />
57
- <slot name="item" :item="item">
58
- {{ item.label }}
59
- </slot>
125
+ <NuxtLink v-if="item.to" :to="item.to" :target="item.target" :rel="item.rel">
126
+ <Icon v-if="item.icon" :name="item.icon" v-bind="iconPropsFor(item)" />
127
+ <slot name="item" :item="item">
128
+ {{ item.label }}
129
+ <ShortcutHint :shortcut="item.shortcut" />
130
+ </slot>
131
+ </NuxtLink>
132
+ <template v-else>
133
+ <Icon v-if="item.icon" :name="item.icon" v-bind="iconPropsFor(item)" />
134
+ <slot name="item" :item="item">
135
+ {{ item.label }}
136
+ <ShortcutHint :shortcut="item.shortcut" />
137
+ </slot>
138
+ </template>
60
139
  </DropdownMenuItem>
61
140
  </template>
62
141
  <DropdownMenuArrow v-if="arrow" v-bind="arrowProps" />
63
142
  </DropdownMenuContent>
64
143
  </DropdownMenuPortal>
65
144
  </DropdownMenuRoot>
145
+
146
+ <Modal
147
+ v-if="mobilePresentation"
148
+ :open="mobileOpen"
149
+ :title="messages.dropdownMenu"
150
+ :description="messages.dropdownMenuDescription"
151
+ :close="false"
152
+ :ui="adaptiveUi"
153
+ @update:open="onUpdateOpen"
154
+ @after-leave="onModalAfterLeave"
155
+ >
156
+ <template #content>
157
+ <div v-bind="mobileListProps">
158
+ <template v-for="(group, groupIndex) in items" :key="groupIndex">
159
+ <div v-if="groupIndex > 0" role="separator" v-bind="separatorProps" />
160
+ <div v-bind="mobileGroupProps">
161
+ <template v-for="(item, itemIndex) in group" :key="itemIndex">
162
+ <!-- A disabled link renders as the disabled button below, which is inert. -->
163
+ <NuxtLink
164
+ v-if="item.to && !item.disabled"
165
+ v-hotkey="hotkeyFor(item)"
166
+ :to="item.to"
167
+ :target="item.target"
168
+ :rel="item.rel"
169
+ v-bind="mobileItemPropsFor(item)"
170
+ @click="selectMobileItem(item)"
171
+ >
172
+ <Icon v-if="item.icon" :name="item.icon" v-bind="iconPropsFor(item)" />
173
+ <slot name="item" :item="item">
174
+ {{ item.label }}
175
+ <ShortcutHint :shortcut="item.shortcut" />
176
+ </slot>
177
+ </NuxtLink>
178
+ <button
179
+ v-else
180
+ v-hotkey="hotkeyFor(item)"
181
+ type="button"
182
+ :disabled="item.disabled"
183
+ v-bind="mobileItemPropsFor(item)"
184
+ @click="selectMobileItem(item)"
185
+ >
186
+ <Icon v-if="item.icon" :name="item.icon" v-bind="iconPropsFor(item)" />
187
+ <slot name="item" :item="item">
188
+ {{ item.label }}
189
+ <ShortcutHint :shortcut="item.shortcut" />
190
+ </slot>
191
+ </button>
192
+ </template>
193
+ </div>
194
+ </template>
195
+ </div>
196
+ </template>
197
+ </Modal>
66
198
  </template>
@@ -1,4 +1,6 @@
1
1
  import type { DropdownThemeSlots } from '../theme/dropdown.js';
2
+ import type { RoundedArrowConfig } from '../utils/arrow.js';
3
+ import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
2
4
  import type { UiProp } from '../utils/ui.js';
3
5
  export interface DropdownItem {
4
6
  label: string;
@@ -7,28 +9,53 @@ export interface DropdownItem {
7
9
  /** Styles this item for a delete/remove-style action (danger text, danger-tinted hover) - just this one flag rather than the full color palette, since a menu item realistically only ever needs this one special case. */
8
10
  destructive?: boolean;
9
11
  onSelect?: () => void;
10
- /** Not read by Dropdown's own default item rendering - carried purely so a custom #item slot override can display one. */
12
+ /** Displays a keyboard shortcut hint. */
11
13
  shortcut?: string;
14
+ /** Bind `shortcut` while this menu is open; activation follows the normal menu item click path. */
15
+ hotkey?: boolean;
16
+ /** Nuxt route or URL to navigate to instead of rendering an action item. */
17
+ to?: string;
18
+ /** Link target, for example `_blank`. */
19
+ target?: string;
20
+ rel?: string;
12
21
  }
13
22
  export interface DropdownProps {
14
23
  items: DropdownItem[][];
15
24
  open?: boolean;
16
25
  /** Initial visibility for an uncontrolled dropdown. Supplying `open` makes the parent authoritative. */
17
26
  defaultOpen?: boolean;
18
- /** Shows a small pointer triangle connecting the menu to its trigger. */
19
- arrow?: boolean;
27
+ /** Shows the pointer; an object configures its size, rounding, and edge clearance. */
28
+ arrow?: boolean | RoundedArrowConfig;
29
+ /** Anchored menu placement. */
30
+ positioning?: OverlayPositioning;
31
+ /** Teleport target, or `false` to render inline. Defaults to `body`. */
32
+ portal?: OverlayPortal;
33
+ /** Opens a dialog-style action list on mobile instead of the anchored menu. */
34
+ adaptive?: boolean;
20
35
  ui?: UiProp<DropdownThemeSlots>;
21
36
  }
22
37
  export interface DropdownEmits {
23
38
  'update:open': [value: boolean];
24
39
  }
25
- declare var __VLS_16: {}, __VLS_48: {
40
+ declare var __VLS_17: {}, __VLS_55: {
41
+ item: DropdownItem;
42
+ }, __VLS_67: {
43
+ item: DropdownItem;
44
+ }, __VLS_102: {
45
+ item: DropdownItem;
46
+ }, __VLS_114: {
26
47
  item: DropdownItem;
27
48
  };
28
49
  type __VLS_Slots = {} & {
29
- default?: (props: typeof __VLS_16) => any;
50
+ default?: (props: typeof __VLS_17) => any;
51
+ } & {
52
+ item?: (props: typeof __VLS_55) => any;
53
+ } & {
54
+ item?: (props: typeof __VLS_67) => any;
55
+ } & {
56
+ item?: (props: typeof __VLS_102) => any;
30
57
  } & {
31
- item?: (props: typeof __VLS_48) => any;
58
+ item?: (props: typeof __VLS_114) => any;
32
59
  };
33
60
  declare const __VLS_base: import("vue").DefineComponent<DropdownProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
34
61
  "update:open": (value: boolean) => any;
@@ -6,6 +6,8 @@ export interface FileTreeNode {
6
6
  /** A node with children renders as a directory, one without as a file - the same convention ContentNavigation's own tree uses. */
7
7
  children?: FileTreeNode[];
8
8
  icon?: string;
9
+ /** Optional language hint for resolving a file-type icon when the filename is not recognized. */
10
+ language?: string;
9
11
  }
10
12
  export interface FileTreeProps {
11
13
  items: FileTreeNode[];
@@ -1,7 +1,8 @@
1
1
  <script setup>
2
- import { computed, ref } from "vue";
2
+ import { computed, ref, toRaw } from "vue";
3
3
  import { useIcons } from "../composables/use-icons";
4
4
  import { fileTreeTheme } from "../theme/file-tree";
5
+ import { resolveFileIcon } from "../utils/file-icons";
5
6
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
6
7
  import { resolveSlot, useComponentTheme, useRootProps } from "../utils/ui";
7
8
  import Icon from "./Icon.vue";
@@ -31,11 +32,14 @@ function toggle(index) {
31
32
  function selectFile(node) {
32
33
  emit("update:selected", node);
33
34
  }
35
+ function isSelected(node) {
36
+ return props.selected != null && toRaw(props.selected) === toRaw(node);
37
+ }
34
38
  function iconFor(node, index) {
35
39
  if (node.icon)
36
40
  return node.icon;
37
41
  if (!node.children)
38
- return icons.value.file;
42
+ return resolveFileIcon(node.language, node.name) ?? icons.value.file;
39
43
  return isExpanded(index) ? icons.value.folderOpen : icons.value.folder;
40
44
  }
41
45
  const theme = useComponentTheme("fileTree", fileTreeTheme);
@@ -46,14 +50,14 @@ const itemProps = computed(() => resolveSlot(ui.value.item, props.ui?.item));
46
50
  const iconProps = computed(() => resolveSlot(ui.value.icon, props.ui?.icon));
47
51
  const labelProps = computed(() => resolveSlot(ui.value.label, props.ui?.label));
48
52
  function rowProps(node) {
49
- return resolveSlot(theme.value({ isNested: props.isNested, selected: props.selected === node, color: effectiveColor.value }).row, props.ui?.row);
53
+ return resolveSlot(theme.value({ isNested: props.isNested, selected: isSelected(node), color: effectiveColor.value }).row, props.ui?.row);
50
54
  }
51
55
  </script>
52
56
 
53
57
  <template>
54
58
  <ul :data-selaras-color="effectiveColor" v-bind="rootProps">
55
59
  <li v-for="(node, index) in items" :key="node.name" v-bind="itemProps">
56
- <button type="button" v-bind="rowProps(node)" @click="node.children ? toggle(index) : selectFile(node)">
60
+ <button type="button" :aria-current="isSelected(node) ? 'true' : void 0" v-bind="rowProps(node)" @click="node.children ? toggle(index) : selectFile(node)">
57
61
  <Icon :name="iconFor(node, index)" v-bind="iconProps" />
58
62
  <span v-bind="labelProps">{{ node.name }}</span>
59
63
  </button>
@@ -6,6 +6,8 @@ export interface FileTreeNode {
6
6
  /** A node with children renders as a directory, one without as a file - the same convention ContentNavigation's own tree uses. */
7
7
  children?: FileTreeNode[];
8
8
  icon?: string;
9
+ /** Optional language hint for resolving a file-type icon when the filename is not recognized. */
10
+ language?: string;
9
11
  }
10
12
  export interface FileTreeProps {
11
13
  items: FileTreeNode[];
@@ -202,7 +202,7 @@ const removeButtonSize = computed(() => ({ sm: "sm", md: "sm", lg: "md" })[effec
202
202
  </script>
203
203
 
204
204
  <template>
205
- <div :data-selaras-color="effectiveColor" v-bind="rootProps">
205
+ <div :data-selaras-color="fileUploadInvalid ? 'danger' : effectiveColor" v-bind="rootProps">
206
206
  <button
207
207
  ref="dropzoneEl"
208
208
  type="button"
@@ -16,6 +16,10 @@ export interface FormFieldProps {
16
16
  size?: FormFieldVariants['size'];
17
17
  /** 'vertical' (default) stacks the label above the control; 'horizontal' places the label beside it, with the control filling the remaining row width. */
18
18
  orientation?: FormFieldVariants['orientation'];
19
+ /** 'floating' overlays the label on an empty control and lifts it on focus, open, or once filled. */
20
+ labelMode?: FormFieldVariants['labelMode'];
21
+ /** Explicit filled state for a custom or third-party control. */
22
+ filled?: boolean;
19
23
  ui?: UiProp<FormFieldThemeSlots>;
20
24
  }
21
25
  declare var __VLS_1: {}, __VLS_3: {