@sewadah/selaras 0.9.0 → 0.10.0

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 (66) hide show
  1. package/README.md +11 -0
  2. package/dist/agent/catalog.json +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/components/Checkbox.d.vue.ts +6 -4
  5. package/dist/runtime/components/Checkbox.vue +30 -22
  6. package/dist/runtime/components/Checkbox.vue.d.ts +6 -4
  7. package/dist/runtime/components/DatePicker.d.vue.ts +27 -27
  8. package/dist/runtime/components/DatePicker.vue +58 -99
  9. package/dist/runtime/components/DatePicker.vue.d.ts +27 -27
  10. package/dist/runtime/components/Editable.d.vue.ts +91 -0
  11. package/dist/runtime/components/Editable.vue +257 -0
  12. package/dist/runtime/components/Editable.vue.d.ts +91 -0
  13. package/dist/runtime/components/Input.vue +21 -22
  14. package/dist/runtime/components/Textarea.vue +20 -23
  15. package/dist/runtime/components/Toolbar.d.vue.ts +25 -0
  16. package/dist/runtime/components/Toolbar.vue +35 -0
  17. package/dist/runtime/components/Toolbar.vue.d.ts +25 -0
  18. package/dist/runtime/components/ToolbarButton.d.vue.ts +20 -0
  19. package/dist/runtime/components/ToolbarButton.vue +46 -0
  20. package/dist/runtime/components/ToolbarButton.vue.d.ts +20 -0
  21. package/dist/runtime/components/ToolbarSeparator.d.vue.ts +10 -0
  22. package/dist/runtime/components/ToolbarSeparator.vue +22 -0
  23. package/dist/runtime/components/ToolbarSeparator.vue.d.ts +10 -0
  24. package/dist/runtime/components/ToolbarToggleGroup.d.vue.ts +37 -0
  25. package/dist/runtime/components/ToolbarToggleGroup.vue +44 -0
  26. package/dist/runtime/components/ToolbarToggleGroup.vue.d.ts +37 -0
  27. package/dist/runtime/internal/ComboboxSelectBase.vue +41 -50
  28. package/dist/runtime/internal/FieldClearButton.d.vue.ts +21 -0
  29. package/dist/runtime/internal/FieldClearButton.vue +30 -0
  30. package/dist/runtime/internal/FieldClearButton.vue.d.ts +21 -0
  31. package/dist/runtime/internal/ListboxOption.d.vue.ts +2 -1
  32. package/dist/runtime/internal/ListboxOption.vue +3 -2
  33. package/dist/runtime/internal/ListboxOption.vue.d.ts +2 -1
  34. package/dist/runtime/theme/date-picker.d.ts +27 -0
  35. package/dist/runtime/theme/date-picker.js +6 -3
  36. package/dist/runtime/theme/editable.d.ts +155 -0
  37. package/dist/runtime/theme/editable.js +25 -0
  38. package/dist/runtime/theme/field-clear-button.d.ts +56 -0
  39. package/dist/runtime/theme/field-clear-button.js +15 -0
  40. package/dist/runtime/theme/input.d.ts +0 -9
  41. package/dist/runtime/theme/input.js +5 -12
  42. package/dist/runtime/theme/listbox.js +1 -1
  43. package/dist/runtime/theme/select.d.ts +9 -0
  44. package/dist/runtime/theme/select.js +11 -10
  45. package/dist/runtime/theme/textarea.d.ts +0 -9
  46. package/dist/runtime/theme/textarea.js +4 -4
  47. package/dist/runtime/theme/toolbar-button.d.ts +65 -0
  48. package/dist/runtime/theme/toolbar-button.js +21 -0
  49. package/dist/runtime/theme/toolbar-separator.d.ts +35 -0
  50. package/dist/runtime/theme/toolbar-separator.js +8 -0
  51. package/dist/runtime/theme/toolbar-toggle-group.d.ts +74 -0
  52. package/dist/runtime/theme/toolbar-toggle-group.js +13 -0
  53. package/dist/runtime/theme/toolbar.d.ts +35 -0
  54. package/dist/runtime/theme/toolbar.js +11 -0
  55. package/dist/runtime/theme-config.d.ts +36 -0
  56. package/dist/runtime/types/index.d.ts +5 -0
  57. package/dist/runtime/utils/field-clear.d.ts +9 -0
  58. package/dist/runtime/utils/field-clear.js +7 -0
  59. package/dist/runtime/utils/icons.d.ts +1 -0
  60. package/dist/runtime/utils/icons.js +1 -0
  61. package/dist/runtime/utils/messages.d.ts +4 -0
  62. package/dist/runtime/utils/messages.js +4 -0
  63. package/dist/runtime/utils/toolbar.d.ts +9 -0
  64. package/dist/runtime/utils/toolbar.js +1 -0
  65. package/package.json +9 -2
  66. package/skills/selaras/SKILL.md +4 -0
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "selaras",
3
3
  "configKey": "selaras",
4
- "version": "0.9.0",
4
+ "version": "0.10.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "unknown"
@@ -14,6 +14,8 @@ export interface CheckboxProps {
14
14
  disabled?: boolean;
15
15
  invalid?: boolean;
16
16
  required?: boolean;
17
+ /** Renders only the visual indicator, without a focus target, form input, or inherited FormField state. Selection is owned by the surrounding control. */
18
+ decorative?: boolean;
17
19
  size?: CheckboxVariants['size'];
18
20
  color?: ColorRole;
19
21
  /** `card` wraps the checkbox and label in a bordered box, highlighted when checked - the same treatment RadioGroup's own `card` variant already applies per item. */
@@ -23,13 +25,13 @@ export interface CheckboxProps {
23
25
  export interface CheckboxEmits {
24
26
  'update:modelValue': [value: boolean | 'indeterminate'];
25
27
  }
26
- declare var __VLS_15: {}, __VLS_17: {}, __VLS_19: {};
28
+ declare var __VLS_20: {}, __VLS_22: {}, __VLS_24: {};
27
29
  type __VLS_Slots = {} & {
28
- default?: (props: typeof __VLS_15) => any;
30
+ default?: (props: typeof __VLS_20) => any;
29
31
  } & {
30
- description?: (props: typeof __VLS_17) => any;
32
+ description?: (props: typeof __VLS_22) => any;
31
33
  } & {
32
- default?: (props: typeof __VLS_19) => any;
34
+ default?: (props: typeof __VLS_24) => any;
33
35
  };
34
36
  declare const __VLS_base: import("vue").DefineComponent<CheckboxProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
37
  "update:modelValue": (value: boolean | "indeterminate") => any;
@@ -1,6 +1,6 @@
1
1
  <script setup>
2
2
  import { CheckboxIndicator, CheckboxRoot } from "reka-ui";
3
- import { computed } from "vue";
3
+ import { computed, mergeProps } from "vue";
4
4
  import { useFormField } from "../composables/use-form-field";
5
5
  import { checkboxTheme } from "../theme/checkbox";
6
6
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
@@ -15,17 +15,19 @@ const props = defineProps({
15
15
  disabled: { type: Boolean, required: false },
16
16
  invalid: { type: Boolean, required: false },
17
17
  required: { type: Boolean, required: false },
18
+ decorative: { type: Boolean, required: false },
18
19
  size: { type: null, required: false },
19
20
  color: { type: null, required: false },
20
21
  variant: { type: null, required: false },
21
22
  ui: { type: Object, required: false }
22
23
  });
23
24
  const emit = defineEmits(["update:modelValue"]);
24
- const field = useFormField();
25
- const checkboxId = computed(() => props.id ?? field?.id);
26
- const checkboxInvalid = computed(() => props.invalid || (field?.invalid.value ?? false));
27
- const effectiveSize = computed(() => props.size ?? field?.size ?? "md");
28
- const describedBy = computed(() => field?.describedBy.value);
25
+ const inheritedField = useFormField();
26
+ const field = computed(() => props.decorative ? void 0 : inheritedField);
27
+ const checkboxId = computed(() => props.id ?? field.value?.id);
28
+ const checkboxInvalid = computed(() => props.invalid || (field.value?.invalid.value ?? false));
29
+ const effectiveSize = computed(() => props.size ?? field.value?.size ?? "md");
30
+ const describedBy = computed(() => field.value?.describedBy.value);
29
31
  const theme = useComponentTheme("checkbox", checkboxTheme);
30
32
  const effectiveColor = computed(() => resolveRegisteredColorRole(props.color ?? "primary", "primary"));
31
33
  const ui = computed(() => theme.value({
@@ -43,28 +45,34 @@ const descriptionProps = computed(() => resolveSlot(ui.value.description, props.
43
45
  const checkIconProps = computed(() => resolveSlot(ui.value.checkIcon, props.ui?.checkIcon));
44
46
  const indeterminateIconProps = computed(() => resolveSlot(ui.value.indeterminateIcon, props.ui?.indeterminateIcon));
45
47
  const glyphState = computed(() => props.modelValue === "indeterminate" ? "indeterminate" : props.modelValue ? "checked" : "unchecked");
48
+ const controlProps = computed(() => props.decorative ? {
49
+ ...boxProps.value,
50
+ "data-state": glyphState.value,
51
+ "data-disabled": props.disabled ? "" : void 0
52
+ } : mergeProps({
53
+ "id": checkboxId.value,
54
+ "modelValue": props.modelValue,
55
+ "name": props.name ?? field.value?.name,
56
+ "disabled": props.disabled,
57
+ "required": props.required,
58
+ "aria-invalid": checkboxInvalid.value || void 0,
59
+ "aria-describedby": describedBy.value
60
+ }, boxProps.value, {
61
+ "onUpdate:modelValue": (value) => emit("update:modelValue", value)
62
+ }));
63
+ const visualIndicatorProps = computed(() => props.decorative ? { "data-state": glyphState.value, "data-disabled": props.disabled ? "" : void 0, ...indicatorProps.value } : { forceMount: true, ...indicatorProps.value });
46
64
  </script>
47
65
 
48
66
  <template>
49
- <label :data-selaras-color="checkboxInvalid ? 'danger' : effectiveColor" v-bind="rootProps">
50
- <CheckboxRoot
51
- :id="checkboxId"
52
- :model-value="modelValue"
53
- :name="name ?? field?.name"
54
- :disabled="disabled"
55
- :required="required"
56
- :aria-invalid="checkboxInvalid || void 0"
57
- :aria-describedby="describedBy"
58
- v-bind="boxProps"
59
- @update:model-value="(value) => emit('update:modelValue', value)"
60
- >
61
- <CheckboxIndicator force-mount v-bind="indicatorProps">
67
+ <component :is="decorative ? 'span' : 'label'" :data-selaras-color="checkboxInvalid ? 'danger' : effectiveColor" v-bind="rootProps" :aria-hidden="decorative ? true : rootProps['aria-hidden']">
68
+ <component :is="decorative ? 'span' : CheckboxRoot" v-bind="controlProps">
69
+ <component :is="decorative ? 'span' : CheckboxIndicator" v-bind="visualIndicatorProps">
62
70
  <svg viewBox="0 0 24 24" :class="applyClassPrefix('size-full')" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
63
71
  <path d="M5 13l4 4L19 7" :data-state="glyphState" v-bind="checkIconProps" />
64
72
  <path d="M5 12h14" :data-state="glyphState" v-bind="indeterminateIconProps" />
65
73
  </svg>
66
- </CheckboxIndicator>
67
- </CheckboxRoot>
74
+ </component>
75
+ </component>
68
76
  <span v-if="description" v-bind="labelGroupProps">
69
77
  <span v-if="label || $slots.default" v-bind="labelProps"><slot>{{ label }}</slot></span>
70
78
  <span v-bind="descriptionProps">
@@ -74,5 +82,5 @@ const glyphState = computed(() => props.modelValue === "indeterminate" ? "indete
74
82
  <span v-else-if="label || $slots.default" v-bind="labelProps">
75
83
  <slot>{{ label }}</slot>
76
84
  </span>
77
- </label>
85
+ </component>
78
86
  </template>
@@ -14,6 +14,8 @@ export interface CheckboxProps {
14
14
  disabled?: boolean;
15
15
  invalid?: boolean;
16
16
  required?: boolean;
17
+ /** Renders only the visual indicator, without a focus target, form input, or inherited FormField state. Selection is owned by the surrounding control. */
18
+ decorative?: boolean;
17
19
  size?: CheckboxVariants['size'];
18
20
  color?: ColorRole;
19
21
  /** `card` wraps the checkbox and label in a bordered box, highlighted when checked - the same treatment RadioGroup's own `card` variant already applies per item. */
@@ -23,13 +25,13 @@ export interface CheckboxProps {
23
25
  export interface CheckboxEmits {
24
26
  'update:modelValue': [value: boolean | 'indeterminate'];
25
27
  }
26
- declare var __VLS_15: {}, __VLS_17: {}, __VLS_19: {};
28
+ declare var __VLS_20: {}, __VLS_22: {}, __VLS_24: {};
27
29
  type __VLS_Slots = {} & {
28
- default?: (props: typeof __VLS_15) => any;
30
+ default?: (props: typeof __VLS_20) => any;
29
31
  } & {
30
- description?: (props: typeof __VLS_17) => any;
32
+ description?: (props: typeof __VLS_22) => any;
31
33
  } & {
32
- default?: (props: typeof __VLS_19) => any;
34
+ default?: (props: typeof __VLS_24) => any;
33
35
  };
34
36
  declare const __VLS_base: import("vue").DefineComponent<CheckboxProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
37
  "update:modelValue": (value: boolean | "indeterminate") => any;
@@ -69,7 +69,7 @@ export interface DatePickerProps {
69
69
  isYearDisabled?: (year: number) => boolean;
70
70
  invalid?: boolean;
71
71
  clearable?: boolean;
72
- /** Chrome color - the clear/trigger buttons and prev/next month nav. Only the color changes; each button keeps its own deliberately-different variant (icon-only buttons use `text`, nav arrows use `ghost`). */
72
+ /** Chrome color for trigger buttons and calendar navigation. Clear controls remain neutral. */
73
73
  color?: ColorRole;
74
74
  /** The color of whatever's currently "active" - the selected day/month/year cell and the Done button. */
75
75
  activeColor?: ColorRole;
@@ -90,7 +90,7 @@ export interface DatePickerEmits {
90
90
  'update:modelValue': [value: DateValue | DateRange | Time | undefined];
91
91
  'update:view': [view: 'date' | 'month' | 'year'];
92
92
  }
93
- declare var __VLS_43: {}, __VLS_63: {}, __VLS_83: {}, __VLS_99: {}, __VLS_119: {
93
+ declare var __VLS_43: {}, __VLS_58: {}, __VLS_80: {}, __VLS_96: {}, __VLS_111: {
94
94
  date: DateValue;
95
95
  dayValue: string;
96
96
  selected: boolean;
@@ -98,7 +98,7 @@ declare var __VLS_43: {}, __VLS_63: {}, __VLS_83: {}, __VLS_99: {}, __VLS_119: {
98
98
  selectionStart: boolean;
99
99
  selectionEnd: boolean;
100
100
  highlighted: boolean;
101
- }, __VLS_122: {}, __VLS_145: {
101
+ }, __VLS_114: {}, __VLS_137: {
102
102
  date: DateValue;
103
103
  dayValue: string;
104
104
  selected: boolean;
@@ -106,61 +106,61 @@ declare var __VLS_43: {}, __VLS_63: {}, __VLS_83: {}, __VLS_99: {}, __VLS_119: {
106
106
  selectionStart: boolean;
107
107
  selectionEnd: boolean;
108
108
  highlighted: boolean;
109
- }, __VLS_148: {}, __VLS_187: {}, __VLS_207: {}, __VLS_227: {}, __VLS_243: {}, __VLS_269: {}, __VLS_292: {}, __VLS_330: {}, __VLS_350: {}, __VLS_370: {}, __VLS_386: {}, __VLS_406: {
109
+ }, __VLS_140: {}, __VLS_179: {}, __VLS_194: {}, __VLS_216: {}, __VLS_232: {}, __VLS_253: {}, __VLS_276: {}, __VLS_314: {}, __VLS_329: {}, __VLS_351: {}, __VLS_367: {}, __VLS_382: {
110
110
  date: DateValue;
111
111
  dayValue: string;
112
112
  selected: boolean;
113
113
  disabled: boolean;
114
- }, __VLS_409: {}, __VLS_432: {
114
+ }, __VLS_385: {}, __VLS_408: {
115
115
  date: DateValue;
116
116
  dayValue: string;
117
117
  selected: boolean;
118
118
  disabled: boolean;
119
- }, __VLS_435: {};
119
+ }, __VLS_411: {};
120
120
  type __VLS_Slots = {} & {
121
121
  'clear-icon'?: (props: typeof __VLS_43) => any;
122
122
  } & {
123
- 'trigger-icon'?: (props: typeof __VLS_63) => any;
123
+ 'trigger-icon'?: (props: typeof __VLS_58) => any;
124
124
  } & {
125
- 'trigger-icon'?: (props: typeof __VLS_83) => any;
125
+ 'trigger-icon'?: (props: typeof __VLS_80) => any;
126
126
  } & {
127
- 'clear-icon'?: (props: typeof __VLS_99) => any;
127
+ 'clear-icon'?: (props: typeof __VLS_96) => any;
128
128
  } & {
129
- day?: (props: typeof __VLS_119) => any;
129
+ day?: (props: typeof __VLS_111) => any;
130
130
  } & {
131
- footer?: (props: typeof __VLS_122) => any;
131
+ footer?: (props: typeof __VLS_114) => any;
132
132
  } & {
133
- day?: (props: typeof __VLS_145) => any;
133
+ day?: (props: typeof __VLS_137) => any;
134
134
  } & {
135
- footer?: (props: typeof __VLS_148) => any;
135
+ footer?: (props: typeof __VLS_140) => any;
136
136
  } & {
137
- 'clear-icon'?: (props: typeof __VLS_187) => any;
137
+ 'clear-icon'?: (props: typeof __VLS_179) => any;
138
138
  } & {
139
- 'trigger-icon'?: (props: typeof __VLS_207) => any;
139
+ 'trigger-icon'?: (props: typeof __VLS_194) => any;
140
140
  } & {
141
- 'trigger-icon'?: (props: typeof __VLS_227) => any;
141
+ 'trigger-icon'?: (props: typeof __VLS_216) => any;
142
142
  } & {
143
- 'clear-icon'?: (props: typeof __VLS_243) => any;
143
+ 'clear-icon'?: (props: typeof __VLS_232) => any;
144
144
  } & {
145
- footer?: (props: typeof __VLS_269) => any;
145
+ footer?: (props: typeof __VLS_253) => any;
146
146
  } & {
147
- footer?: (props: typeof __VLS_292) => any;
147
+ footer?: (props: typeof __VLS_276) => any;
148
148
  } & {
149
- 'clear-icon'?: (props: typeof __VLS_330) => any;
149
+ 'clear-icon'?: (props: typeof __VLS_314) => any;
150
150
  } & {
151
- 'trigger-icon'?: (props: typeof __VLS_350) => any;
151
+ 'trigger-icon'?: (props: typeof __VLS_329) => any;
152
152
  } & {
153
- 'trigger-icon'?: (props: typeof __VLS_370) => any;
153
+ 'trigger-icon'?: (props: typeof __VLS_351) => any;
154
154
  } & {
155
- 'clear-icon'?: (props: typeof __VLS_386) => any;
155
+ 'clear-icon'?: (props: typeof __VLS_367) => any;
156
156
  } & {
157
- day?: (props: typeof __VLS_406) => any;
157
+ day?: (props: typeof __VLS_382) => any;
158
158
  } & {
159
- footer?: (props: typeof __VLS_409) => any;
159
+ footer?: (props: typeof __VLS_385) => any;
160
160
  } & {
161
- day?: (props: typeof __VLS_432) => any;
161
+ day?: (props: typeof __VLS_408) => any;
162
162
  } & {
163
- footer?: (props: typeof __VLS_435) => any;
163
+ footer?: (props: typeof __VLS_411) => any;
164
164
  };
165
165
  declare const __VLS_base: import("vue").DefineComponent<DatePickerProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
166
166
  "update:modelValue": (value: DateValue | DateRange | Time | undefined) => any;
@@ -24,7 +24,7 @@ import {
24
24
  TimeFieldInput,
25
25
  TimeFieldRoot
26
26
  } from "reka-ui";
27
- import { computed, getCurrentInstance, onMounted, onUnmounted, ref, shallowRef, useId, watch } from "vue";
27
+ import { computed, getCurrentInstance, mergeProps, nextTick, onMounted, onUnmounted, ref, shallowRef, useId, watch } from "vue";
28
28
  import { useFormField } from "../composables/use-form-field";
29
29
  import { useIcons } from "../composables/use-icons";
30
30
  import { useLocale } from "../composables/use-locale";
@@ -33,8 +33,10 @@ import { useMessages } from "../composables/use-messages";
33
33
  import DatePickerCalendarBody from "../internal/DatePickerCalendarBody.vue";
34
34
  import DatePickerRangeCalendarBody from "../internal/DatePickerRangeCalendarBody.vue";
35
35
  import DatePickerTimeBody from "../internal/DatePickerTimeBody.vue";
36
+ import FieldClearButton from "../internal/FieldClearButton.vue";
36
37
  import { datePickerTheme } from "../theme/date-picker";
37
38
  import { arrowContentProps, arrowElementProps } from "../utils/arrow";
39
+ import { canClearField, fieldClearWidths } from "../utils/field-clear";
38
40
  import { overlayPortalProps } from "../utils/overlay";
39
41
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
40
42
  import { applyClassPrefix, resolveSlot, useComponentTheme, useRootProps, useThemeBindings } from "../utils/ui";
@@ -137,8 +139,18 @@ const effectiveNumberOfMonths = computed(() => props.numberOfMonths ?? (props.ra
137
139
  const singleModelValue = computed(() => props.range ? void 0 : modelValue.value);
138
140
  const rangeModelValue = computed(() => props.range ? modelValue.value : void 0);
139
141
  const hasValue = computed(() => props.range ? !!modelValue.value?.start : !!modelValue.value);
140
- function clear() {
142
+ const showClear = computed(() => canClearField(props.clearable, props.range ? !!(modelValue.value?.start || modelValue.value?.end) : !!modelValue.value, props.disabled, props.readonly));
143
+ const fieldElement = ref();
144
+ const buttonTrigger = ref();
145
+ async function clear() {
146
+ if (!showClear.value)
147
+ return;
141
148
  updateModelValue(props.range ? { start: void 0, end: void 0 } : void 0);
149
+ await nextTick();
150
+ if (props.triggerMode === "button")
151
+ buttonTrigger.value?.$el?.focus();
152
+ else
153
+ fieldElement.value?.querySelector("[role=spinbutton]")?.focus();
142
154
  }
143
155
  const defaultView = computed(() => {
144
156
  if (props.granularity === "month" || props.granularity === "year")
@@ -364,6 +376,10 @@ const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root);
364
376
  const fieldProps = computed(() => resolveSlot(ui.value.field, props.ui?.field));
365
377
  const segmentProps = computed(() => resolveSlot(ui.value.segment, props.ui?.segment));
366
378
  const separatorProps = computed(() => resolveSlot(ui.value.separator, props.ui?.separator));
379
+ const clearProps = computed(() => resolveSlot(ui.value.clear, props.ui?.clear));
380
+ const buttonClearProps = computed(() => resolveSlot(ui.value.buttonClear, props.ui?.buttonClear));
381
+ const triggerIconProps = computed(() => resolveSlot(ui.value.triggerIcon, props.ui?.triggerIcon));
382
+ const buttonLabelStyle = computed(() => showClear.value ? { paddingInlineEnd: `calc(${fieldClearWidths[effectiveSize.value]} + 0.25rem)` } : void 0);
367
383
  const contentProps = computed(() => ({ ...resolveSlot(ui.value.content, props.ui?.content), ...arrowContentProps(props.arrow), ...props.positioning }));
368
384
  const portalProps = computed(() => overlayPortalProps(props.portal));
369
385
  const arrowProps = computed(() => ({ ...resolveSlot(ui.value.arrow, props.ui?.arrow), ...arrowElementProps(props.arrow) }));
@@ -451,10 +467,7 @@ const timeBodyProps = computed(() => ({
451
467
  const buttonTriggerUi = computed(() => ({
452
468
  base: [
453
469
  "w-full justify-start rounded-[var(--selaras-resolved-radius-md)] bg-[var(--selaras-resolved-surface-default)] text-[var(--selaras-resolved-text-default)] ring-1 ring-inset ring-[var(--selaras-resolved-border-default)] hover:ring-[var(--selaras-resolved-border-hover)] hover:bg-[var(--selaras-resolved-surface-elevated)]",
454
- datePickerInvalid.value ? "ring-[var(--_selaras-color-fill)] hover:ring-[var(--_selaras-color-fill)]" : void 0,
455
- // Room for the clear button, which sits absolutely positioned on top of
456
- // this same end edge - without it, a long formatted date can run under it.
457
- props.clearable && hasValue.value ? "pe-8" : void 0
470
+ datePickerInvalid.value ? "ring-[var(--_selaras-color-fill)] hover:ring-[var(--_selaras-color-fill)]" : void 0
458
471
  ].filter(Boolean).join(" "),
459
472
  // base is justify-start (text left-aligned, not centered) so the button
460
473
  // reads naturally for a short date string - without its own auto margin,
@@ -497,7 +510,7 @@ const buttonTriggerUi = computed(() => ({
497
510
  @update:model-value="(value) => updateModelValue(value)"
498
511
  >
499
512
  <DateRangePickerAnchor as-child>
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">
513
+ <div v-if="triggerMode === 'field'" ref="fieldElement" 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">
501
514
  <DateRangePickerField v-slot="{ segments }" :aria-labelledby="field?.labelId" :aria-describedby="describedBy" :aria-invalid="datePickerInvalid || void 0">
502
515
  <template v-for="segment in segments.start" :key="`start-${segment.part}`">
503
516
  <DateRangePickerInput as="span" type="start" :part="segment.part" v-bind="segmentProps">
@@ -512,22 +525,13 @@ const buttonTriggerUi = computed(() => ({
512
525
  </template>
513
526
  </DateRangePickerField>
514
527
  <div :class="applyClassPrefix('ms-auto flex shrink-0 items-center gap-1')">
515
- <Button
516
- v-if="clearable && hasValue"
517
- variant="text"
518
- :color="color"
519
- :size="iconButtonSize"
520
- :aria-label="messages.clear"
521
- @click="clear"
522
- >
523
- <template #icon="{ class: iconClass }">
524
- <slot name="clear-icon">
525
- <Icon :name="icons.close" :class="iconClass" />
526
- </slot>
528
+ <FieldClearButton v-if="showClear" :size="effectiveSize" v-bind="clearProps" @click.stop="clear">
529
+ <template v-if="$slots['clear-icon']" #default>
530
+ <slot name="clear-icon" />
527
531
  </template>
528
- </Button>
532
+ </FieldClearButton>
529
533
  <DateRangePickerTrigger as-child>
530
- <Button variant="text" :color="color" :size="iconButtonSize" :aria-label="messages.dateRangePicker">
534
+ <Button variant="text" :color="color" :size="iconButtonSize" :ui="{ leadingIcon: triggerIconProps }" :aria-label="messages.dateRangePicker">
531
535
  <template #icon="{ class: iconClass }">
532
536
  <slot name="trigger-icon">
533
537
  <Icon :name="icons.calendar" :class="iconClass" />
@@ -542,6 +546,7 @@ const buttonTriggerUi = computed(() => ({
542
546
  <DateRangePickerTrigger as-child>
543
547
  <Button
544
548
  :id="datePickerId"
549
+ ref="buttonTrigger"
545
550
  variant="text"
546
551
  :color="color"
547
552
  :size="effectiveSize"
@@ -550,29 +555,19 @@ const buttonTriggerUi = computed(() => ({
550
555
  :aria-labelledby="field?.labelId"
551
556
  :ui="buttonTriggerUi"
552
557
  >
553
- {{ hasValue ? formattedValue : field?.floatingLabel.value ? "" : messages.pickDate }}
554
- <template v-if="!(clearable && hasValue)" #trailing-icon="{ class: iconClass }">
558
+ <span :class="applyClassPrefix('min-w-0 flex-1 truncate text-start')" :style="buttonLabelStyle">{{ hasValue ? formattedValue : field?.floatingLabel.value ? "" : messages.pickDate }}</span>
559
+ <template #trailing-icon="{ class: iconClass }">
555
560
  <slot name="trigger-icon">
556
561
  <Icon :name="icons.calendar" :class="iconClass" />
557
562
  </slot>
558
563
  </template>
559
564
  </Button>
560
565
  </DateRangePickerTrigger>
561
- <Button
562
- v-if="clearable && hasValue"
563
- variant="text"
564
- :color="color"
565
- :size="iconButtonSize"
566
- :aria-label="messages.clear"
567
- :class="applyClassPrefix('absolute end-1 top-1/2 -translate-y-1/2')"
568
- @click.stop="clear"
569
- >
570
- <template #icon="{ class: iconClass }">
571
- <slot name="clear-icon">
572
- <Icon :name="icons.close" :class="iconClass" />
573
- </slot>
566
+ <FieldClearButton v-if="showClear" :size="effectiveSize" v-bind="mergeProps(clearProps, buttonClearProps)" @click.stop="clear">
567
+ <template v-if="$slots['clear-icon']" #default>
568
+ <slot name="clear-icon" />
574
569
  </template>
575
- </Button>
570
+ </FieldClearButton>
576
571
  </div>
577
572
  </DateRangePickerAnchor>
578
573
 
@@ -616,7 +611,7 @@ const buttonTriggerUi = computed(() => ({
616
611
 
617
612
  <PopoverRoot v-else-if="timeOnly" :open="open" :data-selaras-color="fieldColor" @update:open="onUpdateOpen">
618
613
  <PopoverAnchor as-child>
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
+ <div v-if="triggerMode === 'field'" ref="fieldElement" 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">
620
615
  <TimeFieldRoot
621
616
  :id="datePickerId"
622
617
  v-slot="{ segments }"
@@ -640,22 +635,13 @@ const buttonTriggerUi = computed(() => ({
640
635
  </template>
641
636
  </TimeFieldRoot>
642
637
  <div :class="applyClassPrefix('ms-auto flex shrink-0 items-center gap-1')">
643
- <Button
644
- v-if="clearable && hasValue"
645
- variant="text"
646
- :color="color"
647
- :size="iconButtonSize"
648
- :aria-label="messages.clear"
649
- @click="clear"
650
- >
651
- <template #icon="{ class: iconClass }">
652
- <slot name="clear-icon">
653
- <Icon :name="icons.close" :class="iconClass" />
654
- </slot>
638
+ <FieldClearButton v-if="showClear" :size="effectiveSize" v-bind="clearProps" @click.stop="clear">
639
+ <template v-if="$slots['clear-icon']" #default>
640
+ <slot name="clear-icon" />
655
641
  </template>
656
- </Button>
642
+ </FieldClearButton>
657
643
  <PopoverTrigger as-child>
658
- <Button variant="text" :color="color" :size="iconButtonSize" :aria-label="messages.timePicker">
644
+ <Button variant="text" :color="color" :size="iconButtonSize" :ui="{ leadingIcon: triggerIconProps }" :aria-label="messages.timePicker">
659
645
  <template #icon="{ class: iconClass }">
660
646
  <slot name="trigger-icon">
661
647
  <Icon :name="icons.clock" :class="iconClass" />
@@ -670,6 +656,7 @@ const buttonTriggerUi = computed(() => ({
670
656
  <PopoverTrigger as-child>
671
657
  <Button
672
658
  :id="datePickerId"
659
+ ref="buttonTrigger"
673
660
  variant="text"
674
661
  :color="color"
675
662
  :size="effectiveSize"
@@ -678,29 +665,19 @@ const buttonTriggerUi = computed(() => ({
678
665
  :aria-labelledby="field?.labelId"
679
666
  :ui="buttonTriggerUi"
680
667
  >
681
- {{ hasValue ? formattedTimeValue : field?.floatingLabel.value ? "" : messages.pickTime }}
682
- <template v-if="!(clearable && hasValue)" #trailing-icon="{ class: iconClass }">
668
+ <span :class="applyClassPrefix('min-w-0 flex-1 truncate text-start')" :style="buttonLabelStyle">{{ hasValue ? formattedTimeValue : field?.floatingLabel.value ? "" : messages.pickTime }}</span>
669
+ <template #trailing-icon="{ class: iconClass }">
683
670
  <slot name="trigger-icon">
684
671
  <Icon :name="icons.clock" :class="iconClass" />
685
672
  </slot>
686
673
  </template>
687
674
  </Button>
688
675
  </PopoverTrigger>
689
- <Button
690
- v-if="clearable && hasValue"
691
- variant="text"
692
- :color="color"
693
- :size="iconButtonSize"
694
- :aria-label="messages.clear"
695
- :class="applyClassPrefix('absolute end-1 top-1/2 -translate-y-1/2')"
696
- @click.stop="clear"
697
- >
698
- <template #icon="{ class: iconClass }">
699
- <slot name="clear-icon">
700
- <Icon :name="icons.close" :class="iconClass" />
701
- </slot>
676
+ <FieldClearButton v-if="showClear" :size="effectiveSize" v-bind="mergeProps(clearProps, buttonClearProps)" @click.stop="clear">
677
+ <template v-if="$slots['clear-icon']" #default>
678
+ <slot name="clear-icon" />
702
679
  </template>
703
- </Button>
680
+ </FieldClearButton>
704
681
  </div>
705
682
  </PopoverAnchor>
706
683
 
@@ -760,7 +737,7 @@ const buttonTriggerUi = computed(() => ({
760
737
  @update:model-value="(value) => updateModelValue(normalizeForGranularity(value))"
761
738
  >
762
739
  <DatePickerAnchor as-child>
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">
740
+ <div v-if="triggerMode === 'field'" ref="fieldElement" 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">
764
741
  <DatePickerField v-slot="{ segments }" :aria-labelledby="field?.labelId" :aria-describedby="describedBy" :aria-invalid="datePickerInvalid || void 0">
765
742
  <template v-for="(segment, index) in visibleSegments(segments)" :key="`${segment.part}-${index}`">
766
743
  <DatePickerInput as="span" :part="segment.part" v-bind="segmentProps">
@@ -769,22 +746,13 @@ const buttonTriggerUi = computed(() => ({
769
746
  </template>
770
747
  </DatePickerField>
771
748
  <div :class="applyClassPrefix('ms-auto flex shrink-0 items-center gap-1')">
772
- <Button
773
- v-if="clearable && hasValue"
774
- variant="text"
775
- :color="color"
776
- :size="iconButtonSize"
777
- :aria-label="messages.clear"
778
- @click="clear"
779
- >
780
- <template #icon="{ class: iconClass }">
781
- <slot name="clear-icon">
782
- <Icon :name="icons.close" :class="iconClass" />
783
- </slot>
749
+ <FieldClearButton v-if="showClear" :size="effectiveSize" v-bind="clearProps" @click.stop="clear">
750
+ <template v-if="$slots['clear-icon']" #default>
751
+ <slot name="clear-icon" />
784
752
  </template>
785
- </Button>
753
+ </FieldClearButton>
786
754
  <DatePickerTrigger as-child>
787
- <Button variant="text" :color="color" :size="iconButtonSize" :aria-label="messages.datePicker">
755
+ <Button variant="text" :color="color" :size="iconButtonSize" :ui="{ leadingIcon: triggerIconProps }" :aria-label="messages.datePicker">
788
756
  <template #icon="{ class: iconClass }">
789
757
  <slot name="trigger-icon">
790
758
  <Icon :name="icons.calendar" :class="iconClass" />
@@ -799,6 +767,7 @@ const buttonTriggerUi = computed(() => ({
799
767
  <DatePickerTrigger as-child>
800
768
  <Button
801
769
  :id="datePickerId"
770
+ ref="buttonTrigger"
802
771
  variant="text"
803
772
  :color="color"
804
773
  :size="effectiveSize"
@@ -807,29 +776,19 @@ const buttonTriggerUi = computed(() => ({
807
776
  :aria-labelledby="field?.labelId"
808
777
  :ui="buttonTriggerUi"
809
778
  >
810
- {{ hasValue ? formattedValue : field?.floatingLabel.value ? "" : messages.pickDate }}
811
- <template v-if="!(clearable && hasValue)" #trailing-icon="{ class: iconClass }">
779
+ <span :class="applyClassPrefix('min-w-0 flex-1 truncate text-start')" :style="buttonLabelStyle">{{ hasValue ? formattedValue : field?.floatingLabel.value ? "" : messages.pickDate }}</span>
780
+ <template #trailing-icon="{ class: iconClass }">
812
781
  <slot name="trigger-icon">
813
782
  <Icon :name="icons.calendar" :class="iconClass" />
814
783
  </slot>
815
784
  </template>
816
785
  </Button>
817
786
  </DatePickerTrigger>
818
- <Button
819
- v-if="clearable && hasValue"
820
- variant="text"
821
- :color="color"
822
- :size="iconButtonSize"
823
- :aria-label="messages.clear"
824
- :class="applyClassPrefix('absolute end-1 top-1/2 -translate-y-1/2')"
825
- @click.stop="clear"
826
- >
827
- <template #icon="{ class: iconClass }">
828
- <slot name="clear-icon">
829
- <Icon :name="icons.close" :class="iconClass" />
830
- </slot>
787
+ <FieldClearButton v-if="showClear" :size="effectiveSize" v-bind="mergeProps(clearProps, buttonClearProps)" @click.stop="clear">
788
+ <template v-if="$slots['clear-icon']" #default>
789
+ <slot name="clear-icon" />
831
790
  </template>
832
- </Button>
791
+ </FieldClearButton>
833
792
  </div>
834
793
  </DatePickerAnchor>
835
794