@sewadah/selaras 0.8.0 → 0.9.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.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "selaras",
3
3
  "configKey": "selaras",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "unknown"
@@ -69,6 +69,12 @@ const unscopedSlots = ["header", "footer", "empty", "empty-filter", "clear-icon"
69
69
  @update:model-value="updateValue"
70
70
  @update:search-term="emit('update:searchTerm', $event)"
71
71
  >
72
+ <template v-if="slots.leading" #leading="scope">
73
+ <slot name="leading" v-bind="scope" />
74
+ </template>
75
+ <template v-if="slots.trailing" #trailing="scope">
76
+ <slot name="trailing" v-bind="scope" />
77
+ </template>
72
78
  <template v-if="slots.item" #item="{ item }">
73
79
  <slot name="item" :item="itemData(item)" />
74
80
  </template>
@@ -1,9 +1,11 @@
1
1
  import type { VariantProps } from 'tailwind-variants';
2
2
  import type { InputThemeSlots } from '../theme/input.js';
3
3
  import type { ColorRole } from '../utils/color-registry.js';
4
+ import type { FieldAdornmentSlotProps } from '../utils/field-adornments.js';
4
5
  import type { UiProp } from '../utils/ui.js';
5
6
  import { inputTheme } from '../theme/input.js';
6
7
  type InputVariants = VariantProps<typeof inputTheme>;
8
+ type __VLS_Slots = InputSlots;
7
9
  export interface InputProps {
8
10
  id?: string;
9
11
  name?: string;
@@ -23,10 +25,11 @@ export interface InputProps {
23
25
  export interface InputEmits {
24
26
  'update:modelValue': [value: string];
25
27
  }
26
- declare var __VLS_15: {};
27
- type __VLS_Slots = {} & {
28
- 'clear-icon'?: (props: typeof __VLS_15) => any;
29
- };
28
+ export interface InputSlots {
29
+ 'leading'?: (props: FieldAdornmentSlotProps) => any;
30
+ 'trailing'?: (props: FieldAdornmentSlotProps) => any;
31
+ 'clear-icon'?: () => any;
32
+ }
30
33
  declare const __VLS_base: import("vue").DefineComponent<InputProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
31
34
  "update:modelValue": (value: string) => any;
32
35
  }, string, import("vue").PublicProps, Readonly<InputProps> & Readonly<{
@@ -4,6 +4,7 @@ import { useFormField } from "../composables/use-form-field";
4
4
  import { useIcons } from "../composables/use-icons";
5
5
  import { useMessages } from "../composables/use-messages";
6
6
  import { inputTheme } from "../theme/input";
7
+ import { useFieldAdornments } from "../utils/field-adornments";
7
8
  import { isNativeInputA11yAttr, isNativeInputAttr } from "../utils/native-input";
8
9
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
9
10
  import { resolveSlot, useComponentTheme, useFallthroughAttrs, useRootProps, useThemeProps } from "../utils/ui";
@@ -26,6 +27,8 @@ const props = defineProps({
26
27
  ui: { type: Object, required: false }
27
28
  });
28
29
  const emit = defineEmits(["update:modelValue"]);
30
+ const slots = defineSlots();
31
+ const { leading, trailing, padding, clearOffset } = useFieldAdornments();
29
32
  const field = useFormField();
30
33
  const effectivePlaceholder = computed(() => props.placeholder || (field?.floatingLabel.value ? " " : void 0));
31
34
  const inputId = computed(() => props.id ?? field?.id);
@@ -39,6 +42,8 @@ function clear() {
39
42
  const themeProps = useThemeProps("input");
40
43
  const effectiveSize = computed(() => props.size ?? field?.size ?? themeProps.value.size ?? "md");
41
44
  const clearSize = computed(() => ({ sm: "sm", md: "sm", lg: "md" })[effectiveSize.value]);
45
+ const adornmentScope = computed(() => ({ disabled: !!props.disabled, size: effectiveSize.value, buttonSize: clearSize.value }));
46
+ const clearWidth = computed(() => ({ sm: "1.75rem", md: "2.25rem", lg: "2.5rem" })[effectiveSize.value]);
42
47
  const icons = useIcons();
43
48
  const messages = useMessages();
44
49
  const theme = useComponentTheme("input", inputTheme);
@@ -55,11 +60,20 @@ const rootProps = useRootProps(() => ui.value.root, () => props.ui?.root, { excl
55
60
  const nativeInputAttrs = useFallthroughAttrs(isInputAttr);
56
61
  const baseProps = computed(() => resolveSlot(ui.value.base, props.ui?.base));
57
62
  const inputProps = computed(() => mergeProps(baseProps.value, nativeInputAttrs.value));
63
+ function adornedInputProps() {
64
+ return mergeProps({ style: padding(!!slots.leading, !!slots.trailing, showClear.value ? clearWidth.value : "0rem") }, inputProps.value);
65
+ }
66
+ function clearProps() {
67
+ return mergeProps({ style: slots.trailing ? clearOffset() : void 0 }, resolveSlot(ui.value.clear, props.ui?.clear));
68
+ }
58
69
  </script>
59
70
 
60
71
  <template>
61
- <div :data-selaras-color="inputInvalid ? 'danger' : effectiveColor" :data-selaras-field-leading="icon ? '' : void 0" v-bind="rootProps">
62
- <Icon v-if="icon" :name="icon" v-bind="resolveSlot(ui.leadingIcon, props.ui?.leadingIcon)" />
72
+ <div :data-selaras-color="inputInvalid ? 'danger' : effectiveColor" :data-selaras-field-leading="icon || slots.leading ? '' : void 0" v-bind="rootProps">
73
+ <fieldset v-if="slots.leading" ref="leading" :disabled="disabled" :inert="disabled || void 0" v-bind="resolveSlot(ui.leading, props.ui?.leading)">
74
+ <slot name="leading" v-bind="adornmentScope" />
75
+ </fieldset>
76
+ <Icon v-else-if="icon" :name="icon" v-bind="resolveSlot(ui.leadingIcon, props.ui?.leadingIcon)" />
63
77
  <input
64
78
  :id="inputId"
65
79
  data-ui-group-item
@@ -70,7 +84,7 @@ const inputProps = computed(() => mergeProps(baseProps.value, nativeInputAttrs.v
70
84
  :disabled="disabled"
71
85
  :aria-invalid="inputInvalid || void 0"
72
86
  :aria-describedby="describedBy"
73
- v-bind="inputProps"
87
+ v-bind="adornedInputProps()"
74
88
  @input="$emit('update:modelValue', $event.target.value)"
75
89
  >
76
90
  <Button
@@ -79,7 +93,7 @@ const inputProps = computed(() => mergeProps(baseProps.value, nativeInputAttrs.v
79
93
  variant="text"
80
94
  color="neutral"
81
95
  :aria-label="messages.clear"
82
- v-bind="resolveSlot(ui.clear, props.ui?.clear)"
96
+ v-bind="clearProps()"
83
97
  @click="clear"
84
98
  >
85
99
  <template #icon="{ class: iconClass }">
@@ -88,6 +102,9 @@ const inputProps = computed(() => mergeProps(baseProps.value, nativeInputAttrs.v
88
102
  </slot>
89
103
  </template>
90
104
  </Button>
91
- <Icon v-else-if="trailingIcon" :name="trailingIcon" v-bind="resolveSlot(ui.trailingIcon, props.ui?.trailingIcon)" />
105
+ <Icon v-else-if="trailingIcon && !slots.trailing" :name="trailingIcon" v-bind="resolveSlot(ui.trailingIcon, props.ui?.trailingIcon)" />
106
+ <fieldset v-if="slots.trailing" ref="trailing" :disabled="disabled" :inert="disabled || void 0" v-bind="resolveSlot(ui.trailing, props.ui?.trailing)">
107
+ <slot name="trailing" v-bind="adornmentScope" />
108
+ </fieldset>
92
109
  </div>
93
110
  </template>
@@ -1,9 +1,11 @@
1
1
  import type { VariantProps } from 'tailwind-variants';
2
2
  import type { InputThemeSlots } from '../theme/input.js';
3
3
  import type { ColorRole } from '../utils/color-registry.js';
4
+ import type { FieldAdornmentSlotProps } from '../utils/field-adornments.js';
4
5
  import type { UiProp } from '../utils/ui.js';
5
6
  import { inputTheme } from '../theme/input.js';
6
7
  type InputVariants = VariantProps<typeof inputTheme>;
8
+ type __VLS_Slots = InputSlots;
7
9
  export interface InputProps {
8
10
  id?: string;
9
11
  name?: string;
@@ -23,10 +25,11 @@ export interface InputProps {
23
25
  export interface InputEmits {
24
26
  'update:modelValue': [value: string];
25
27
  }
26
- declare var __VLS_15: {};
27
- type __VLS_Slots = {} & {
28
- 'clear-icon'?: (props: typeof __VLS_15) => any;
29
- };
28
+ export interface InputSlots {
29
+ 'leading'?: (props: FieldAdornmentSlotProps) => any;
30
+ 'trailing'?: (props: FieldAdornmentSlotProps) => any;
31
+ 'clear-icon'?: () => any;
32
+ }
30
33
  declare const __VLS_base: import("vue").DefineComponent<InputProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
31
34
  "update:modelValue": (value: string) => any;
32
35
  }, string, import("vue").PublicProps, Readonly<InputProps> & Readonly<{
@@ -75,6 +75,12 @@ const unscopedSlots = ["header", "footer", "empty", "empty-filter", "filter-icon
75
75
  @update:model-value="updateValue"
76
76
  @update:search-term="emit('update:searchTerm', $event)"
77
77
  >
78
+ <template v-if="slots.leading" #leading="scope">
79
+ <slot name="leading" v-bind="scope" />
80
+ </template>
81
+ <template v-if="slots.trailing" #trailing="scope">
82
+ <slot name="trailing" v-bind="scope" />
83
+ </template>
78
84
  <template v-if="slots.item" #item="{ item }">
79
85
  <slot name="item" :item="itemData(item)" />
80
86
  </template>
@@ -1,9 +1,11 @@
1
1
  import type { VariantProps } from 'tailwind-variants';
2
2
  import type { TextareaThemeSlots } from '../theme/textarea.js';
3
3
  import type { ColorRole } from '../utils/color-registry.js';
4
+ import type { FieldAdornmentSlotProps } from '../utils/field-adornments.js';
4
5
  import type { UiProp } from '../utils/ui.js';
5
6
  import { textareaTheme } from '../theme/textarea.js';
6
7
  type TextareaVariants = VariantProps<typeof textareaTheme>;
8
+ type __VLS_Slots = TextareaSlots;
7
9
  export interface TextareaProps {
8
10
  id?: string;
9
11
  name?: string;
@@ -27,10 +29,11 @@ export interface TextareaProps {
27
29
  export interface TextareaEmits {
28
30
  'update:modelValue': [value: string];
29
31
  }
30
- declare var __VLS_15: {};
31
- type __VLS_Slots = {} & {
32
- 'clear-icon'?: (props: typeof __VLS_15) => any;
33
- };
32
+ export interface TextareaSlots {
33
+ 'leading'?: (props: FieldAdornmentSlotProps) => any;
34
+ 'trailing'?: (props: FieldAdornmentSlotProps) => any;
35
+ 'clear-icon'?: () => any;
36
+ }
34
37
  declare const __VLS_base: import("vue").DefineComponent<TextareaProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
38
  "update:modelValue": (value: string) => any;
36
39
  }, string, import("vue").PublicProps, Readonly<TextareaProps> & Readonly<{
@@ -4,6 +4,7 @@ import { useFormField } from "../composables/use-form-field";
4
4
  import { useIcons } from "../composables/use-icons";
5
5
  import { useMessages } from "../composables/use-messages";
6
6
  import { textareaTheme } from "../theme/textarea";
7
+ import { useFieldAdornments } from "../utils/field-adornments";
7
8
  import { isNativeInputA11yAttr, isNativeInputAttr } from "../utils/native-input";
8
9
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
9
10
  import { resolveSlot, useComponentTheme, useFallthroughAttrs, useRootProps } from "../utils/ui";
@@ -28,6 +29,8 @@ const props = defineProps({
28
29
  ui: { type: Object, required: false }
29
30
  });
30
31
  const emit = defineEmits(["update:modelValue"]);
32
+ const slots = defineSlots();
33
+ const { leading, trailing, padding, clearOffset } = useFieldAdornments();
31
34
  const field = useFormField();
32
35
  const effectivePlaceholder = computed(() => props.placeholder || (field?.floatingLabel.value ? " " : void 0));
33
36
  const textareaId = computed(() => props.id ?? field?.id);
@@ -40,6 +43,8 @@ function clear() {
40
43
  }
41
44
  const effectiveSize = computed(() => props.size ?? field?.size ?? "md");
42
45
  const clearSize = computed(() => ({ sm: "sm", md: "sm", lg: "md" })[effectiveSize.value]);
46
+ const adornmentScope = computed(() => ({ disabled: !!props.disabled, size: effectiveSize.value, buttonSize: clearSize.value }));
47
+ const clearWidth = computed(() => ({ sm: "1.75rem", md: "2.25rem", lg: "2.5rem" })[effectiveSize.value]);
43
48
  const icons = useIcons();
44
49
  const messages = useMessages();
45
50
  const theme = useComponentTheme("textarea", textareaTheme);
@@ -66,8 +71,8 @@ async function resize() {
66
71
  if (props.maxrows) {
67
72
  const cs = getComputedStyle(el);
68
73
  const lineHeight = Number.parseFloat(cs.lineHeight);
69
- const padding = Number.parseFloat(cs.paddingTop) + Number.parseFloat(cs.paddingBottom);
70
- const maxHeight = lineHeight * props.maxrows + padding;
74
+ const padding2 = Number.parseFloat(cs.paddingTop) + Number.parseFloat(cs.paddingBottom);
75
+ const maxHeight = lineHeight * props.maxrows + padding2;
71
76
  el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
72
77
  el.style.overflowY = el.scrollHeight > maxHeight ? "auto" : "hidden";
73
78
  return;
@@ -76,11 +81,20 @@ async function resize() {
76
81
  }
77
82
  onMounted(resize);
78
83
  watch(() => props.modelValue, resize);
84
+ function adornedInputProps() {
85
+ return mergeProps({ style: padding(!!slots.leading, !!slots.trailing, showClear.value ? clearWidth.value : "0rem") }, baseProps.value);
86
+ }
87
+ function clearProps() {
88
+ return mergeProps({ style: slots.trailing ? clearOffset() : void 0 }, resolveSlot(ui.value.clear, props.ui?.clear));
89
+ }
79
90
  </script>
80
91
 
81
92
  <template>
82
- <div :data-selaras-color="textareaInvalid ? 'danger' : effectiveColor" :data-selaras-field-leading="icon ? '' : void 0" v-bind="rootProps">
83
- <Icon v-if="icon" :name="icon" v-bind="resolveSlot(ui.leadingIcon, props.ui?.leadingIcon)" />
93
+ <div :data-selaras-color="textareaInvalid ? 'danger' : effectiveColor" :data-selaras-field-leading="icon || slots.leading ? '' : void 0" v-bind="rootProps">
94
+ <fieldset v-if="slots.leading" ref="leading" :disabled="disabled" :inert="disabled || void 0" v-bind="resolveSlot(ui.leading, props.ui?.leading)">
95
+ <slot name="leading" v-bind="adornmentScope" />
96
+ </fieldset>
97
+ <Icon v-else-if="icon" :name="icon" v-bind="resolveSlot(ui.leadingIcon, props.ui?.leadingIcon)" />
84
98
  <textarea
85
99
  :id="textareaId"
86
100
  ref="textareaRef"
@@ -92,7 +106,7 @@ watch(() => props.modelValue, resize);
92
106
  :disabled="disabled"
93
107
  :aria-invalid="textareaInvalid || void 0"
94
108
  :aria-describedby="describedBy"
95
- v-bind="baseProps"
109
+ v-bind="adornedInputProps()"
96
110
  @input="$emit('update:modelValue', $event.target.value);
97
111
  resize()"
98
112
  />
@@ -102,7 +116,7 @@ watch(() => props.modelValue, resize);
102
116
  variant="text"
103
117
  color="neutral"
104
118
  :aria-label="messages.clear"
105
- v-bind="resolveSlot(ui.clear, props.ui?.clear)"
119
+ v-bind="clearProps()"
106
120
  @click="clear"
107
121
  >
108
122
  <template #icon="{ class: iconClass }">
@@ -111,6 +125,9 @@ watch(() => props.modelValue, resize);
111
125
  </slot>
112
126
  </template>
113
127
  </Button>
114
- <Icon v-else-if="trailingIcon" :name="trailingIcon" v-bind="resolveSlot(ui.trailingIcon, props.ui?.trailingIcon)" />
128
+ <Icon v-else-if="trailingIcon && !slots.trailing" :name="trailingIcon" v-bind="resolveSlot(ui.trailingIcon, props.ui?.trailingIcon)" />
129
+ <fieldset v-if="slots.trailing" ref="trailing" :disabled="disabled" :inert="disabled || void 0" v-bind="resolveSlot(ui.trailing, props.ui?.trailing)">
130
+ <slot name="trailing" v-bind="adornmentScope" />
131
+ </fieldset>
115
132
  </div>
116
133
  </template>
@@ -1,9 +1,11 @@
1
1
  import type { VariantProps } from 'tailwind-variants';
2
2
  import type { TextareaThemeSlots } from '../theme/textarea.js';
3
3
  import type { ColorRole } from '../utils/color-registry.js';
4
+ import type { FieldAdornmentSlotProps } from '../utils/field-adornments.js';
4
5
  import type { UiProp } from '../utils/ui.js';
5
6
  import { textareaTheme } from '../theme/textarea.js';
6
7
  type TextareaVariants = VariantProps<typeof textareaTheme>;
8
+ type __VLS_Slots = TextareaSlots;
7
9
  export interface TextareaProps {
8
10
  id?: string;
9
11
  name?: string;
@@ -27,10 +29,11 @@ export interface TextareaProps {
27
29
  export interface TextareaEmits {
28
30
  'update:modelValue': [value: string];
29
31
  }
30
- declare var __VLS_15: {};
31
- type __VLS_Slots = {} & {
32
- 'clear-icon'?: (props: typeof __VLS_15) => any;
33
- };
32
+ export interface TextareaSlots {
33
+ 'leading'?: (props: FieldAdornmentSlotProps) => any;
34
+ 'trailing'?: (props: FieldAdornmentSlotProps) => any;
35
+ 'clear-icon'?: () => any;
36
+ }
34
37
  declare const __VLS_base: import("vue").DefineComponent<TextareaProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
38
  "update:modelValue": (value: string) => any;
36
39
  }, string, import("vue").PublicProps, Readonly<TextareaProps> & Readonly<{
@@ -2,12 +2,18 @@ import type { VariantProps } from 'tailwind-variants';
2
2
  import type { SelectThemeSlots } from '../theme/select.js';
3
3
  import type { RoundedArrowConfig } from '../utils/arrow.js';
4
4
  import type { ColorRole } from '../utils/color-registry.js';
5
+ import type { FieldAdornmentSlotProps } from '../utils/field-adornments.js';
5
6
  import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
6
7
  import type { SelectModelValue } from '../utils/select-contracts.js';
7
8
  import type { UiProp } from '../utils/ui.js';
8
- import type { SelectItems, SelectOption } from './combobox-select.js';
9
+ import type { SelectItems } from './combobox-select.js';
9
10
  import { selectTheme } from '../theme/select.js';
10
11
  type SelectVariants = VariantProps<typeof selectTheme>;
12
+ type __VLS_Slots = {
13
+ leading?: (props: FieldAdornmentSlotProps) => any;
14
+ trailing?: (props: FieldAdornmentSlotProps) => any;
15
+ [name: string]: ((props: any) => any) | undefined;
16
+ };
11
17
  export interface ComboboxSelectBaseProps {
12
18
  id?: string;
13
19
  name?: string;
@@ -58,68 +64,6 @@ export interface ComboboxSelectBaseEmits {
58
64
  'update:modelValue': [value: SelectModelValue | SelectModelValue[] | undefined];
59
65
  'update:searchTerm': [value: string];
60
66
  }
61
- declare var __VLS_43: {
62
- item: SelectModelValue | SelectOption<SelectModelValue>;
63
- }, __VLS_98: {}, __VLS_105: {}, __VLS_120: {}, __VLS_136: {}, __VLS_143: {}, __VLS_150: {}, __VLS_174: {
64
- item: SelectModelValue | SelectOption<SelectModelValue>;
65
- }, __VLS_194: {
66
- selected: import("./combobox-select.js").ResolvedOption | undefined;
67
- }, __VLS_205: {}, __VLS_212: {}, __VLS_219: {}, __VLS_249: {}, __VLS_252: {}, __VLS_255: {}, __VLS_258: {}, __VLS_261: {
68
- item: SelectModelValue | SelectOption<SelectModelValue>;
69
- }, __VLS_264: {
70
- group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
71
- }, __VLS_267: {}, __VLS_305: string, __VLS_306: {}, __VLS_309: {}, __VLS_319: {
72
- item: SelectModelValue | SelectOption<SelectModelValue>;
73
- }, __VLS_322: {
74
- group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
75
- };
76
- type __VLS_Slots = {} & {
77
- [K in NonNullable<typeof __VLS_305>]?: (props: typeof __VLS_306) => any;
78
- } & {
79
- item?: (props: typeof __VLS_43) => any;
80
- } & {
81
- 'clear-icon'?: (props: typeof __VLS_98) => any;
82
- } & {
83
- 'loading-icon'?: (props: typeof __VLS_105) => any;
84
- } & {
85
- 'dropdown-icon'?: (props: typeof __VLS_120) => any;
86
- } & {
87
- 'clear-icon'?: (props: typeof __VLS_136) => any;
88
- } & {
89
- 'loading-icon'?: (props: typeof __VLS_143) => any;
90
- } & {
91
- 'dropdown-icon'?: (props: typeof __VLS_150) => any;
92
- } & {
93
- item?: (props: typeof __VLS_174) => any;
94
- } & {
95
- value?: (props: typeof __VLS_194) => any;
96
- } & {
97
- 'clear-icon'?: (props: typeof __VLS_205) => any;
98
- } & {
99
- 'loading-icon'?: (props: typeof __VLS_212) => any;
100
- } & {
101
- 'dropdown-icon'?: (props: typeof __VLS_219) => any;
102
- } & {
103
- header?: (props: typeof __VLS_249) => any;
104
- } & {
105
- 'filter-icon'?: (props: typeof __VLS_252) => any;
106
- } & {
107
- empty?: (props: typeof __VLS_255) => any;
108
- } & {
109
- 'empty-filter'?: (props: typeof __VLS_258) => any;
110
- } & {
111
- item?: (props: typeof __VLS_261) => any;
112
- } & {
113
- group?: (props: typeof __VLS_264) => any;
114
- } & {
115
- footer?: (props: typeof __VLS_267) => any;
116
- } & {
117
- header?: (props: typeof __VLS_309) => any;
118
- } & {
119
- item?: (props: typeof __VLS_319) => any;
120
- } & {
121
- group?: (props: typeof __VLS_322) => any;
122
- };
123
67
  declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBaseProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
124
68
  "update:modelValue": (value: SelectModelValue | SelectModelValue[] | undefined) => any;
125
69
  "update:open": (value: boolean) => any;
@@ -28,10 +28,11 @@ import { useIsMobile } from "../composables/use-media-query";
28
28
  import { useMessages } from "../composables/use-messages";
29
29
  import { selectTheme } from "../theme/select";
30
30
  import { arrowContentProps, arrowElementProps } from "../utils/arrow";
31
+ import { useFieldAdornments } from "../utils/field-adornments";
31
32
  import { isNativeInputA11yAttr, isNativeInputAttr, isNativeInputEvent } from "../utils/native-input";
32
33
  import { overlayPortalProps } from "../utils/overlay";
33
34
  import { resolveRegisteredColorRole } from "../utils/registered-colors";
34
- import { resolveSlot, useComponentTheme, useFallthroughAttrs, useRootProps, useThemeBindings } from "../utils/ui";
35
+ import { applyClassPrefix, resolveSlot, useComponentTheme, useFallthroughAttrs, useRootProps, useThemeBindings } from "../utils/ui";
35
36
  import { useComboboxSelect } from "./combobox-select";
36
37
  import ComboboxSelectBody from "./ComboboxSelectBody.vue";
37
38
  defineOptions({ inheritAttrs: false });
@@ -72,6 +73,8 @@ const props = defineProps({
72
73
  ui: { type: Object, required: false }
73
74
  });
74
75
  const emit = defineEmits(["update:open", "update:modelValue", "update:searchTerm"]);
76
+ const slots = defineSlots();
77
+ const { leading, trailing, padding } = useFieldAdornments();
75
78
  const instance = getCurrentInstance();
76
79
  const initialValue = Array.isArray(props.defaultValue) ? [...props.defaultValue] : props.defaultValue;
77
80
  const isControlled = () => Object.hasOwn(instance.vnode.props ?? {}, "modelValue") || Object.hasOwn(instance.vnode.props ?? {}, "model-value");
@@ -413,6 +416,7 @@ const searchDescribedBy = computed(() => [
413
416
  ].filter(Boolean).join(" ") || void 0);
414
417
  const effectiveSize = computed(() => props.size ?? field?.size ?? "md");
415
418
  const clearSize = computed(() => ({ sm: "sm", md: "sm", lg: "md" })[effectiveSize.value]);
419
+ const adornmentScope = computed(() => ({ disabled: !!props.disabled, size: effectiveSize.value, buttonSize: clearSize.value }));
416
420
  const icons = useIcons();
417
421
  const messages = useMessages();
418
422
  const theme = useComponentTheme("select", selectTheme);
@@ -427,6 +431,9 @@ const triggerProps = computed(() => mergeProps(
427
431
  resolveSlot(ui.value.trigger, props.ui?.trigger),
428
432
  !props.creatable ? nativeTriggerAttrs.value : {}
429
433
  ));
434
+ function adornedTriggerProps() {
435
+ return mergeProps({ style: padding(!!slots.leading, !!slots.trailing) }, triggerProps.value);
436
+ }
430
437
  const valueProps = computed(() => resolveSlot(ui.value.value, props.ui?.value));
431
438
  const commaValueProps = computed(() => {
432
439
  const override = props.ui?.value;
@@ -596,7 +603,10 @@ const bodyProps = computed(() => ({
596
603
  @update:open="updateOpen($event)"
597
604
  @update:model-value="(value2) => onComboboxValueUpdate(value2)"
598
605
  >
599
- <ComboboxAnchor>
606
+ <ComboboxAnchor :class="applyClassPrefix('relative')">
607
+ <fieldset v-if="slots.leading" ref="leading" :disabled="disabled" :inert="disabled || void 0" v-bind="resolveSlot(ui.leading, props.ui?.leading)">
608
+ <slot name="leading" v-bind="adornmentScope" />
609
+ </fieldset>
600
610
  <!--
601
611
  Select (creatable=false): a button trigger, opened by clicking anywhere
602
612
  on it - the search input (if any) lives inside the popover instead.
@@ -608,7 +618,7 @@ const bodyProps = computed(() => ({
608
618
  data-ui-group-item
609
619
  :data-selaras-field-filled="selectedValues.length ? '' : void 0"
610
620
  :data-selaras-field-active="open ? '' : void 0"
611
- v-bind="triggerProps"
621
+ v-bind="adornedTriggerProps()"
612
622
  @keydown.capture="onSearchKeydown"
613
623
  >
614
624
  <!--
@@ -745,7 +755,7 @@ const bodyProps = computed(() => ({
745
755
  data-ui-group-item
746
756
  :data-selaras-field-filled="selectedValues.length ? '' : void 0"
747
757
  :data-selaras-field-active="open ? '' : void 0"
748
- v-bind="triggerProps"
758
+ v-bind="adornedTriggerProps()"
749
759
  @click="openMobileAutocomplete()"
750
760
  >
751
761
  <span v-if="multiple && selectedOptions.length" v-bind="commaValueProps">{{ commaText }}</span>
@@ -794,7 +804,7 @@ const bodyProps = computed(() => ({
794
804
  :aria-invalid="selectInvalid || void 0"
795
805
  :aria-describedby="describedBy"
796
806
  :aria-busy="loading || void 0"
797
- v-bind="mergeProps(triggerProps, modalTriggerAttrs)"
807
+ v-bind="mergeProps(adornedTriggerProps(), modalTriggerAttrs)"
798
808
  tabindex="0"
799
809
  @keydown="onTriggerKeydown"
800
810
  @blur="selectedChipValue = void 0"
@@ -894,6 +904,9 @@ const bodyProps = computed(() => ({
894
904
  </template>
895
905
  <span v-if="loading" class="sr-only">{{ messages.loading }}</span>
896
906
  </ComboboxTrigger>
907
+ <fieldset v-if="slots.trailing" ref="trailing" :disabled="disabled" :inert="disabled || void 0" v-bind="resolveSlot(ui.trailing, props.ui?.trailing)">
908
+ <slot name="trailing" v-bind="adornmentScope" />
909
+ </fieldset>
897
910
  </ComboboxAnchor>
898
911
 
899
912
  <ComboboxPortal v-if="!showMobileModal" v-bind="portalProps">
@@ -2,12 +2,18 @@ import type { VariantProps } from 'tailwind-variants';
2
2
  import type { SelectThemeSlots } from '../theme/select.js';
3
3
  import type { RoundedArrowConfig } from '../utils/arrow.js';
4
4
  import type { ColorRole } from '../utils/color-registry.js';
5
+ import type { FieldAdornmentSlotProps } from '../utils/field-adornments.js';
5
6
  import type { OverlayPortal, OverlayPositioning } from '../utils/overlay.js';
6
7
  import type { SelectModelValue } from '../utils/select-contracts.js';
7
8
  import type { UiProp } from '../utils/ui.js';
8
- import type { SelectItems, SelectOption } from './combobox-select.js';
9
+ import type { SelectItems } from './combobox-select.js';
9
10
  import { selectTheme } from '../theme/select.js';
10
11
  type SelectVariants = VariantProps<typeof selectTheme>;
12
+ type __VLS_Slots = {
13
+ leading?: (props: FieldAdornmentSlotProps) => any;
14
+ trailing?: (props: FieldAdornmentSlotProps) => any;
15
+ [name: string]: ((props: any) => any) | undefined;
16
+ };
11
17
  export interface ComboboxSelectBaseProps {
12
18
  id?: string;
13
19
  name?: string;
@@ -58,68 +64,6 @@ export interface ComboboxSelectBaseEmits {
58
64
  'update:modelValue': [value: SelectModelValue | SelectModelValue[] | undefined];
59
65
  'update:searchTerm': [value: string];
60
66
  }
61
- declare var __VLS_43: {
62
- item: SelectModelValue | SelectOption<SelectModelValue>;
63
- }, __VLS_98: {}, __VLS_105: {}, __VLS_120: {}, __VLS_136: {}, __VLS_143: {}, __VLS_150: {}, __VLS_174: {
64
- item: SelectModelValue | SelectOption<SelectModelValue>;
65
- }, __VLS_194: {
66
- selected: import("./combobox-select.js").ResolvedOption | undefined;
67
- }, __VLS_205: {}, __VLS_212: {}, __VLS_219: {}, __VLS_249: {}, __VLS_252: {}, __VLS_255: {}, __VLS_258: {}, __VLS_261: {
68
- item: SelectModelValue | SelectOption<SelectModelValue>;
69
- }, __VLS_264: {
70
- group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
71
- }, __VLS_267: {}, __VLS_305: string, __VLS_306: {}, __VLS_309: {}, __VLS_319: {
72
- item: SelectModelValue | SelectOption<SelectModelValue>;
73
- }, __VLS_322: {
74
- group: import("./combobox-select.js").SelectOptionGroup<SelectModelValue>;
75
- };
76
- type __VLS_Slots = {} & {
77
- [K in NonNullable<typeof __VLS_305>]?: (props: typeof __VLS_306) => any;
78
- } & {
79
- item?: (props: typeof __VLS_43) => any;
80
- } & {
81
- 'clear-icon'?: (props: typeof __VLS_98) => any;
82
- } & {
83
- 'loading-icon'?: (props: typeof __VLS_105) => any;
84
- } & {
85
- 'dropdown-icon'?: (props: typeof __VLS_120) => any;
86
- } & {
87
- 'clear-icon'?: (props: typeof __VLS_136) => any;
88
- } & {
89
- 'loading-icon'?: (props: typeof __VLS_143) => any;
90
- } & {
91
- 'dropdown-icon'?: (props: typeof __VLS_150) => any;
92
- } & {
93
- item?: (props: typeof __VLS_174) => any;
94
- } & {
95
- value?: (props: typeof __VLS_194) => any;
96
- } & {
97
- 'clear-icon'?: (props: typeof __VLS_205) => any;
98
- } & {
99
- 'loading-icon'?: (props: typeof __VLS_212) => any;
100
- } & {
101
- 'dropdown-icon'?: (props: typeof __VLS_219) => any;
102
- } & {
103
- header?: (props: typeof __VLS_249) => any;
104
- } & {
105
- 'filter-icon'?: (props: typeof __VLS_252) => any;
106
- } & {
107
- empty?: (props: typeof __VLS_255) => any;
108
- } & {
109
- 'empty-filter'?: (props: typeof __VLS_258) => any;
110
- } & {
111
- item?: (props: typeof __VLS_261) => any;
112
- } & {
113
- group?: (props: typeof __VLS_264) => any;
114
- } & {
115
- footer?: (props: typeof __VLS_267) => any;
116
- } & {
117
- header?: (props: typeof __VLS_309) => any;
118
- } & {
119
- item?: (props: typeof __VLS_319) => any;
120
- } & {
121
- group?: (props: typeof __VLS_322) => any;
122
- };
123
67
  declare const __VLS_base: import("vue").DefineComponent<ComboboxSelectBaseProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
124
68
  "update:modelValue": (value: SelectModelValue | SelectModelValue[] | undefined) => any;
125
69
  "update:open": (value: boolean) => any;
@@ -42,6 +42,8 @@ export declare const inputTheme: import("tailwind-variants").TVReturnType<{
42
42
  }, {
43
43
  root: string;
44
44
  base: string;
45
+ leading: string;
46
+ trailing: string;
45
47
  leadingIcon: string;
46
48
  trailingIcon: string;
47
49
  clear: string;
@@ -89,6 +91,8 @@ export declare const inputTheme: import("tailwind-variants").TVReturnType<{
89
91
  }, {
90
92
  root: string;
91
93
  base: string;
94
+ leading: string;
95
+ trailing: string;
92
96
  leadingIcon: string;
93
97
  trailingIcon: string;
94
98
  clear: string;
@@ -136,6 +140,8 @@ export declare const inputTheme: import("tailwind-variants").TVReturnType<{
136
140
  }, {
137
141
  root: string;
138
142
  base: string;
143
+ leading: string;
144
+ trailing: string;
139
145
  leadingIcon: string;
140
146
  trailingIcon: string;
141
147
  clear: string;
@@ -10,6 +10,8 @@ export const inputTheme = tv({
10
10
  // hardcoded here - hover's own color stays neutral regardless of
11
11
  // `color`, matching every other component's own hover state.
12
12
  base: "w-full 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)] placeholder:text-[var(--selaras-resolved-text-muted)] outline-none transition-[color,background-color,box-shadow] not-focus:hover:ring-[var(--selaras-resolved-border-hover)] hover:bg-[var(--selaras-resolved-surface-elevated)] focus:ring-2 focus:ring-[var(--_selaras-color-focus)] disabled:opacity-50 disabled:pointer-events-none",
13
+ leading: "absolute top-1/2 -translate-y-1/2 start-0 m-0 flex min-w-0 items-center gap-1 border-0 p-0 ps-2.5 text-sm text-[var(--selaras-resolved-text-muted)] disabled:opacity-50",
14
+ trailing: "absolute top-1/2 -translate-y-1/2 end-0 m-0 flex min-w-0 items-center gap-1 border-0 p-0 pe-1 text-sm text-[var(--selaras-resolved-text-muted)] disabled:opacity-50",
13
15
  leadingIcon: "absolute start-2.5 shrink-0 text-[var(--selaras-resolved-text-muted)]",
14
16
  trailingIcon: "absolute end-2.5 shrink-0 text-[var(--selaras-resolved-text-muted)]",
15
17
  // A separate slot from trailingIcon (not reused) - that one is a
@@ -29,6 +29,8 @@ export declare const selectTheme: import("tailwind-variants").TVReturnType<{
29
29
  }, {
30
30
  root: string;
31
31
  trigger: string;
32
+ leading: string;
33
+ trailing: string;
32
34
  value: string;
33
35
  chipOverflow: string;
34
36
  icon: string;
@@ -76,6 +78,8 @@ export declare const selectTheme: import("tailwind-variants").TVReturnType<{
76
78
  }, {
77
79
  root: string;
78
80
  trigger: string;
81
+ leading: string;
82
+ trailing: string;
79
83
  value: string;
80
84
  chipOverflow: string;
81
85
  icon: string;
@@ -123,6 +127,8 @@ export declare const selectTheme: import("tailwind-variants").TVReturnType<{
123
127
  }, {
124
128
  root: string;
125
129
  trigger: string;
130
+ leading: string;
131
+ trailing: string;
126
132
  value: string;
127
133
  chipOverflow: string;
128
134
  icon: string;