@leaflink/stash 50.2.0 → 50.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"Field.vue_vue_type_script_setup_true_lang-DEizIcDo.js","sources":["../src/components/Field/Field.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, useAttrs, useSlots } from 'vue';\n\n import Label from '../Label/Label.vue';\n\n defineOptions({\n inheritAttrs: false,\n });\n\n export interface FieldProps {\n /**\n * Adds spacing under the field that is consistent whether hint/error text is displayed.\n */\n addBottomSpace?: boolean;\n\n /**\n * Error text to display. Replaces `hintText` (if provided) & adds error styling.\n */\n errorText?: string;\n\n /**\n * Displays text below the input; hidden when the isReadOnly prop is truthy.\n */\n hintText?: string;\n\n /**\n * ID for the Label and Input; must be unique\n */\n id?: string;\n\n /**\n * ID for the error text element; useful for aria-errormessage\n */\n errorId?: string;\n\n /**\n * Whether it's a readonly field.\n */\n isReadOnly?: boolean;\n\n /**\n * Whether the field is required.\n */\n isRequired?: boolean;\n\n /**\n * Label to render above the input.\n */\n label?: string;\n\n /**\n * Show \"(optional)\" to the right of the label text\n */\n showOptionalInLabel?: boolean;\n\n /**\n * Indicates wheter the field is a fieldset or not\n */\n fieldset?: boolean;\n\n /**\n * Indicates whether the field is disabled.\n */\n isDisabled?: boolean;\n\n /**\n * Indicates whether the field is disabled.\n *\n * @deprecated Use `isDisabled` instead.\n */\n disabled?: boolean;\n }\n\n const props = withDefaults(defineProps<FieldProps>(), {\n addBottomSpace: false,\n errorText: undefined,\n hintText: undefined,\n id: undefined,\n errorId: undefined,\n isRequired: false,\n label: undefined,\n showOptionalInLabel: false,\n fieldset: false,\n isDisabled: false,\n disabled: false,\n });\n const attrs = useAttrs();\n const slots = useSlots();\n const fieldId = computed(() => props.id || uniqueId('stash-field-'));\n const fieldErrorId = computed(() => props.errorId || uniqueId('stash-field-error-'));\n const labelId = computed(() => uniqueId('stash-field-label-'));\n const hasError = computed(() => !!props.errorText);\n const wrapperElement = computed(() => (props.fieldset ? 'fieldset' : 'div'));\n\n // Any attributes that are unique to form elements, you want to exclude from\n // being bound from the root element.\n const rootAttrs = computed(() => {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { placeholder, ...otherAttrs } = attrs;\n\n return otherAttrs;\n });\n</script>\n\n<template>\n <component\n :is=\"wrapperElement\"\n data-test=\"stash-field\"\n class=\"stash-field\"\n :class=\"[\n { 'tw-p-0': props.fieldset },\n { 'tw-mb-9': props.addBottomSpace && !props.errorText && !props.hintText && !slots.hint },\n { 'tw-mb-4': props.addBottomSpace && (props.errorText || props.hintText || slots.hint) },\n { 'stash-field--disabled': props.isDisabled || props.disabled },\n { 'stash-field--is-read-only': props.isReadOnly },\n { 'stash-field--is-required': props.isRequired },\n { 'stash-field--has-error': hasError },\n ]\"\n v-bind=\"rootAttrs\"\n >\n <Label\n v-if=\"props.label\"\n :id=\"labelId\"\n :for=\"fieldId\"\n :has-error=\"hasError\"\n :is-required=\"isRequired\"\n :show-optional=\"props.showOptionalInLabel\"\n :legend=\"props.fieldset\"\n :disabled=\"props.isDisabled || props.disabled\"\n >\n {{ props.label }}\n </Label>\n\n <!-- @slot for the form field; the Label can also be rendered here instead of using the label prop -->\n <slot\n :field-id=\"fieldId\"\n :field-error-id=\"fieldErrorId\"\n :has-error=\"hasError\"\n :is-required=\"isRequired\"\n :is-read-only=\"props.isReadOnly\"\n :disabled=\"props.isDisabled || props.disabled\"\n :label-id=\"labelId\"\n :show-optional-in-label=\"props.showOptionalInLabel\"\n ></slot>\n\n <span\n v-if=\"props.errorText\"\n :id=\"fieldErrorId\"\n class=\"stash-field-error tw-mt-1 tw-block tw-whitespace-pre-line tw-text-xs tw-text-red-500\"\n data-test=\"stash-field-error\"\n >\n {{ props.errorText }}\n </span>\n\n <span\n v-else-if=\"props.hintText && !props.isReadOnly\"\n class=\"stash-field-hint tw-mt-1 tw-block tw-whitespace-pre-line tw-text-xs\"\n data-test=\"stash-field-hint\"\n >\n {{ props.hintText }}\n </span>\n\n <div\n v-else-if=\"slots.hint && !props.isReadOnly\"\n class=\"stash-field-hint tw-mt-1 tw-whitespace-pre-line tw-text-xs\"\n data-test=\"stash-field-hint\"\n >\n <!-- @slot for displaying hint text below the field -->\n <slot name=\"hint\"></slot>\n </div>\n </component>\n</template>\n"],"names":["props","__props","attrs","useAttrs","slots","useSlots","fieldId","computed","uniqueId","fieldErrorId","labelId","hasError","wrapperElement","rootAttrs","placeholder","otherAttrs"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EE,UAAMA,IAAQC,GAaRC,IAAQC,KACRC,IAAQC,KACRC,IAAUC,EAAS,MAAMP,EAAM,MAAMQ,EAAS,cAAc,CAAC,GAC7DC,IAAeF,EAAS,MAAMP,EAAM,WAAWQ,EAAS,oBAAoB,CAAC,GAC7EE,IAAUH,EAAS,MAAMC,EAAS,oBAAoB,CAAC,GACvDG,IAAWJ,EAAS,MAAM,CAAC,CAACP,EAAM,SAAS,GAC3CY,IAAiBL,EAAS,MAAOP,EAAM,WAAW,aAAa,KAAM,GAIrEa,IAAYN,EAAS,MAAM;AAE/B,YAAM,EAAE,aAAAO,GAAa,GAAGC,EAAA,IAAeb;AAEhC,aAAAa;AAAA,IAAA,CACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"Field.vue_vue_type_script_setup_true_lang-DEizIcDo.js","sources":["../src/components/Field/Field.vue"],"sourcesContent":["<script lang=\"ts\">\n import { FieldProps } from './Field.types';\n\n export * from './Field.types';\n</script>\n\n<script setup lang=\"ts\">\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, useAttrs, useSlots } from 'vue';\n\n import Label from '../Label/Label.vue';\n\n defineOptions({\n inheritAttrs: false,\n });\n\n const props = withDefaults(defineProps<FieldProps>(), {\n addBottomSpace: false,\n errorText: undefined,\n hintText: undefined,\n id: undefined,\n errorId: undefined,\n isRequired: false,\n label: undefined,\n showOptionalInLabel: false,\n fieldset: false,\n isDisabled: false,\n disabled: false,\n });\n const attrs = useAttrs();\n const slots = useSlots();\n const fieldId = computed(() => props.id || uniqueId('stash-field-'));\n const fieldErrorId = computed(() => props.errorId || uniqueId('stash-field-error-'));\n const labelId = computed(() => uniqueId('stash-field-label-'));\n const hasError = computed(() => !!props.errorText);\n const wrapperElement = computed(() => (props.fieldset ? 'fieldset' : 'div'));\n\n // Any attributes that are unique to form elements, you want to exclude from\n // being bound from the root element.\n const rootAttrs = computed(() => {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { placeholder, ...otherAttrs } = attrs;\n\n return otherAttrs;\n });\n</script>\n\n<template>\n <component\n :is=\"wrapperElement\"\n data-test=\"stash-field\"\n class=\"stash-field\"\n :class=\"[\n { 'tw-p-0': props.fieldset },\n { 'tw-mb-9': props.addBottomSpace && !props.errorText && !props.hintText && !slots.hint },\n { 'tw-mb-4': props.addBottomSpace && (props.errorText || props.hintText || slots.hint) },\n { 'stash-field--disabled': props.isDisabled || props.disabled },\n { 'stash-field--is-read-only': props.isReadOnly },\n { 'stash-field--is-required': props.isRequired },\n { 'stash-field--has-error': hasError },\n ]\"\n v-bind=\"rootAttrs\"\n >\n <Label\n v-if=\"props.label\"\n :id=\"labelId\"\n :for=\"fieldId\"\n :has-error=\"hasError\"\n :is-required=\"isRequired\"\n :show-optional=\"props.showOptionalInLabel\"\n :legend=\"props.fieldset\"\n :disabled=\"props.isDisabled || props.disabled\"\n >\n {{ props.label }}\n </Label>\n\n <!-- @slot for the form field; the Label can also be rendered here instead of using the label prop -->\n <slot\n :field-id=\"fieldId\"\n :field-error-id=\"fieldErrorId\"\n :has-error=\"hasError\"\n :is-required=\"isRequired\"\n :is-read-only=\"props.isReadOnly\"\n :disabled=\"props.isDisabled || props.disabled\"\n :label-id=\"labelId\"\n :show-optional-in-label=\"props.showOptionalInLabel\"\n ></slot>\n\n <span\n v-if=\"props.errorText\"\n :id=\"fieldErrorId\"\n class=\"stash-field-error tw-mt-1 tw-block tw-whitespace-pre-line tw-text-xs tw-text-red-500\"\n data-test=\"stash-field-error\"\n >\n {{ props.errorText }}\n </span>\n\n <span\n v-else-if=\"props.hintText && !props.isReadOnly\"\n class=\"stash-field-hint tw-mt-1 tw-block tw-whitespace-pre-line tw-text-xs\"\n data-test=\"stash-field-hint\"\n >\n {{ props.hintText }}\n </span>\n\n <div\n v-else-if=\"slots.hint && !props.isReadOnly\"\n class=\"stash-field-hint tw-mt-1 tw-whitespace-pre-line tw-text-xs\"\n data-test=\"stash-field-hint\"\n >\n <!-- @slot for displaying hint text below the field -->\n <slot name=\"hint\"></slot>\n </div>\n </component>\n</template>\n"],"names":["props","__props","attrs","useAttrs","slots","useSlots","fieldId","computed","uniqueId","fieldErrorId","labelId","hasError","wrapperElement","rootAttrs","placeholder","otherAttrs"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBE,UAAMA,IAAQC,GAaRC,IAAQC,KACRC,IAAQC,KACRC,IAAUC,EAAS,MAAMP,EAAM,MAAMQ,EAAS,cAAc,CAAC,GAC7DC,IAAeF,EAAS,MAAMP,EAAM,WAAWQ,EAAS,oBAAoB,CAAC,GAC7EE,IAAUH,EAAS,MAAMC,EAAS,oBAAoB,CAAC,GACvDG,IAAWJ,EAAS,MAAM,CAAC,CAACP,EAAM,SAAS,GAC3CY,IAAiBL,EAAS,MAAOP,EAAM,WAAW,aAAa,KAAM,GAIrEa,IAAYN,EAAS,MAAM;AAE/B,YAAM,EAAE,aAAAO,GAAa,GAAGC,EAAA,IAAeb;AAEhC,aAAAa;AAAA,IAAA,CACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"FilterSelect.js","sources":["../src/components/FilterSelect/FilterSelect.vue"],"sourcesContent":["<script lang=\"ts\">\n export * from './FilterSelect.types';\n</script>\n\n<script lang=\"ts\" setup>\n import { computed } from 'vue';\n\n import { t } from '../../locale';\n import Field, { type FieldProps } from '../Field/Field.vue';\n import FilterChip from '../FilterChip/FilterChip.vue';\n import { FilterSelectModelValue, FilterSelectOption } from './FilterSelect.types';\n\n export interface FilterSelectProps extends FieldProps {\n /**\n * The name attribute for the `<input>` elements. It is also used as a unique field to differentiate FilterSelect instances.\n */\n name: string;\n\n options: FilterSelectOption[];\n\n modelValue: FilterSelectModelValue;\n\n /**\n * When true, this prop adds an \"All\" option for selecting all options.\n */\n canSelectAll?: boolean;\n }\n\n const props = withDefaults(defineProps<FilterSelectProps>(), {\n canSelectAll: false,\n });\n\n const emit = defineEmits<{\n (e: 'update:modelValue', value: FilterSelectModelValue): void;\n }>();\n\n /**\n * @see: https://vuejs.org/guide/components/v-model.html#component-v-model\n */\n const selected = computed({\n get() {\n return props.modelValue;\n },\n set(value) {\n emit('update:modelValue', value);\n },\n });\n\n const isAllSelected = computed({\n get() {\n return props.options.length === props.modelValue.length;\n },\n set(isChecked) {\n emit('update:modelValue', isChecked ? props.options.map((option) => option.value) : []);\n },\n });\n\n function getOptionId(option: FilterSelectOption) {\n return `${props.name}--${option.value}`;\n }\n\n function isOptionSelected(option: FilterSelectOption) {\n return props.modelValue.includes(option.value);\n }\n\n function onEnter(enteredValue: FilterSelectOption['value']) {\n const newSelected = selected.value.filter((value) => value !== enteredValue);\n\n if (newSelected.length === selected.value.length) {\n newSelected.push(enteredValue);\n }\n\n selected.value = newSelected;\n }\n\n function onEnterAll() {\n isAllSelected.value = !isAllSelected.value;\n }\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-filter-select\" data-test=\"stash-filter-select\" fieldset>\n <div class=\"tw-flex tw-flex-wrap tw-gap-1.5\">\n <div v-if=\"props.canSelectAll\" :key=\"`${props.name}--all`\">\n <input\n :id=\"`${props.name}--all`\"\n v-model=\"isAllSelected\"\n class=\"tw-sr-only\"\n tabindex=\"-1\"\n type=\"checkbox\"\n value=\"all\"\n />\n <label :for=\"`${props.name}--all`\">\n <FilterChip :is-selected=\"isAllSelected\" tabindex=\"0\" @keypress.enter=\"onEnterAll\">\n {{ t('ll.all') }}\n </FilterChip>\n </label>\n </div>\n <div v-for=\"option in props.options\" :key=\"getOptionId(option)\">\n <input\n :id=\"getOptionId(option)\"\n v-model=\"selected\"\n class=\"tw-sr-only\"\n tabindex=\"-1\"\n type=\"checkbox\"\n :value=\"option.value\"\n />\n <label :for=\"getOptionId(option)\">\n <FilterChip :is-selected=\"isOptionSelected(option)\" tabindex=\"0\" @keypress.enter=\"onEnter(option.value)\">\n {{ option.label }}\n </FilterChip>\n </label>\n </div>\n </div>\n </Field>\n</template>\n"],"names":["props","__props","emit","__emit","selected","computed","value","isAllSelected","isChecked","option","getOptionId","isOptionSelected","onEnter","enteredValue","newSelected","onEnterAll"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA4BE,UAAMA,IAAQC,GAIRC,IAAOC,GAOPC,IAAWC,EAAS;AAAA,MACxB,MAAM;AACJ,eAAOL,EAAM;AAAA,MACf;AAAA,MACA,IAAIM,GAAO;AACT,QAAAJ,EAAK,qBAAqBI,CAAK;AAAA,MACjC;AAAA,IAAA,CACD,GAEKC,IAAgBF,EAAS;AAAA,MAC7B,MAAM;AACJ,eAAOL,EAAM,QAAQ,WAAWA,EAAM,WAAW;AAAA,MACnD;AAAA,MACA,IAAIQ,GAAW;AACR,QAAAN,EAAA,qBAAqBM,IAAYR,EAAM,QAAQ,IAAI,CAACS,MAAWA,EAAO,KAAK,IAAI,CAAE,CAAA;AAAA,MACxF;AAAA,IAAA,CACD;AAED,aAASC,EAAYD,GAA4B;AAC/C,aAAO,GAAGT,EAAM,IAAI,KAAKS,EAAO,KAAK;AAAA,IACvC;AAEA,aAASE,EAAiBF,GAA4B;AACpD,aAAOT,EAAM,WAAW,SAASS,EAAO,KAAK;AAAA,IAC/C;AAEA,aAASG,EAAQC,GAA2C;AAC1D,YAAMC,IAAcV,EAAS,MAAM,OAAO,CAACE,MAAUA,MAAUO,CAAY;AAE3E,MAAIC,EAAY,WAAWV,EAAS,MAAM,UACxCU,EAAY,KAAKD,CAAY,GAG/BT,EAAS,QAAQU;AAAA,IACnB;AAEA,aAASC,IAAa;AACN,MAAAR,EAAA,QAAQ,CAACA,EAAc;AAAA,IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"FilterSelect.js","sources":["../src/components/FilterSelect/FilterSelect.vue"],"sourcesContent":["<script lang=\"ts\">\n export * from './FilterSelect.types';\n</script>\n\n<script lang=\"ts\" setup>\n import { computed } from 'vue';\n\n import { t } from '../../locale';\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n import FilterChip from '../FilterChip/FilterChip.vue';\n import { FilterSelectModelValue, FilterSelectOption } from './FilterSelect.types';\n\n export interface FilterSelectProps extends FieldProps {\n /**\n * The name attribute for the `<input>` elements. It is also used as a unique field to differentiate FilterSelect instances.\n */\n name: string;\n\n options: FilterSelectOption[];\n\n modelValue: FilterSelectModelValue;\n\n /**\n * When true, this prop adds an \"All\" option for selecting all options.\n */\n canSelectAll?: boolean;\n }\n\n const props = withDefaults(defineProps<FilterSelectProps>(), {\n canSelectAll: false,\n });\n\n const emit = defineEmits<{\n (e: 'update:modelValue', value: FilterSelectModelValue): void;\n }>();\n\n /**\n * @see: https://vuejs.org/guide/components/v-model.html#component-v-model\n */\n const selected = computed({\n get() {\n return props.modelValue;\n },\n set(value) {\n emit('update:modelValue', value);\n },\n });\n\n const isAllSelected = computed({\n get() {\n return props.options.length === props.modelValue.length;\n },\n set(isChecked) {\n emit('update:modelValue', isChecked ? props.options.map((option) => option.value) : []);\n },\n });\n\n function getOptionId(option: FilterSelectOption) {\n return `${props.name}--${option.value}`;\n }\n\n function isOptionSelected(option: FilterSelectOption) {\n return props.modelValue.includes(option.value);\n }\n\n function onEnter(enteredValue: FilterSelectOption['value']) {\n const newSelected = selected.value.filter((value) => value !== enteredValue);\n\n if (newSelected.length === selected.value.length) {\n newSelected.push(enteredValue);\n }\n\n selected.value = newSelected;\n }\n\n function onEnterAll() {\n isAllSelected.value = !isAllSelected.value;\n }\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-filter-select\" data-test=\"stash-filter-select\" fieldset>\n <div class=\"tw-flex tw-flex-wrap tw-gap-1.5\">\n <div v-if=\"props.canSelectAll\" :key=\"`${props.name}--all`\">\n <input\n :id=\"`${props.name}--all`\"\n v-model=\"isAllSelected\"\n class=\"tw-sr-only\"\n tabindex=\"-1\"\n type=\"checkbox\"\n value=\"all\"\n />\n <label :for=\"`${props.name}--all`\">\n <FilterChip :is-selected=\"isAllSelected\" tabindex=\"0\" @keypress.enter=\"onEnterAll\">\n {{ t('ll.all') }}\n </FilterChip>\n </label>\n </div>\n <div v-for=\"option in props.options\" :key=\"getOptionId(option)\">\n <input\n :id=\"getOptionId(option)\"\n v-model=\"selected\"\n class=\"tw-sr-only\"\n tabindex=\"-1\"\n type=\"checkbox\"\n :value=\"option.value\"\n />\n <label :for=\"getOptionId(option)\">\n <FilterChip :is-selected=\"isOptionSelected(option)\" tabindex=\"0\" @keypress.enter=\"onEnter(option.value)\">\n {{ option.label }}\n </FilterChip>\n </label>\n </div>\n </div>\n </Field>\n</template>\n"],"names":["props","__props","emit","__emit","selected","computed","value","isAllSelected","isChecked","option","getOptionId","isOptionSelected","onEnter","enteredValue","newSelected","onEnterAll"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6BE,UAAMA,IAAQC,GAIRC,IAAOC,GAOPC,IAAWC,EAAS;AAAA,MACxB,MAAM;AACJ,eAAOL,EAAM;AAAA,MACf;AAAA,MACA,IAAIM,GAAO;AACT,QAAAJ,EAAK,qBAAqBI,CAAK;AAAA,MACjC;AAAA,IAAA,CACD,GAEKC,IAAgBF,EAAS;AAAA,MAC7B,MAAM;AACJ,eAAOL,EAAM,QAAQ,WAAWA,EAAM,WAAW;AAAA,MACnD;AAAA,MACA,IAAIQ,GAAW;AACR,QAAAN,EAAA,qBAAqBM,IAAYR,EAAM,QAAQ,IAAI,CAACS,MAAWA,EAAO,KAAK,IAAI,CAAE,CAAA;AAAA,MACxF;AAAA,IAAA,CACD;AAED,aAASC,EAAYD,GAA4B;AAC/C,aAAO,GAAGT,EAAM,IAAI,KAAKS,EAAO,KAAK;AAAA,IACvC;AAEA,aAASE,EAAiBF,GAA4B;AACpD,aAAOT,EAAM,WAAW,SAASS,EAAO,KAAK;AAAA,IAC/C;AAEA,aAASG,EAAQC,GAA2C;AAC1D,YAAMC,IAAcV,EAAS,MAAM,OAAO,CAACE,MAAUA,MAAUO,CAAY;AAE3E,MAAIC,EAAY,WAAWV,EAAS,MAAM,UACxCU,EAAY,KAAKD,CAAY,GAG/BT,EAAS,QAAQU;AAAA,IACnB;AAEA,aAASC,IAAa;AACN,MAAAR,EAAA,QAAQ,CAACA,EAAc;AAAA,IACvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/Input.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Input.js","sources":["../src/components/Input/Input.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import isNil from 'lodash-es/isNil';\n import { computed, onMounted, ref, useAttrs, useCssModule, useSlots, watchEffect } from 'vue';\n\n import { convertDecimal, decimalSeparator, sanitizeDecimal } from '../../utils/i18n';\n import Field, { type FieldProps } from '../Field/Field.vue';\n import Icon from '../Icon/Icon.vue';\n\n export type InputValue = string | number | undefined;\n\n export interface InputProps extends FieldProps {\n /**\n * Autocomplete takes in a string of off or on\n */\n autocomplete?: string;\n\n /**\n * Value for the input element.\n */\n modelValue?: string | number;\n\n /**\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: string | number | null;\n\n /**\n * Input type. Excludes certain types that have a dedicated component.\n *\n * Note: For distinguishing between text & number internally, passing `number`\n * will still render an input with a type of `text` (for localization).\n */\n type?: string extends 'button' | 'checkbox' | 'radio' | 'submit' ? never : string;\n\n /**\n * Placeholder text for the input.\n * **Note:** placeholders should be used to display examples; they should not be used as labels because they are not accessible as labels. If a real label cannot be used, use the `aria-label` attribute.\n */\n placeholder?: string;\n }\n\n defineOptions({\n name: 'll-input',\n inheritAttrs: false,\n });\n\n const props = withDefaults(defineProps<InputProps>(), {\n autocomplete: 'off',\n type: 'text',\n modelValue: '',\n value: null,\n placeholder: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the input value changes.\n */\n (e: 'update:model-value', v: string | number): void;\n /**\n * Emitted when the input value changes.\n */\n (e: 'change', v: string | number): void;\n /**\n * Emitted when the input is focused\n */\n (e: 'focus', evt: Event): void;\n /**\n * Emitted when the input is blurred\n */\n (e: 'blur', evt: Event): void;\n }>();\n\n const slots = useSlots();\n const classes = useCssModule();\n const attrs = useAttrs();\n\n // declare a ref to hold the element reference\n // the name must match template ref value\n const inputEl = ref<HTMLInputElement>();\n\n // this exposes the input ref to any parent that renders the `Input` component\n // and gives it a template ref, giving parent components access to the DOM element.\n defineExpose({ inputEl });\n\n const internalValue = ref<string | number>(props.modelValue);\n\n if (props.type === 'number' && (props.modelValue || props.modelValue === 0)) {\n internalValue.value = convertDecimal(props.modelValue, decimalSeparator);\n }\n\n const showPassword = ref(false);\n\n const isReadOnly = computed(() => props.isReadOnly || ('readonly' in attrs && attrs.readonly !== false));\n const isNumber = computed(() => props.type === 'number');\n\n const inputAttrs = computed(() => {\n const tempAttrs = { ...attrs };\n\n delete tempAttrs.class;\n\n return tempAttrs;\n });\n\n const inputType = computed(() => {\n // type 'number' is a special use case and requires\n // that the type be 'text' to support internationalization\n if (isNumber.value) {\n return 'text';\n }\n\n if (props.type === 'password' && showPassword.value) {\n return 'text';\n }\n\n return props.type;\n });\n\n watchEffect(() => {\n internalValue.value = format(props.modelValue);\n });\n\n /**\n * Converts the localized number to a valid number (to emit). i.e. 12,50 > 12.50\n *\n * @param {string|number} value - Number you want to parse.\n * @returns {number|string} number or empty string (for empty input)\n */\n function parseNumber(value: string | number = '', isUserTyping = false): string | number {\n // If this isn't a number input, we shouldn't be calling this function\n if (!isNumber.value) {\n return value;\n }\n\n let tempValue = value;\n\n // If blank, just stop\n if (isNil(tempValue) || `${tempValue}`.length === 0) return '';\n\n // clean out different locale decimals\n if (decimalSeparator !== '.') {\n tempValue = convertDecimal(tempValue, '.');\n }\n\n // If the user is in the middle of an input, let them finish the number\n if (isUserTyping) {\n // Prefix values with a 0 if the user starts with a decimal point\n if (tempValue === '.') tempValue = '0.';\n\n // This is to prevent the parsing from correcting 0. to 0 and causing the value to be reset\n if (tempValue.toString().startsWith('0.')) return tempValue;\n }\n\n // Empty or null values convert to 0 with NumberFormat, so return '';\n if (!isNil(tempValue) && `${tempValue}`.length) {\n tempValue = Intl.NumberFormat('en-US', {\n style: 'decimal',\n maximumFractionDigits: 7,\n useGrouping: false,\n }).format(Number(tempValue));\n } else {\n return '';\n }\n\n // Ensure valid number, otherwise clear value\n return isNaN(Number(tempValue)) ? '' : parseFloat(tempValue);\n }\n\n /**\n * Formats the input based on conditions\n *\n * @param {number|string} value - The value to format.\n */\n function format(value: string | number) {\n if (isNumber.value) {\n return sanitizeDecimal(value, decimalSeparator);\n }\n\n return value;\n }\n\n /**\n * Fire after focusing out of input, if value has changed.\n */\n function handleChange() {\n // Parse the final value on blur\n const parsedValue = isNumber.value ? parseNumber(internalValue.value) : internalValue.value;\n emit('change', parsedValue);\n }\n\n /**\n * Fired when typing on input.\n */\n function handleInput(e: Event) {\n const value = (e.target as HTMLInputElement).value;\n\n // Update the internal value with a sanitized version\n internalValue.value = format(value);\n\n const parsedValue = isNumber.value ? parseNumber(internalValue.value, true) : value;\n\n emit('update:model-value', parsedValue);\n }\n\n onMounted(() => {\n if (props.value !== null) {\n throw new Error('ll-input: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-input: use the @update:model-value event instead of @input');\n }\n });\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-input\" :class=\"[classes.root, attrs.class]\" data-test=\"ll-input\">\n <template #default=\"{ fieldId, fieldErrorId, hasError }\">\n <div class=\"tw-relative\">\n <input\n v-bind=\"inputAttrs\"\n :id=\"fieldId\"\n ref=\"inputEl\"\n v-model=\"internalValue\"\n :aria-errormessage=\"fieldErrorId\"\n :aria-invalid=\"hasError\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"props.placeholder\"\n :class=\"[\n { [classes['input-prepended']]: !!slots.prepend },\n { [classes['input-appended']]: !!slots.append || props.type === 'password' },\n { [classes['has-error']]: !!props.errorText },\n ]\"\n :type=\"inputType\"\n :disabled=\"props.isDisabled || props.disabled\"\n :readonly=\"isReadOnly\"\n @change=\"handleChange\"\n @input=\"handleInput\"\n @blur=\"(evt) => emit('blur', evt)\"\n @focus=\"(evt) => emit('focus', evt)\"\n />\n\n <div\n v-if=\"slots.prepend\"\n class=\"stash-input-prepend font-semibold\"\n :class=\"[classes.symbol, classes['symbol--prepend']]\"\n >\n <!-- @slot renders content on the left side of the input -->\n <slot name=\"prepend\"> </slot>\n </div>\n\n <div\n v-if=\"slots.append || props.type === 'password'\"\n class=\"stash-input-append font-semibold\"\n :class=\"[classes.symbol, classes['symbol--append']]\"\n >\n <!-- @slot renders content on the right side of the input -->\n <slot v-if=\"slots.append\" name=\"append\"></slot>\n <Icon\n v-else\n :name=\"showPassword ? 'hide' : 'show'\"\n class=\"tw-cursor-pointer\"\n :class=\"{ 'tw-text-ice-500': props.isDisabled || props.disabled }\"\n :data-test=\"showPassword ? 'hide-password-icon' : 'show-password-icon'\"\n @click=\"showPassword = !showPassword\"\n />\n </div>\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .root {\n position: relative;\n }\n\n .root input {\n background: var(--color-white);\n border: 1px solid var(--color-ice-500);\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n height: theme('height.input');\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n outline: none;\n padding: 0 theme('spacing.3');\n width: 100%;\n }\n\n .root input:hover {\n border-color: var(--color-ice-500);\n }\n\n .root input:active,\n .root input:focus {\n border-color: var(--color-blue-500);\n }\n\n .root input::placeholder {\n color: var(--color-ice-500);\n opacity: 1;\n }\n\n .root input.has-error:not(:focus) {\n border-color: var(--color-red-500);\n }\n\n .root input.input-prepended {\n padding-left: 36px;\n }\n\n .root input.input-appended {\n padding-right: 36px;\n }\n\n .symbol {\n align-items: center;\n display: flex;\n height: theme('height.input');\n justify-content: center;\n overflow: hidden;\n position: absolute;\n top: 0;\n width: theme('height.input');\n }\n\n .symbol--prepend {\n border-bottom-left-radius: theme('borderRadius.DEFAULT');\n border-top-left-radius: theme('borderRadius.DEFAULT');\n left: 0;\n }\n\n .symbol--append {\n border-bottom-right-radius: theme('borderRadius.DEFAULT');\n border-top-right-radius: theme('borderRadius.DEFAULT');\n right: 0;\n }\n\n .root input[disabled] {\n background-color: var(--color-ice-100) !important;\n border-color: var(--color-ice-500) !important;\n color: var(--color-ice-500) !important;\n pointer-events: none;\n outline-color: var(--color-ice-500) !important;\n\n & + .symbol--prepend,\n & + .symbol--append {\n color: var(--color-ice-500);\n }\n }\n\n .root input[readonly] {\n border-color: transparent;\n background-color: transparent;\n padding-left: 0;\n padding-right: 0;\n }\n\n .root input[disabled]:active,\n .root input[readonly]:active,\n .root input[disabled]:focus,\n .root input[readonly]:focus {\n box-shadow: none !important;\n }\n\n .root input[disabled]::placeholder {\n text-transform: none;\n }\n</style>\n"],"names":["props","__props","emit","__emit","slots","useSlots","classes","useCssModule","attrs","useAttrs","inputEl","ref","__expose","internalValue","convertDecimal","decimalSeparator","showPassword","isReadOnly","computed","isNumber","inputAttrs","tempAttrs","inputType","watchEffect","format","parseNumber","value","isUserTyping","tempValue","isNil","sanitizeDecimal","handleChange","parsedValue","handleInput","e","onMounted"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CE,UAAMA,IAAQC,GAQRC,IAAOC,GAmBPC,IAAQC,KACRC,IAAUC,KACVC,IAAQC,KAIRC,IAAUC;AAIH,IAAAC,EAAA,EAAE,SAAAF,GAAS;AAElB,UAAAG,IAAgBF,EAAqBX,EAAM,UAAU;AAE3D,IAAIA,EAAM,SAAS,aAAaA,EAAM,cAAcA,EAAM,eAAe,OACvEa,EAAc,QAAQC,EAAed,EAAM,YAAYe,CAAgB;AAGnE,UAAAC,IAAeL,EAAI,EAAK,GAExBM,IAAaC,EAAS,MAAMlB,EAAM,cAAe,cAAcQ,KAASA,EAAM,aAAa,EAAM,GACjGW,IAAWD,EAAS,MAAMlB,EAAM,SAAS,QAAQ,GAEjDoB,IAAaF,EAAS,MAAM;AAC1B,YAAAG,IAAY,EAAE,GAAGb;AAEvB,oBAAOa,EAAU,OAEVA;AAAA,IAAA,CACR,GAEKC,IAAYJ,EAAS,MAGrBC,EAAS,SAITnB,EAAM,SAAS,cAAcgB,EAAa,QACrC,SAGFhB,EAAM,IACd;AAED,IAAAuB,EAAY,MAAM;AACF,MAAAV,EAAA,QAAQW,EAAOxB,EAAM,UAAU;AAAA,IAAA,CAC9C;AAQD,aAASyB,EAAYC,IAAyB,IAAIC,IAAe,IAAwB;AAEnF,UAAA,CAACR,EAAS;AACL,eAAAO;AAGT,UAAIE,IAAYF;AAGZ,UAAAG,EAAMD,CAAS,KAAK,GAAGA,CAAS,GAAG,WAAW,EAAU,QAAA;AAQ5D,UALIb,MAAqB,QACXa,IAAAd,EAAec,GAAW,GAAG,IAIvCD,MAEEC,MAAc,QAAiBA,IAAA,OAG/BA,EAAU,SAAS,EAAE,WAAW,IAAI;AAAU,eAAAA;AAIpD,UAAI,CAACC,EAAMD,CAAS,KAAK,GAAGA,CAAS,GAAG;AAC1B,QAAAA,IAAA,KAAK,aAAa,SAAS;AAAA,UACrC,OAAO;AAAA,UACP,uBAAuB;AAAA,UACvB,aAAa;AAAA,QACd,CAAA,EAAE,OAAO,OAAOA,CAAS,CAAC;AAAA;AAEpB,eAAA;AAIT,aAAO,MAAM,OAAOA,CAAS,CAAC,IAAI,KAAK,WAAWA,CAAS;AAAA,IAC7D;AAOA,aAASJ,EAAOE,GAAwB;AACtC,aAAIP,EAAS,QACJW,EAAgBJ,GAAOX,CAAgB,IAGzCW;AAAA,IACT;AAKA,aAASK,IAAe;AAEtB,YAAMC,IAAcb,EAAS,QAAQM,EAAYZ,EAAc,KAAK,IAAIA,EAAc;AACtF,MAAAX,EAAK,UAAU8B,CAAW;AAAA,IAC5B;AAKA,aAASC,EAAYC,GAAU;AACvB,YAAAR,IAASQ,EAAE,OAA4B;AAG/B,MAAArB,EAAA,QAAQW,EAAOE,CAAK;AAElC,YAAMM,IAAcb,EAAS,QAAQM,EAAYZ,EAAc,OAAO,EAAI,IAAIa;AAE9E,MAAAxB,EAAK,sBAAsB8B,CAAW;AAAA,IACxC;AAEA,WAAAG,EAAU,MAAM;AACV,UAAAnC,EAAM,UAAU;AACZ,cAAA,IAAI,MAAM,0DAA0D;AAG5E,UAAIQ,EAAM;AACF,cAAA,IAAI,MAAM,+DAA+D;AAAA,IACjF,CACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"Input.js","sources":["../src/components/Input/Input.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import isNil from 'lodash-es/isNil';\n import { computed, onMounted, ref, useAttrs, useCssModule, useSlots, watchEffect } from 'vue';\n\n import { convertDecimal, decimalSeparator, sanitizeDecimal } from '../../utils/i18n';\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n import Icon from '../Icon/Icon.vue';\n\n export type InputValue = string | number | undefined;\n\n export interface InputProps extends FieldProps {\n /**\n * Autocomplete takes in a string of off or on\n */\n autocomplete?: string;\n\n /**\n * Value for the input element.\n */\n modelValue?: string | number;\n\n /**\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: string | number | null;\n\n /**\n * Input type. Excludes certain types that have a dedicated component.\n *\n * Note: For distinguishing between text & number internally, passing `number`\n * will still render an input with a type of `text` (for localization).\n */\n type?: string extends 'button' | 'checkbox' | 'radio' | 'submit' ? never : string;\n\n /**\n * Placeholder text for the input.\n * **Note:** placeholders should be used to display examples; they should not be used as labels because they are not accessible as labels. If a real label cannot be used, use the `aria-label` attribute.\n */\n placeholder?: string;\n }\n\n defineOptions({\n name: 'll-input',\n inheritAttrs: false,\n });\n\n const props = withDefaults(defineProps<InputProps>(), {\n autocomplete: 'off',\n type: 'text',\n modelValue: '',\n value: null,\n placeholder: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the input value changes.\n */\n (e: 'update:model-value', v: string | number): void;\n /**\n * Emitted when the input value changes.\n */\n (e: 'change', v: string | number): void;\n /**\n * Emitted when the input is focused\n */\n (e: 'focus', evt: Event): void;\n /**\n * Emitted when the input is blurred\n */\n (e: 'blur', evt: Event): void;\n }>();\n\n const slots = useSlots();\n const classes = useCssModule();\n const attrs = useAttrs();\n\n // declare a ref to hold the element reference\n // the name must match template ref value\n const inputEl = ref<HTMLInputElement>();\n\n // this exposes the input ref to any parent that renders the `Input` component\n // and gives it a template ref, giving parent components access to the DOM element.\n defineExpose({ inputEl });\n\n const internalValue = ref<string | number>(props.modelValue);\n\n if (props.type === 'number' && (props.modelValue || props.modelValue === 0)) {\n internalValue.value = convertDecimal(props.modelValue, decimalSeparator);\n }\n\n const showPassword = ref(false);\n\n const isReadOnly = computed(() => props.isReadOnly || ('readonly' in attrs && attrs.readonly !== false));\n const isNumber = computed(() => props.type === 'number');\n\n const inputAttrs = computed(() => {\n const tempAttrs = { ...attrs };\n\n delete tempAttrs.class;\n\n return tempAttrs;\n });\n\n const inputType = computed(() => {\n // type 'number' is a special use case and requires\n // that the type be 'text' to support internationalization\n if (isNumber.value) {\n return 'text';\n }\n\n if (props.type === 'password' && showPassword.value) {\n return 'text';\n }\n\n return props.type;\n });\n\n watchEffect(() => {\n internalValue.value = format(props.modelValue);\n });\n\n /**\n * Converts the localized number to a valid number (to emit). i.e. 12,50 > 12.50\n *\n * @param {string|number} value - Number you want to parse.\n * @returns {number|string} number or empty string (for empty input)\n */\n function parseNumber(value: string | number = '', isUserTyping = false): string | number {\n // If this isn't a number input, we shouldn't be calling this function\n if (!isNumber.value) {\n return value;\n }\n\n let tempValue = value;\n\n // If blank, just stop\n if (isNil(tempValue) || `${tempValue}`.length === 0) return '';\n\n // clean out different locale decimals\n if (decimalSeparator !== '.') {\n tempValue = convertDecimal(tempValue, '.');\n }\n\n // If the user is in the middle of an input, let them finish the number\n if (isUserTyping) {\n // Prefix values with a 0 if the user starts with a decimal point\n if (tempValue === '.') tempValue = '0.';\n\n // This is to prevent the parsing from correcting 0. to 0 and causing the value to be reset\n if (tempValue.toString().startsWith('0.')) return tempValue;\n }\n\n // Empty or null values convert to 0 with NumberFormat, so return '';\n if (!isNil(tempValue) && `${tempValue}`.length) {\n tempValue = Intl.NumberFormat('en-US', {\n style: 'decimal',\n maximumFractionDigits: 7,\n useGrouping: false,\n }).format(Number(tempValue));\n } else {\n return '';\n }\n\n // Ensure valid number, otherwise clear value\n return isNaN(Number(tempValue)) ? '' : parseFloat(tempValue);\n }\n\n /**\n * Formats the input based on conditions\n *\n * @param {number|string} value - The value to format.\n */\n function format(value: string | number) {\n if (isNumber.value) {\n return sanitizeDecimal(value, decimalSeparator);\n }\n\n return value;\n }\n\n /**\n * Fire after focusing out of input, if value has changed.\n */\n function handleChange() {\n // Parse the final value on blur\n const parsedValue = isNumber.value ? parseNumber(internalValue.value) : internalValue.value;\n emit('change', parsedValue);\n }\n\n /**\n * Fired when typing on input.\n */\n function handleInput(e: Event) {\n const value = (e.target as HTMLInputElement).value;\n\n // Update the internal value with a sanitized version\n internalValue.value = format(value);\n\n const parsedValue = isNumber.value ? parseNumber(internalValue.value, true) : value;\n\n emit('update:model-value', parsedValue);\n }\n\n onMounted(() => {\n if (props.value !== null) {\n throw new Error('ll-input: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-input: use the @update:model-value event instead of @input');\n }\n });\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-input\" :class=\"[classes.root, attrs.class]\" data-test=\"ll-input\">\n <template #default=\"{ fieldId, fieldErrorId, hasError }\">\n <div class=\"tw-relative\">\n <input\n v-bind=\"inputAttrs\"\n :id=\"fieldId\"\n ref=\"inputEl\"\n v-model=\"internalValue\"\n :aria-errormessage=\"fieldErrorId\"\n :aria-invalid=\"hasError\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"props.placeholder\"\n :class=\"[\n { [classes['input-prepended']]: !!slots.prepend },\n { [classes['input-appended']]: !!slots.append || props.type === 'password' },\n { [classes['has-error']]: !!props.errorText },\n ]\"\n :type=\"inputType\"\n :disabled=\"props.isDisabled || props.disabled\"\n :readonly=\"isReadOnly\"\n @change=\"handleChange\"\n @input=\"handleInput\"\n @blur=\"(evt) => emit('blur', evt)\"\n @focus=\"(evt) => emit('focus', evt)\"\n />\n\n <div\n v-if=\"slots.prepend\"\n class=\"stash-input-prepend font-semibold\"\n :class=\"[classes.symbol, classes['symbol--prepend']]\"\n >\n <!-- @slot renders content on the left side of the input -->\n <slot name=\"prepend\"> </slot>\n </div>\n\n <div\n v-if=\"slots.append || props.type === 'password'\"\n class=\"stash-input-append font-semibold\"\n :class=\"[classes.symbol, classes['symbol--append']]\"\n >\n <!-- @slot renders content on the right side of the input -->\n <slot v-if=\"slots.append\" name=\"append\"></slot>\n <Icon\n v-else\n :name=\"showPassword ? 'hide' : 'show'\"\n class=\"tw-cursor-pointer\"\n :class=\"{ 'tw-text-ice-500': props.isDisabled || props.disabled }\"\n :data-test=\"showPassword ? 'hide-password-icon' : 'show-password-icon'\"\n @click=\"showPassword = !showPassword\"\n />\n </div>\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .root {\n position: relative;\n }\n\n .root input {\n background: var(--color-white);\n border: 1px solid var(--color-ice-500);\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n height: theme('height.input');\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n outline: none;\n padding: 0 theme('spacing.3');\n width: 100%;\n }\n\n .root input:hover {\n border-color: var(--color-ice-500);\n }\n\n .root input:active,\n .root input:focus {\n border-color: var(--color-blue-500);\n }\n\n .root input::placeholder {\n color: var(--color-ice-500);\n opacity: 1;\n }\n\n .root input.has-error:not(:focus) {\n border-color: var(--color-red-500);\n }\n\n .root input.input-prepended {\n padding-left: 36px;\n }\n\n .root input.input-appended {\n padding-right: 36px;\n }\n\n .symbol {\n align-items: center;\n display: flex;\n height: theme('height.input');\n justify-content: center;\n overflow: hidden;\n position: absolute;\n top: 0;\n width: theme('height.input');\n }\n\n .symbol--prepend {\n border-bottom-left-radius: theme('borderRadius.DEFAULT');\n border-top-left-radius: theme('borderRadius.DEFAULT');\n left: 0;\n }\n\n .symbol--append {\n border-bottom-right-radius: theme('borderRadius.DEFAULT');\n border-top-right-radius: theme('borderRadius.DEFAULT');\n right: 0;\n }\n\n .root input[disabled] {\n background-color: var(--color-ice-100) !important;\n border-color: var(--color-ice-500) !important;\n color: var(--color-ice-500) !important;\n pointer-events: none;\n outline-color: var(--color-ice-500) !important;\n\n & + .symbol--prepend,\n & + .symbol--append {\n color: var(--color-ice-500);\n }\n }\n\n .root input[readonly] {\n border-color: transparent;\n background-color: transparent;\n padding-left: 0;\n padding-right: 0;\n }\n\n .root input[disabled]:active,\n .root input[readonly]:active,\n .root input[disabled]:focus,\n .root input[readonly]:focus {\n box-shadow: none !important;\n }\n\n .root input[disabled]::placeholder {\n text-transform: none;\n }\n</style>\n"],"names":["props","__props","emit","__emit","slots","useSlots","classes","useCssModule","attrs","useAttrs","inputEl","ref","__expose","internalValue","convertDecimal","decimalSeparator","showPassword","isReadOnly","computed","isNumber","inputAttrs","tempAttrs","inputType","watchEffect","format","parseNumber","value","isUserTyping","tempValue","isNil","sanitizeDecimal","handleChange","parsedValue","handleInput","e","onMounted"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CE,UAAMA,IAAQC,GAQRC,IAAOC,GAmBPC,IAAQC,KACRC,IAAUC,KACVC,IAAQC,KAIRC,IAAUC;AAIH,IAAAC,EAAA,EAAE,SAAAF,GAAS;AAElB,UAAAG,IAAgBF,EAAqBX,EAAM,UAAU;AAE3D,IAAIA,EAAM,SAAS,aAAaA,EAAM,cAAcA,EAAM,eAAe,OACvEa,EAAc,QAAQC,EAAed,EAAM,YAAYe,CAAgB;AAGnE,UAAAC,IAAeL,EAAI,EAAK,GAExBM,IAAaC,EAAS,MAAMlB,EAAM,cAAe,cAAcQ,KAASA,EAAM,aAAa,EAAM,GACjGW,IAAWD,EAAS,MAAMlB,EAAM,SAAS,QAAQ,GAEjDoB,IAAaF,EAAS,MAAM;AAC1B,YAAAG,IAAY,EAAE,GAAGb;AAEvB,oBAAOa,EAAU,OAEVA;AAAA,IAAA,CACR,GAEKC,IAAYJ,EAAS,MAGrBC,EAAS,SAITnB,EAAM,SAAS,cAAcgB,EAAa,QACrC,SAGFhB,EAAM,IACd;AAED,IAAAuB,EAAY,MAAM;AACF,MAAAV,EAAA,QAAQW,EAAOxB,EAAM,UAAU;AAAA,IAAA,CAC9C;AAQD,aAASyB,EAAYC,IAAyB,IAAIC,IAAe,IAAwB;AAEnF,UAAA,CAACR,EAAS;AACL,eAAAO;AAGT,UAAIE,IAAYF;AAGZ,UAAAG,EAAMD,CAAS,KAAK,GAAGA,CAAS,GAAG,WAAW,EAAU,QAAA;AAQ5D,UALIb,MAAqB,QACXa,IAAAd,EAAec,GAAW,GAAG,IAIvCD,MAEEC,MAAc,QAAiBA,IAAA,OAG/BA,EAAU,SAAS,EAAE,WAAW,IAAI;AAAU,eAAAA;AAIpD,UAAI,CAACC,EAAMD,CAAS,KAAK,GAAGA,CAAS,GAAG;AAC1B,QAAAA,IAAA,KAAK,aAAa,SAAS;AAAA,UACrC,OAAO;AAAA,UACP,uBAAuB;AAAA,UACvB,aAAa;AAAA,QACd,CAAA,EAAE,OAAO,OAAOA,CAAS,CAAC;AAAA;AAEpB,eAAA;AAIT,aAAO,MAAM,OAAOA,CAAS,CAAC,IAAI,KAAK,WAAWA,CAAS;AAAA,IAC7D;AAOA,aAASJ,EAAOE,GAAwB;AACtC,aAAIP,EAAS,QACJW,EAAgBJ,GAAOX,CAAgB,IAGzCW;AAAA,IACT;AAKA,aAASK,IAAe;AAEtB,YAAMC,IAAcb,EAAS,QAAQM,EAAYZ,EAAc,KAAK,IAAIA,EAAc;AACtF,MAAAX,EAAK,UAAU8B,CAAW;AAAA,IAC5B;AAKA,aAASC,EAAYC,GAAU;AACvB,YAAAR,IAASQ,EAAE,OAA4B;AAG/B,MAAArB,EAAA,QAAQW,EAAOE,CAAK;AAElC,YAAMM,IAAcb,EAAS,QAAQM,EAAYZ,EAAc,OAAO,EAAI,IAAIa;AAE9E,MAAAxB,EAAK,sBAAsB8B,CAAW;AAAA,IACxC;AAEA,WAAAG,EAAU,MAAM;AACV,UAAAnC,EAAM,UAAU;AACZ,cAAA,IAAI,MAAM,0DAA0D;AAG5E,UAAIQ,EAAM;AACF,cAAA,IAAI,MAAM,+DAA+D;AAAA,IACjF,CACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"InputOptions.js","sources":["../src/components/InputOptions/InputOptions.vue"],"sourcesContent":["<script lang=\"ts\">\n import Field, { type FieldProps } from '../Field/Field.vue';\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type Option = any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type SelectedOption = any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n export interface InputOptionsProps extends FieldProps {\n /**\n * The current value inclusive of the input & select\n */\n modelValue?: { value: string; option?: SelectedOption };\n\n /**\n * Input type\n */\n type?: string extends 'button' | 'checkbox' | 'radio' | 'submit' ? never : string;\n\n /**\n * Prevents the Selected Option from being truncated, if true\n */\n noTruncate?: boolean;\n\n /**\n * Options for the select\n */\n options?: Option[];\n\n /**\n * Placeholder text for the input\n * **Note:** placeholders should be used to display examples; they should not be used as labels because they are not accessible as labels. If a real label cannot be used, use the `aria-label` attribute.\n */\n placeholder?: string;\n\n /**\n * If `options` are an object, this is what prop to use for display.\n */\n displayBy?: string;\n\n /**\n * Default field to track selected options by.\n */\n trackBy?: string;\n }\n</script>\n\n<script lang=\"ts\" setup>\n import { ref, useAttrs, useCssModule, useSlots, watch, watchEffect } from 'vue';\n\n import Input from '../Input/Input.vue';\n import Select from '../Select/Select.vue';\n\n defineOptions({\n name: 'll-input-options',\n });\n\n const props = withDefaults(defineProps<InputOptionsProps>(), {\n modelValue: () => ({ value: '', option: undefined }),\n noTruncate: false,\n options: () => [],\n type: 'text',\n placeholder: undefined,\n displayBy: undefined,\n trackBy: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes\n */\n (\n e: 'update:model-value',\n v: { value?: string; option?: SelectedOption; isValueChange: boolean; type: 'input' | 'select' },\n ): void;\n /**\n * Emitted when either the input or select changes\n */\n (\n e: 'change',\n v: { value?: string; option?: SelectedOption; isValueChange: boolean; type: 'input' | 'select' },\n ): void;\n }>();\n\n const attrs = useAttrs();\n const slots = useSlots();\n const classes = useCssModule();\n const internalInput = ref<string>();\n const isInputFocused = ref(false);\n const selectedOption = ref<Option | undefined>();\n\n // Input value changed\n function handleInput(val?: string | number) {\n internalInput.value = String(val);\n\n emit('update:model-value', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: true,\n type: 'input',\n });\n }\n\n // Input blurred\n function handleInputChange() {\n emit('change', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: true,\n type: 'input',\n });\n }\n\n function handleSelectChange(val?: Option) {\n selectedOption.value = val;\n\n emit('change', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: false,\n type: 'select',\n });\n emit('update:model-value', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: false,\n type: 'select',\n });\n }\n\n watchEffect(() => {\n if (!selectedOption.value) {\n selectedOption.value = props.options[0];\n }\n });\n\n watch(\n () => props.modelValue.value,\n () => {\n internalInput.value = props.modelValue.value;\n },\n { immediate: true },\n );\n\n watch(\n () => props.modelValue.option,\n () => {\n selectedOption.value = props.modelValue.option;\n },\n { immediate: true },\n );\n\n if (attrs.value) {\n throw new Error('ll-input-options: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-input-options: use the @update:model-value event instead of @input');\n }\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-input-options\" data-test=\"stash-input-options\">\n <template #default=\"{ fieldId, labelId }\">\n <div v-if=\"props.isReadOnly\" class=\"tw-flex tw-h-input tw-items-center tw-text-sm\">\n <span :id=\"fieldId\" :aria-labelledby=\"labelId\" class=\"tw-h-min\"\n >{{ internalInput || 0 }} {{ selectedOption?.name }}</span\n >\n </div>\n <div v-else class=\"tw-flex\" :class=\"{ [classes['has-error']]: !!props.errorText }\">\n <Input\n :id=\"fieldId\"\n class=\"stash-input-options__input -tw-mr-[1px] tw-inline-block tw-flex-1\"\n :class=\"[classes.input, { 'tw-z-control': isInputFocused }]\"\n data-test=\"stash-input-options|input\"\n :type=\"props.type\"\n :model-value=\"internalInput\"\n :disabled=\"props.isDisabled || props.disabled\"\n :placeholder=\"props.placeholder\"\n @change=\"handleInputChange\"\n @update:model-value=\"handleInput\"\n @blur=\"isInputFocused = false\"\n @focus=\"isInputFocused = true\"\n />\n\n <Select\n single\n hide-search\n prevent-empty\n class=\"stash-input-options__select tw-min-w-20\"\n data-test=\"stash-input-options|select\"\n :class=\"classes.select\"\n :no-truncate=\"noTruncate\"\n :options=\"options\"\n :model-value=\"selectedOption\"\n :display-by=\"props.displayBy\"\n :track-by=\"props.trackBy\"\n :disabled=\"props.isDisabled || props.disabled\"\n @update:model-value=\"handleSelectChange\"\n />\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Hint slot for rendering text below the input -->\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .input input {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n }\n\n .select :global(.stash-select__content) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n }\n\n .select:global(.stash-select--active .stash-select__content) {\n min-width: 0;\n }\n\n .has-error input,\n .has-error input:hover:not(:focus),\n .has-error :global(.stash-select__content),\n .has-error :global(.stash-select__content:hover:not(:focus)) {\n border-color: var(--color-red-500);\n }\n</style>\n"],"names":["props","__props","emit","__emit","attrs","useAttrs","slots","useSlots","classes","useCssModule","internalInput","ref","isInputFocused","selectedOption","handleInput","val","handleInputChange","handleSelectChange","watchEffect","watch"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DE,UAAMA,IAAQC,GAURC,IAAOC,GAiBPC,IAAQC,KACRC,IAAQC,KACRC,IAAUC,KACVC,IAAgBC,KAChBC,IAAiBD,EAAI,EAAK,GAC1BE,IAAiBF;AAGvB,aAASG,EAAYC,GAAuB;AAC5B,MAAAL,EAAA,QAAQ,OAAOK,CAAG,GAEhCb,EAAK,sBAAsB;AAAA,QACzB,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAGA,aAASG,IAAoB;AAC3B,MAAAd,EAAK,UAAU;AAAA,QACb,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAEA,aAASI,EAAmBF,GAAc;AACxC,MAAAF,EAAe,QAAQE,GAEvBb,EAAK,UAAU;AAAA,QACb,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP,GACDX,EAAK,sBAAsB;AAAA,QACzB,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAwBA,QAtBAK,EAAY,MAAM;AACZ,MAACL,EAAe,UACHA,EAAA,QAAQb,EAAM,QAAQ,CAAC;AAAA,IACxC,CACD,GAEDmB;AAAA,MACE,MAAMnB,EAAM,WAAW;AAAA,MACvB,MAAM;AACU,QAAAU,EAAA,QAAQV,EAAM,WAAW;AAAA,MACzC;AAAA,MACA,EAAE,WAAW,GAAK;AAAA,IAAA,GAGpBmB;AAAA,MACE,MAAMnB,EAAM,WAAW;AAAA,MACvB,MAAM;AACW,QAAAa,EAAA,QAAQb,EAAM,WAAW;AAAA,MAC1C;AAAA,MACA,EAAE,WAAW,GAAK;AAAA,IAAA,GAGhBI,EAAM;AACF,YAAA,IAAI,MAAM,kEAAkE;AAGpF,QAAIA,EAAM;AACF,YAAA,IAAI,MAAM,uEAAuE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"InputOptions.js","sources":["../src/components/InputOptions/InputOptions.vue"],"sourcesContent":["<script lang=\"ts\">\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type Option = any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type SelectedOption = any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n export interface InputOptionsProps extends FieldProps {\n /**\n * The current value inclusive of the input & select\n */\n modelValue?: { value: string; option?: SelectedOption };\n\n /**\n * Input type\n */\n type?: string extends 'button' | 'checkbox' | 'radio' | 'submit' ? never : string;\n\n /**\n * Prevents the Selected Option from being truncated, if true\n */\n noTruncate?: boolean;\n\n /**\n * Options for the select\n */\n options?: Option[];\n\n /**\n * Placeholder text for the input\n * **Note:** placeholders should be used to display examples; they should not be used as labels because they are not accessible as labels. If a real label cannot be used, use the `aria-label` attribute.\n */\n placeholder?: string;\n\n /**\n * If `options` are an object, this is what prop to use for display.\n */\n displayBy?: string;\n\n /**\n * Default field to track selected options by.\n */\n trackBy?: string;\n }\n</script>\n\n<script lang=\"ts\" setup>\n import { ref, useAttrs, useCssModule, useSlots, watch, watchEffect } from 'vue';\n\n import Input from '../Input/Input.vue';\n import Select from '../Select/Select.vue';\n\n defineOptions({\n name: 'll-input-options',\n });\n\n const props = withDefaults(defineProps<InputOptionsProps>(), {\n modelValue: () => ({ value: '', option: undefined }),\n noTruncate: false,\n options: () => [],\n type: 'text',\n placeholder: undefined,\n displayBy: undefined,\n trackBy: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes\n */\n (\n e: 'update:model-value',\n v: { value?: string; option?: SelectedOption; isValueChange: boolean; type: 'input' | 'select' },\n ): void;\n /**\n * Emitted when either the input or select changes\n */\n (\n e: 'change',\n v: { value?: string; option?: SelectedOption; isValueChange: boolean; type: 'input' | 'select' },\n ): void;\n }>();\n\n const attrs = useAttrs();\n const slots = useSlots();\n const classes = useCssModule();\n const internalInput = ref<string>();\n const isInputFocused = ref(false);\n const selectedOption = ref<Option | undefined>();\n\n // Input value changed\n function handleInput(val?: string | number) {\n internalInput.value = String(val);\n\n emit('update:model-value', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: true,\n type: 'input',\n });\n }\n\n // Input blurred\n function handleInputChange() {\n emit('change', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: true,\n type: 'input',\n });\n }\n\n function handleSelectChange(val?: Option) {\n selectedOption.value = val;\n\n emit('change', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: false,\n type: 'select',\n });\n emit('update:model-value', {\n value: internalInput.value,\n option: selectedOption.value,\n isValueChange: false,\n type: 'select',\n });\n }\n\n watchEffect(() => {\n if (!selectedOption.value) {\n selectedOption.value = props.options[0];\n }\n });\n\n watch(\n () => props.modelValue.value,\n () => {\n internalInput.value = props.modelValue.value;\n },\n { immediate: true },\n );\n\n watch(\n () => props.modelValue.option,\n () => {\n selectedOption.value = props.modelValue.option;\n },\n { immediate: true },\n );\n\n if (attrs.value) {\n throw new Error('ll-input-options: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-input-options: use the @update:model-value event instead of @input');\n }\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-input-options\" data-test=\"stash-input-options\">\n <template #default=\"{ fieldId, labelId }\">\n <div v-if=\"props.isReadOnly\" class=\"tw-flex tw-h-input tw-items-center tw-text-sm\">\n <span :id=\"fieldId\" :aria-labelledby=\"labelId\" class=\"tw-h-min\"\n >{{ internalInput || 0 }} {{ selectedOption?.name }}</span\n >\n </div>\n <div v-else class=\"tw-flex\" :class=\"{ [classes['has-error']]: !!props.errorText }\">\n <Input\n :id=\"fieldId\"\n class=\"stash-input-options__input -tw-mr-[1px] tw-inline-block tw-flex-1\"\n :class=\"[classes.input, { 'tw-z-control': isInputFocused }]\"\n data-test=\"stash-input-options|input\"\n :type=\"props.type\"\n :model-value=\"internalInput\"\n :disabled=\"props.isDisabled || props.disabled\"\n :placeholder=\"props.placeholder\"\n @change=\"handleInputChange\"\n @update:model-value=\"handleInput\"\n @blur=\"isInputFocused = false\"\n @focus=\"isInputFocused = true\"\n />\n\n <Select\n single\n hide-search\n prevent-empty\n class=\"stash-input-options__select tw-min-w-20\"\n data-test=\"stash-input-options|select\"\n :class=\"classes.select\"\n :no-truncate=\"noTruncate\"\n :options=\"options\"\n :model-value=\"selectedOption\"\n :display-by=\"props.displayBy\"\n :track-by=\"props.trackBy\"\n :disabled=\"props.isDisabled || props.disabled\"\n @update:model-value=\"handleSelectChange\"\n />\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Hint slot for rendering text below the input -->\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .input input {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n }\n\n .select :global(.stash-select__content) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n }\n\n .select:global(.stash-select--active .stash-select__content) {\n min-width: 0;\n }\n\n .has-error input,\n .has-error input:hover:not(:focus),\n .has-error :global(.stash-select__content),\n .has-error :global(.stash-select__content:hover:not(:focus)) {\n border-color: var(--color-red-500);\n }\n</style>\n"],"names":["props","__props","emit","__emit","attrs","useAttrs","slots","useSlots","classes","useCssModule","internalInput","ref","isInputFocused","selectedOption","handleInput","val","handleInputChange","handleSelectChange","watchEffect","watch"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DE,UAAMA,IAAQC,GAURC,IAAOC,GAiBPC,IAAQC,KACRC,IAAQC,KACRC,IAAUC,KACVC,IAAgBC,KAChBC,IAAiBD,EAAI,EAAK,GAC1BE,IAAiBF;AAGvB,aAASG,EAAYC,GAAuB;AAC5B,MAAAL,EAAA,QAAQ,OAAOK,CAAG,GAEhCb,EAAK,sBAAsB;AAAA,QACzB,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAGA,aAASG,IAAoB;AAC3B,MAAAd,EAAK,UAAU;AAAA,QACb,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAEA,aAASI,EAAmBF,GAAc;AACxC,MAAAF,EAAe,QAAQE,GAEvBb,EAAK,UAAU;AAAA,QACb,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP,GACDX,EAAK,sBAAsB;AAAA,QACzB,OAAOQ,EAAc;AAAA,QACrB,QAAQG,EAAe;AAAA,QACvB,eAAe;AAAA,QACf,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAwBA,QAtBAK,EAAY,MAAM;AACZ,MAACL,EAAe,UACHA,EAAA,QAAQb,EAAM,QAAQ,CAAC;AAAA,IACxC,CACD,GAEDmB;AAAA,MACE,MAAMnB,EAAM,WAAW;AAAA,MACvB,MAAM;AACU,QAAAU,EAAA,QAAQV,EAAM,WAAW;AAAA,MACzC;AAAA,MACA,EAAE,WAAW,GAAK;AAAA,IAAA,GAGpBmB;AAAA,MACE,MAAMnB,EAAM,WAAW;AAAA,MACvB,MAAM;AACW,QAAAa,EAAA,QAAQb,EAAM,WAAW;AAAA,MAC1C;AAAA,MACA,EAAE,WAAW,GAAK;AAAA,IAAA,GAGhBI,EAAM;AACF,YAAA,IAAI,MAAM,kEAAkE;AAGpF,QAAIA,EAAM;AACF,YAAA,IAAI,MAAM,uEAAuE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -2079,18 +2079,14 @@ type: "input" | "select";
2079
2079
  }) => any) | undefined;
2080
2080
  "onUpdate:model-value"?: ((v: {
2081
2081
  value?: string | undefined;
2082
- option?: any; /**
2083
- * Hide the Select All checkbox for bulk actions
2084
- */
2082
+ option?: any;
2085
2083
  isValueChange: boolean;
2086
2084
  type: "input" | "select";
2087
2085
  }) => any) | undefined;
2088
2086
  }>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
2089
2087
  "update:model-value": (v: {
2090
2088
  value?: string | undefined;
2091
- option?: any; /**
2092
- * Hide the Select All checkbox for bulk actions
2093
- */
2089
+ option?: any;
2094
2090
  isValueChange: boolean;
2095
2091
  type: "input" | "select";
2096
2092
  }) => void;
@@ -2198,9 +2194,7 @@ type: "input" | "select";
2198
2194
  }) => any) | undefined;
2199
2195
  "onUpdate:model-value"?: ((v: {
2200
2196
  value?: string | undefined;
2201
- option?: any; /**
2202
- * Hide the Select All checkbox for bulk actions
2203
- */
2197
+ option?: any;
2204
2198
  isValueChange: boolean;
2205
2199
  type: "input" | "select";
2206
2200
  }) => any) | undefined;
@@ -2299,18 +2293,14 @@ type: "input" | "select";
2299
2293
  }) => any) | undefined;
2300
2294
  "onUpdate:model-value"?: ((v: {
2301
2295
  value?: string | undefined;
2302
- option?: any; /**
2303
- * Hide the Select All checkbox for bulk actions
2304
- */
2296
+ option?: any;
2305
2297
  isValueChange: boolean;
2306
2298
  type: "input" | "select";
2307
2299
  }) => any) | undefined;
2308
2300
  }>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
2309
2301
  "update:model-value": (v: {
2310
2302
  value?: string | undefined;
2311
- option?: any; /**
2312
- * Hide the Select All checkbox for bulk actions
2313
- */
2303
+ option?: any;
2314
2304
  isValueChange: boolean;
2315
2305
  type: "input" | "select";
2316
2306
  }) => void;
@@ -237,7 +237,7 @@ const _e = /* @__PURE__ */ h({
237
237
  _: 3
238
238
  }, 16, ["aria-errormessage", "error-id", "aria-invalid"]));
239
239
  }
240
- }), ve = /* @__PURE__ */ x(_e, [["__scopeId", "data-v-3e1d9e30"]]);
240
+ }), ve = /* @__PURE__ */ x(_e, [["__scopeId", "data-v-20066b6e"]]);
241
241
  export {
242
242
  y as RADIO_GROUP_INJECTION,
243
243
  w as RadioGroupVariant,
@@ -1 +1 @@
1
- {"version":3,"file":"RadioGroup.js","sources":["../src/components/RadioGroup/components/VariantButton.vue","../src/components/RadioGroup/components/VariantChip.vue","../src/components/RadioGroup/components/VariantRadio.vue","../src/components/RadioGroup/components/VariantTile.vue","../src/components/RadioGroup/RadioGroup.types.ts","../src/components/RadioGroup/RadioGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantButton must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-flex\" :class=\"classes.root\">\n <div v-for=\"option in options\" :key=\"`${name}-${option.id}`\" :class=\"[{ 'tw-w-full': fullWidth }]\">\n <input\n :id=\"`${name}-${option.id}`\"\n class=\"tw-sr-only\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <label :for=\"`${name}-${option.id}`\">\n {{ option.text }}\n </label>\n </div>\n </div>\n</template>\n\n<style module>\n .root label {\n padding: 8px 30px;\n border: 1px solid var(--color-ice-500);\n font-weight: theme('fontWeight.semibold');\n color: var(--color-ice-700);\n cursor: pointer;\n transition: all 0.2s;\n user-select: none;\n display: block;\n text-align: center;\n }\n\n .root > div:first-child label {\n border-top-left-radius: theme('borderRadius.DEFAULT');\n border-bottom-left-radius: theme('borderRadius.DEFAULT');\n }\n\n .root > div:last-child label {\n border-top-right-radius: theme('borderRadius.DEFAULT');\n border-bottom-right-radius: theme('borderRadius.DEFAULT');\n }\n\n .root > div:not(:first-child) label {\n margin-left: -1px;\n }\n\n .root > div:not(:last-child) label {\n border-right-color: transparent;\n }\n\n .root input:disabled ~ label {\n background-color: var(--color-ice-200);\n color: var(--color-ice-500);\n cursor: auto;\n }\n\n .root input:not(:checked, :disabled) ~ label:hover {\n border-color: var(--color-blue-500);\n color: var(--color-blue-500);\n z-index: 1;\n position: relative;\n }\n\n .root input:checked:not(:disabled) ~ label {\n border-color: var(--color-blue-500);\n color: var(--color-blue-500);\n background-color: var(--color-blue-100);\n z-index: 1;\n position: relative;\n }\n</style>\n","<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantChip must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-my-1.5 tw-flex tw-flex-wrap\" :class=\"classes.root\">\n <div v-for=\"option in options\" :key=\"`${name}-${option.id}`\" :class=\"[{ 'tw-w-full': fullWidth }]\">\n <input\n :id=\"`${name}-${option.id}`\"\n class=\"tw-sr-only\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <label :for=\"`${name}-${option.id}`\">\n {{ option.text }}\n </label>\n </div>\n </div>\n</template>\n\n<style module>\n .root {\n gap: theme('spacing.6');\n }\n\n .root label {\n padding: theme('spacing[1.5]') theme('spacing.3');\n border: 1px solid var(--color-ice-500);\n font-weight: theme('fontWeight.normal');\n color: var(--color-ice-900);\n cursor: pointer;\n transition: all 0.2s;\n user-select: none;\n border-radius: 9999px;\n white-space: nowrap;\n }\n\n .root input:disabled ~ label {\n background-color: var(--color-ice-200);\n color: var(--color-ice-500);\n cursor: auto;\n }\n\n .root input:checked:not(:disabled) ~ label {\n border-color: var(--color-blue-500);\n color: var(--color-white);\n background-color: var(--color-blue-500);\n font-weight: theme('fontWeight.bold');\n }\n\n .root input:not(:checked, :disabled) ~ label:hover {\n border-color: var(--color-blue-500);\n }\n</style>\n","<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantRadio must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-flex tw-flex-wrap\" :class=\"classes.root\">\n <div v-for=\"option in options\" :key=\"`${name}-${option.id}`\" :class=\"[{ 'tw-w-full': fullWidth }]\">\n <input\n :id=\"`${name}-${option.id}`\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <label :for=\"`${name}-${option.id}`\">\n {{ option.text }}\n </label>\n </div>\n </div>\n</template>\n\n<style module>\n .root {\n gap: theme('spacing.6');\n }\n\n .root label {\n font-weight: theme('fontWeight.medium');\n user-select: none;\n cursor: pointer;\n padding: 0 theme('spacing.3');\n }\n\n .root input {\n appearance: none;\n border-radius: 50%;\n width: 20px;\n height: 20px;\n border: 1px solid var(--color-ice-500);\n transition: all 0.2s;\n position: relative;\n cursor: pointer;\n top: 5px;\n }\n\n .root input:checked {\n background-image: radial-gradient(var(--color-blue-500) 50%, transparent 54%);\n }\n\n .root input:disabled {\n background: #eef2f4;\n }\n\n .root input:disabled ~ label {\n cursor: auto;\n }\n\n .root input:disabled:checked {\n background-image: radial-gradient(var(--color-ice-500) 50%, transparent 54%);\n }\n\n .root input:hover:not(:disabled) {\n border-color: var(--color-blue-500);\n }\n\n .root input:hover:not(:disabled) ~ label {\n color: var(--color-ice-900);\n }\n\n .root input:not(:disabled) ~ label:hover {\n color: var(--color-ice-900);\n }\n</style>\n","<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantTile must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-flex tw-flex-wrap\" :class=\"classes.root\">\n <label\n v-for=\"option in options\"\n :key=\"`${name}-${option.id}`\"\n :class=\"[classes['tile-container'], { 'tw-w-full': fullWidth }]\"\n :for=\"`${name}-${option.id}`\"\n >\n <div\n class=\"tw-flex tw-border\"\n :class=\"[\n classes['tile-header'],\n {\n 'tw-border-blue-500 tw-bg-blue-100 tw-text-ice-900': modelValue === option.value,\n 'tw-border-ice-500 tw-bg-ice-100 tw-text-ice-700': modelValue !== option.value,\n },\n ]\"\n >\n <input\n :id=\"`${name}-${option.id}`\"\n class=\"tw-sr-only\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <div>\n <span>\n {{ option.text }}\n </span>\n </div>\n </div>\n <div\n class=\"tw-border-x tw-border-b\"\n :class=\"[\n classes['tile-body'],\n {\n 'tw-border-ice-500': modelValue !== option.value,\n 'tw-border-blue-500': modelValue === option.value,\n },\n ]\"\n >\n <p class=\"tw-m-0 tw-text-ice-900\">{{ option.subTitle }}</p>\n <p class=\"tw-m-0 tw-text-ice-700\">{{ option.subText }}</p>\n </div>\n </label>\n </div>\n</template>\n\n<style module>\n .root {\n gap: theme('spacing.6');\n }\n\n .root label {\n cursor: pointer;\n user-select: none;\n }\n\n .root input:disabled ~ label {\n cursor: auto;\n }\n\n .root input:hover:not(:disabled) ~ label {\n color: var(--color-ice-900);\n }\n\n .root input:not(:disabled) ~ label:hover {\n color: var(--color-ice-900);\n }\n\n .root label.tile-container {\n border-radius: theme('borderRadius.DEFAULT');\n display: flex;\n flex: 1;\n flex-direction: column;\n }\n\n .tile-header,\n .tile-body {\n transition: all 0.2s;\n }\n\n .tile-header {\n border-radius: theme('borderRadius.DEFAULT') theme('borderRadius.DEFAULT') 0 0;\n padding: theme('spacing.3') 0;\n }\n\n .root label.tile-container:hover .tile-header {\n background-color: var(--color-blue-100) !important;\n border-color: var(--color-blue-500) !important;\n color: var(--color-ice-900) !important;\n transition: all 0.2s;\n }\n\n .tile-body {\n border-radius: 0 0 theme('borderRadius.DEFAULT') theme('borderRadius.DEFAULT');\n background-color: var(--color-white);\n padding: theme('spacing.6');\n display: flex;\n flex-direction: column;\n }\n\n .root label.tile-container:hover .tile-body {\n border-color: var(--color-blue-500) !important;\n transition: all 0.2s;\n }\n\n .root input {\n appearance: none;\n background-color: var(--color-white);\n border-radius: 50%;\n width: 20px;\n height: 20px;\n border: 1px solid var(--color-ice-500);\n transition: all 0.2s;\n position: relative;\n cursor: pointer;\n top: 2px;\n }\n\n .root input ~ div {\n font-weight: theme('fontWeight.medium');\n user-select: none;\n cursor: pointer;\n padding: 0 theme('spacing.3');\n }\n\n .root input:disabled {\n background: #eef2f4;\n }\n\n .root input:checked {\n background-image: radial-gradient(var(--color-blue-500) 50%, transparent 54%);\n }\n\n .root label.tile-container input {\n margin-left: theme('spacing.3');\n }\n\n .root input:disabled:checked {\n background-image: radial-gradient(var(--color-ice-500) 50%, transparent 54%);\n }\n\n @media screen and (width <= 640px) {\n .root {\n flex-direction: column;\n }\n }\n</style>\n","import { ComputedRef } from 'vue';\n\nexport enum RadioGroupVariant {\n Radio = 'radio',\n Button = 'button',\n Chip = 'chip',\n Tile = 'tile',\n}\n\nexport type RadioGroupVariants = `${RadioGroupVariant}`;\n\n/**\n * An individual radio `<input>` within a RadioGroup instance\n */\nexport interface RadioGroupOption {\n /**\n * Disables the RadioGroupOption if truthy\n */\n disabled?: boolean;\n\n /**\n * The unique identifier for the option\n */\n id: number | string;\n\n /**\n * The text to be displayed for the option\n */\n text: string;\n\n /**\n * The value of the option. Used for the modelValue of the RadioGroup.\n */\n value: string;\n\n /**\n * The subtitle for tile variant\n */\n subTitle?: string;\n\n /**\n * The subtext for tile variant\n */\n subText?: string;\n}\n\n/**\n * Properties and utilities provided to children of a RadioGroup instance\n */\nexport interface RadioGroupInjection {\n /**\n * This type should match RadioGroupProps['disabled']\n */\n disabled: ComputedRef<boolean | undefined>;\n\n /**\n * This type should match RadioGroupProps['fullWidth']\n */\n fullWidth: ComputedRef<boolean | undefined>;\n\n /**\n * This type should match RadioGroupProps['modelValue']\n */\n modelValue: ComputedRef<string | undefined>;\n\n /**\n * This type should match RadioGroupProps['name']\n */\n name: ComputedRef<string | undefined>;\n\n /**\n * This type should match RadioGroupProps['options']\n */\n options: ComputedRef<RadioGroupOption[] | undefined>;\n\n /**\n * Triggered when the user changes their selection; updates the v-model.\n */\n update: (e: Event) => void;\n\n /**\n * This type should match RadioGroupProps['variant']\n * @default 'radio'\n */\n variant: ComputedRef<RadioGroupVariants | undefined>;\n}\n","<script lang=\"ts\">\n import Field, { type FieldProps } from '../Field/Field.vue';\n import { RadioGroupOption, RadioGroupVariant, RadioGroupVariants } from './RadioGroup.types';\n\n export * from './RadioGroup.keys';\n export * from './RadioGroup.types';\n\n export interface RadioGroupProps extends FieldProps {\n /**\n * Passed to the \"name\" attribute of the `<input>` elements within the RadioGroup.\n */\n name?: string;\n\n /**\n * Deprecated - Compose your radio group with the `Radio` component instead\n * @deprecated\n */\n options?: RadioGroupOption[];\n\n /**\n * The value of the selected radio option. Use this prop with the `update:modelValue` event to create a controlled component.\n */\n modelValue?: RadioGroupOption['value'];\n\n /**\n * @defaultValue 'radio'\n */\n variant?: RadioGroupVariants;\n\n /**\n * Whether the group should expand to the parent's width\n */\n fullWidth?: boolean;\n }\n</script>\n\n<script setup lang=\"ts\">\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, provide } from 'vue';\n\n import VariantButton from './components/VariantButton.vue';\n import VariantChip from './components/VariantChip.vue';\n import VariantRadio from './components/VariantRadio.vue';\n import VariantTile from './components/VariantTile.vue';\n import { RADIO_GROUP_INJECTION } from './RadioGroup.keys';\n\n const variantComponentsMap = {\n [RadioGroupVariant.Button]: VariantButton,\n [RadioGroupVariant.Chip]: VariantChip,\n [RadioGroupVariant.Radio]: VariantRadio,\n [RadioGroupVariant.Tile]: VariantTile,\n };\n\n const props = withDefaults(defineProps<RadioGroupProps>(), {\n variant: 'radio',\n fullWidth: false,\n modelValue: undefined,\n name: undefined,\n options: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Occurs when the user selects a radio option. Also, it enables v-model usage on the component.\n */\n (e: 'update:modelValue', value: RadioGroupOption['value']): void;\n }>();\n\n function update(e: Event) {\n emit('update:modelValue', (e.target as HTMLInputElement).value);\n }\n\n const errorId = uniqueId('radio-group-field-error-');\n\n provide(RADIO_GROUP_INJECTION.key, {\n name: computed(() => props.name),\n disabled: computed(() => props.isDisabled || props.disabled),\n fullWidth: computed(() => props.fullWidth),\n modelValue: computed(() => props.modelValue),\n options: computed(() => props.options),\n variant: computed(() => props.variant),\n update,\n });\n</script>\n\n<template>\n <Field\n v-bind=\"props\"\n class=\"stash-radio-group\"\n data-test=\"stash-radio-group\"\n :aria-errormessage=\"errorId\"\n :error-id=\"errorId\"\n :aria-invalid=\"!!props.errorText\"\n role=\"radiogroup\"\n fieldset\n >\n <slot v-if=\"props.options\">\n <component :is=\"variantComponentsMap[props.variant]\" />\n </slot>\n <div\n v-else\n class=\"tw-flex\"\n :class=\"{\n 'tw-flex-wrap': props.variant !== RadioGroupVariant.Button,\n 'tw-gap-x-1.5 tw-gap-y-3': props.variant === RadioGroupVariant.Chip,\n 'tw-gap-6': props.variant === RadioGroupVariant.Radio || props.variant === RadioGroupVariant.Tile,\n }\"\n >\n <slot></slot>\n </div>\n </Field>\n</template>\n\n<style scoped>\n .stash-radio-group :global(.stash-field-hint),\n .stash-radio-group :global(.stash-field-error) {\n display: block;\n width: 100%;\n }\n\n .stash-radio-group > :global(.stash-label) {\n margin-bottom: 4px;\n }\n</style>\n"],"names":["radioGroupInjection","inject","RADIO_GROUP_INJECTION","name","disabled","fullWidth","modelValue","options","update","classes","useCssModule","RadioGroupVariant","variantComponentsMap","VariantButton","VariantChip","VariantRadio","VariantTile","props","__props","emit","__emit","e","errorId","uniqueId","provide","computed"],"mappings":";;;;;;;;AAKQ,UAAAA,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,wDAAwD;AAG1E,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACPV,UAAAV,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,sDAAsD;AAGxE,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACPV,UAAAV,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,uDAAuD;AAGzE,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACPV,UAAAV,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,sDAAsD;AAGxE,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACVN,IAAAC,sBAAAA,OACVA,EAAA,QAAQ,SACRA,EAAA,SAAS,UACTA,EAAA,OAAO,QACPA,EAAA,OAAO,QAJGA,IAAAA,KAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;AC4CV,UAAMC,IAAuB;AAAA,MAC3B,CAACD,EAAkB,MAAM,GAAGE;AAAA,MAC5B,CAACF,EAAkB,IAAI,GAAGG;AAAA,MAC1B,CAACH,EAAkB,KAAK,GAAGI;AAAA,MAC3B,CAACJ,EAAkB,IAAI,GAAGK;AAAA,IAAA,GAGtBC,IAAQC,GAQRC,IAAOC;AAOb,aAASZ,EAAOa,GAAU;AACnB,MAAAF,EAAA,qBAAsBE,EAAE,OAA4B,KAAK;AAAA,IAChE;AAEM,UAAAC,IAAUC,EAAS,0BAA0B;AAEnD,WAAAC,EAAQtB,EAAsB,KAAK;AAAA,MACjC,MAAMuB,EAAS,MAAMR,EAAM,IAAI;AAAA,MAC/B,UAAUQ,EAAS,MAAMR,EAAM,cAAcA,EAAM,QAAQ;AAAA,MAC3D,WAAWQ,EAAS,MAAMR,EAAM,SAAS;AAAA,MACzC,YAAYQ,EAAS,MAAMR,EAAM,UAAU;AAAA,MAC3C,SAASQ,EAAS,MAAMR,EAAM,OAAO;AAAA,MACrC,SAASQ,EAAS,MAAMR,EAAM,OAAO;AAAA,MACrC,QAAAT;AAAA,IAAA,CACD;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"RadioGroup.js","sources":["../src/components/RadioGroup/components/VariantButton.vue","../src/components/RadioGroup/components/VariantChip.vue","../src/components/RadioGroup/components/VariantRadio.vue","../src/components/RadioGroup/components/VariantTile.vue","../src/components/RadioGroup/RadioGroup.types.ts","../src/components/RadioGroup/RadioGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantButton must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-flex\" :class=\"classes.root\">\n <div v-for=\"option in options\" :key=\"`${name}-${option.id}`\" :class=\"[{ 'tw-w-full': fullWidth }]\">\n <input\n :id=\"`${name}-${option.id}`\"\n class=\"tw-sr-only\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <label :for=\"`${name}-${option.id}`\">\n {{ option.text }}\n </label>\n </div>\n </div>\n</template>\n\n<style module>\n .root label {\n padding: 8px 30px;\n border: 1px solid var(--color-ice-500);\n font-weight: theme('fontWeight.semibold');\n color: var(--color-ice-700);\n cursor: pointer;\n transition: all 0.2s;\n user-select: none;\n display: block;\n text-align: center;\n }\n\n .root > div:first-child label {\n border-top-left-radius: theme('borderRadius.DEFAULT');\n border-bottom-left-radius: theme('borderRadius.DEFAULT');\n }\n\n .root > div:last-child label {\n border-top-right-radius: theme('borderRadius.DEFAULT');\n border-bottom-right-radius: theme('borderRadius.DEFAULT');\n }\n\n .root > div:not(:first-child) label {\n margin-left: -1px;\n }\n\n .root > div:not(:last-child) label {\n border-right-color: transparent;\n }\n\n .root input:disabled ~ label {\n background-color: var(--color-ice-200);\n color: var(--color-ice-500);\n cursor: auto;\n }\n\n .root input:not(:checked, :disabled) ~ label:hover {\n border-color: var(--color-blue-500);\n color: var(--color-blue-500);\n z-index: 1;\n position: relative;\n }\n\n .root input:checked:not(:disabled) ~ label {\n border-color: var(--color-blue-500);\n color: var(--color-blue-500);\n background-color: var(--color-blue-100);\n z-index: 1;\n position: relative;\n }\n</style>\n","<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantChip must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-my-1.5 tw-flex tw-flex-wrap\" :class=\"classes.root\">\n <div v-for=\"option in options\" :key=\"`${name}-${option.id}`\" :class=\"[{ 'tw-w-full': fullWidth }]\">\n <input\n :id=\"`${name}-${option.id}`\"\n class=\"tw-sr-only\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <label :for=\"`${name}-${option.id}`\">\n {{ option.text }}\n </label>\n </div>\n </div>\n</template>\n\n<style module>\n .root {\n gap: theme('spacing.6');\n }\n\n .root label {\n padding: theme('spacing[1.5]') theme('spacing.3');\n border: 1px solid var(--color-ice-500);\n font-weight: theme('fontWeight.normal');\n color: var(--color-ice-900);\n cursor: pointer;\n transition: all 0.2s;\n user-select: none;\n border-radius: 9999px;\n white-space: nowrap;\n }\n\n .root input:disabled ~ label {\n background-color: var(--color-ice-200);\n color: var(--color-ice-500);\n cursor: auto;\n }\n\n .root input:checked:not(:disabled) ~ label {\n border-color: var(--color-blue-500);\n color: var(--color-white);\n background-color: var(--color-blue-500);\n font-weight: theme('fontWeight.bold');\n }\n\n .root input:not(:checked, :disabled) ~ label:hover {\n border-color: var(--color-blue-500);\n }\n</style>\n","<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantRadio must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-flex tw-flex-wrap\" :class=\"classes.root\">\n <div v-for=\"option in options\" :key=\"`${name}-${option.id}`\" :class=\"[{ 'tw-w-full': fullWidth }]\">\n <input\n :id=\"`${name}-${option.id}`\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <label :for=\"`${name}-${option.id}`\">\n {{ option.text }}\n </label>\n </div>\n </div>\n</template>\n\n<style module>\n .root {\n gap: theme('spacing.6');\n }\n\n .root label {\n font-weight: theme('fontWeight.medium');\n user-select: none;\n cursor: pointer;\n padding: 0 theme('spacing.3');\n }\n\n .root input {\n appearance: none;\n border-radius: 50%;\n width: 20px;\n height: 20px;\n border: 1px solid var(--color-ice-500);\n transition: all 0.2s;\n position: relative;\n cursor: pointer;\n top: 5px;\n }\n\n .root input:checked {\n background-image: radial-gradient(var(--color-blue-500) 50%, transparent 54%);\n }\n\n .root input:disabled {\n background: #eef2f4;\n }\n\n .root input:disabled ~ label {\n cursor: auto;\n }\n\n .root input:disabled:checked {\n background-image: radial-gradient(var(--color-ice-500) 50%, transparent 54%);\n }\n\n .root input:hover:not(:disabled) {\n border-color: var(--color-blue-500);\n }\n\n .root input:hover:not(:disabled) ~ label {\n color: var(--color-ice-900);\n }\n\n .root input:not(:disabled) ~ label:hover {\n color: var(--color-ice-900);\n }\n</style>\n","<script setup lang=\"ts\">\n import { inject, useCssModule } from 'vue';\n\n import { RADIO_GROUP_INJECTION } from '../RadioGroup.keys';\n\n const radioGroupInjection = inject(RADIO_GROUP_INJECTION.key);\n\n if (!radioGroupInjection) {\n throw new Error('VariantTile must be used with a RadioGroup instance.');\n }\n\n const { name, disabled, fullWidth, modelValue, options, update } = radioGroupInjection;\n const classes = useCssModule();\n</script>\n\n<template>\n <div class=\"tw-flex tw-flex-wrap\" :class=\"classes.root\">\n <label\n v-for=\"option in options\"\n :key=\"`${name}-${option.id}`\"\n :class=\"[classes['tile-container'], { 'tw-w-full': fullWidth }]\"\n :for=\"`${name}-${option.id}`\"\n >\n <div\n class=\"tw-flex tw-border\"\n :class=\"[\n classes['tile-header'],\n {\n 'tw-border-blue-500 tw-bg-blue-100 tw-text-ice-900': modelValue === option.value,\n 'tw-border-ice-500 tw-bg-ice-100 tw-text-ice-700': modelValue !== option.value,\n },\n ]\"\n >\n <input\n :id=\"`${name}-${option.id}`\"\n class=\"tw-sr-only\"\n type=\"radio\"\n :name=\"name\"\n :value=\"option.value\"\n :checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n @input=\"update\"\n />\n <div>\n <span>\n {{ option.text }}\n </span>\n </div>\n </div>\n <div\n class=\"tw-border-x tw-border-b\"\n :class=\"[\n classes['tile-body'],\n {\n 'tw-border-ice-500': modelValue !== option.value,\n 'tw-border-blue-500': modelValue === option.value,\n },\n ]\"\n >\n <p class=\"tw-m-0 tw-text-ice-900\">{{ option.subTitle }}</p>\n <p class=\"tw-m-0 tw-text-ice-700\">{{ option.subText }}</p>\n </div>\n </label>\n </div>\n</template>\n\n<style module>\n .root {\n gap: theme('spacing.6');\n }\n\n .root label {\n cursor: pointer;\n user-select: none;\n }\n\n .root input:disabled ~ label {\n cursor: auto;\n }\n\n .root input:hover:not(:disabled) ~ label {\n color: var(--color-ice-900);\n }\n\n .root input:not(:disabled) ~ label:hover {\n color: var(--color-ice-900);\n }\n\n .root label.tile-container {\n border-radius: theme('borderRadius.DEFAULT');\n display: flex;\n flex: 1;\n flex-direction: column;\n }\n\n .tile-header,\n .tile-body {\n transition: all 0.2s;\n }\n\n .tile-header {\n border-radius: theme('borderRadius.DEFAULT') theme('borderRadius.DEFAULT') 0 0;\n padding: theme('spacing.3') 0;\n }\n\n .root label.tile-container:hover .tile-header {\n background-color: var(--color-blue-100) !important;\n border-color: var(--color-blue-500) !important;\n color: var(--color-ice-900) !important;\n transition: all 0.2s;\n }\n\n .tile-body {\n border-radius: 0 0 theme('borderRadius.DEFAULT') theme('borderRadius.DEFAULT');\n background-color: var(--color-white);\n padding: theme('spacing.6');\n display: flex;\n flex-direction: column;\n }\n\n .root label.tile-container:hover .tile-body {\n border-color: var(--color-blue-500) !important;\n transition: all 0.2s;\n }\n\n .root input {\n appearance: none;\n background-color: var(--color-white);\n border-radius: 50%;\n width: 20px;\n height: 20px;\n border: 1px solid var(--color-ice-500);\n transition: all 0.2s;\n position: relative;\n cursor: pointer;\n top: 2px;\n }\n\n .root input ~ div {\n font-weight: theme('fontWeight.medium');\n user-select: none;\n cursor: pointer;\n padding: 0 theme('spacing.3');\n }\n\n .root input:disabled {\n background: #eef2f4;\n }\n\n .root input:checked {\n background-image: radial-gradient(var(--color-blue-500) 50%, transparent 54%);\n }\n\n .root label.tile-container input {\n margin-left: theme('spacing.3');\n }\n\n .root input:disabled:checked {\n background-image: radial-gradient(var(--color-ice-500) 50%, transparent 54%);\n }\n\n @media screen and (width <= 640px) {\n .root {\n flex-direction: column;\n }\n }\n</style>\n","import { ComputedRef } from 'vue';\n\nexport enum RadioGroupVariant {\n Radio = 'radio',\n Button = 'button',\n Chip = 'chip',\n Tile = 'tile',\n}\n\nexport type RadioGroupVariants = `${RadioGroupVariant}`;\n\n/**\n * An individual radio `<input>` within a RadioGroup instance\n */\nexport interface RadioGroupOption {\n /**\n * Disables the RadioGroupOption if truthy\n */\n disabled?: boolean;\n\n /**\n * The unique identifier for the option\n */\n id: number | string;\n\n /**\n * The text to be displayed for the option\n */\n text: string;\n\n /**\n * The value of the option. Used for the modelValue of the RadioGroup.\n */\n value: string;\n\n /**\n * The subtitle for tile variant\n */\n subTitle?: string;\n\n /**\n * The subtext for tile variant\n */\n subText?: string;\n}\n\n/**\n * Properties and utilities provided to children of a RadioGroup instance\n */\nexport interface RadioGroupInjection {\n /**\n * This type should match RadioGroupProps['disabled']\n */\n disabled: ComputedRef<boolean | undefined>;\n\n /**\n * This type should match RadioGroupProps['fullWidth']\n */\n fullWidth: ComputedRef<boolean | undefined>;\n\n /**\n * This type should match RadioGroupProps['modelValue']\n */\n modelValue: ComputedRef<string | undefined>;\n\n /**\n * This type should match RadioGroupProps['name']\n */\n name: ComputedRef<string | undefined>;\n\n /**\n * This type should match RadioGroupProps['options']\n */\n options: ComputedRef<RadioGroupOption[] | undefined>;\n\n /**\n * Triggered when the user changes their selection; updates the v-model.\n */\n update: (e: Event) => void;\n\n /**\n * This type should match RadioGroupProps['variant']\n * @default 'radio'\n */\n variant: ComputedRef<RadioGroupVariants | undefined>;\n}\n","<script lang=\"ts\">\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n import { RadioGroupOption, RadioGroupVariant, RadioGroupVariants } from './RadioGroup.types';\n\n export * from './RadioGroup.keys';\n export * from './RadioGroup.types';\n\n export interface RadioGroupProps extends FieldProps {\n /**\n * Passed to the \"name\" attribute of the `<input>` elements within the RadioGroup.\n */\n name?: string;\n\n /**\n * Deprecated - Compose your radio group with the `Radio` component instead\n * @deprecated\n */\n options?: RadioGroupOption[];\n\n /**\n * The value of the selected radio option. Use this prop with the `update:modelValue` event to create a controlled component.\n */\n modelValue?: RadioGroupOption['value'];\n\n /**\n * @defaultValue 'radio'\n */\n variant?: RadioGroupVariants;\n\n /**\n * Whether the group should expand to the parent's width\n */\n fullWidth?: boolean;\n }\n</script>\n\n<script setup lang=\"ts\">\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, provide } from 'vue';\n\n import VariantButton from './components/VariantButton.vue';\n import VariantChip from './components/VariantChip.vue';\n import VariantRadio from './components/VariantRadio.vue';\n import VariantTile from './components/VariantTile.vue';\n import { RADIO_GROUP_INJECTION } from './RadioGroup.keys';\n\n const variantComponentsMap = {\n [RadioGroupVariant.Button]: VariantButton,\n [RadioGroupVariant.Chip]: VariantChip,\n [RadioGroupVariant.Radio]: VariantRadio,\n [RadioGroupVariant.Tile]: VariantTile,\n };\n\n const props = withDefaults(defineProps<RadioGroupProps>(), {\n variant: 'radio',\n fullWidth: false,\n modelValue: undefined,\n name: undefined,\n options: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Occurs when the user selects a radio option. Also, it enables v-model usage on the component.\n */\n (e: 'update:modelValue', value: RadioGroupOption['value']): void;\n }>();\n\n function update(e: Event) {\n emit('update:modelValue', (e.target as HTMLInputElement).value);\n }\n\n const errorId = uniqueId('radio-group-field-error-');\n\n provide(RADIO_GROUP_INJECTION.key, {\n name: computed(() => props.name),\n disabled: computed(() => props.isDisabled || props.disabled),\n fullWidth: computed(() => props.fullWidth),\n modelValue: computed(() => props.modelValue),\n options: computed(() => props.options),\n variant: computed(() => props.variant),\n update,\n });\n</script>\n\n<template>\n <Field\n v-bind=\"props\"\n class=\"stash-radio-group\"\n data-test=\"stash-radio-group\"\n :aria-errormessage=\"errorId\"\n :error-id=\"errorId\"\n :aria-invalid=\"!!props.errorText\"\n role=\"radiogroup\"\n fieldset\n >\n <slot v-if=\"props.options\">\n <component :is=\"variantComponentsMap[props.variant]\" />\n </slot>\n <div\n v-else\n class=\"tw-flex\"\n :class=\"{\n 'tw-flex-wrap': props.variant !== RadioGroupVariant.Button,\n 'tw-gap-x-1.5 tw-gap-y-3': props.variant === RadioGroupVariant.Chip,\n 'tw-gap-6': props.variant === RadioGroupVariant.Radio || props.variant === RadioGroupVariant.Tile,\n }\"\n >\n <slot></slot>\n </div>\n </Field>\n</template>\n\n<style scoped>\n .stash-radio-group :global(.stash-field-hint),\n .stash-radio-group :global(.stash-field-error) {\n display: block;\n width: 100%;\n }\n\n .stash-radio-group > :global(.stash-label) {\n margin-bottom: 4px;\n }\n</style>\n"],"names":["radioGroupInjection","inject","RADIO_GROUP_INJECTION","name","disabled","fullWidth","modelValue","options","update","classes","useCssModule","RadioGroupVariant","variantComponentsMap","VariantButton","VariantChip","VariantRadio","VariantTile","props","__props","emit","__emit","e","errorId","uniqueId","provide","computed"],"mappings":";;;;;;;;AAKQ,UAAAA,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,wDAAwD;AAG1E,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACPV,UAAAV,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,sDAAsD;AAGxE,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACPV,UAAAV,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,uDAAuD;AAGzE,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACPV,UAAAV,IAAsBC,EAAOC,EAAsB,GAAG;AAE5D,QAAI,CAACF;AACG,YAAA,IAAI,MAAM,sDAAsD;AAGxE,UAAM,EAAE,MAAAG,GAAM,UAAAC,GAAU,WAAAC,GAAW,YAAAC,GAAY,SAAAC,GAAS,QAAAC,EAAW,IAAAR,GAC7DS,IAAUC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACVN,IAAAC,sBAAAA,OACVA,EAAA,QAAQ,SACRA,EAAA,SAAS,UACTA,EAAA,OAAO,QACPA,EAAA,OAAO,QAJGA,IAAAA,KAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;AC6CV,UAAMC,IAAuB;AAAA,MAC3B,CAACD,EAAkB,MAAM,GAAGE;AAAA,MAC5B,CAACF,EAAkB,IAAI,GAAGG;AAAA,MAC1B,CAACH,EAAkB,KAAK,GAAGI;AAAA,MAC3B,CAACJ,EAAkB,IAAI,GAAGK;AAAA,IAAA,GAGtBC,IAAQC,GAQRC,IAAOC;AAOb,aAASZ,EAAOa,GAAU;AACnB,MAAAF,EAAA,qBAAsBE,EAAE,OAA4B,KAAK;AAAA,IAChE;AAEM,UAAAC,IAAUC,EAAS,0BAA0B;AAEnD,WAAAC,EAAQtB,EAAsB,KAAK;AAAA,MACjC,MAAMuB,EAAS,MAAMR,EAAM,IAAI;AAAA,MAC/B,UAAUQ,EAAS,MAAMR,EAAM,cAAcA,EAAM,QAAQ;AAAA,MAC3D,WAAWQ,EAAS,MAAMR,EAAM,SAAS;AAAA,MACzC,YAAYQ,EAAS,MAAMR,EAAM,UAAU;AAAA,MAC3C,SAASQ,EAAS,MAAMR,EAAM,OAAO;AAAA,MACrC,SAASQ,EAAS,MAAMR,EAAM,OAAO;AAAA,MACrC,QAAAT;AAAA,IAAA,CACD;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/Select.js CHANGED
@@ -473,7 +473,7 @@ const tt = { class: "tw-flex tw-h-input tw-items-center tw-text-sm" }, lt = ["id
473
473
  } : void 0
474
474
  ]), 1040, ["class", "error-text", "hint-text"]));
475
475
  }
476
- }), $t = /* @__PURE__ */ et(pt, [["__scopeId", "data-v-c1887a6c"]]);
476
+ }), $t = /* @__PURE__ */ et(pt, [["__scopeId", "data-v-8a707e00"]]);
477
477
  export {
478
478
  $t as default
479
479
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Select.js","sources":["../src/components/Select/Select.vue"],"sourcesContent":["<script lang=\"ts\">\n import Field, { type FieldProps } from '../Field/Field.vue';\n\n // Note: the `trackBy` prop, the `single` prop, and the fact that options can be strings prevents us from knowing the type of the selected options\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type Option = any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type SelectedOptions = Option | Option[];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n export interface SelectProps extends FieldProps {\n /**\n * If there is only 1 option selected, it prevents that option from being de-selected\n */\n preventEmpty?: boolean;\n\n /**\n * @deprecated The `allowEmpty` prop is no longer supported; use `preventEmpty` instead.\n */\n allowEmpty?: string | boolean | null;\n\n /**\n * If `options` are an object, this is what prop to use for display.\n */\n displayBy?: string;\n\n /**\n * List of one or more fields to search on. When empty, displayBy will be used\n */\n searchBy?: string[];\n\n /**\n * Default field to track selected options by.\n */\n trackBy?: string;\n\n /**\n * Placeholder text.\n */\n placeholder?: string;\n\n /**\n * @deprecated Use the `error-text` prop instead\n */\n error?: string;\n\n /**\n * @deprecated Use the `hint-text` prop instead\n */\n hint?: string;\n\n /**\n * Sets a custom icon.\n */\n icon?: string | boolean;\n\n /**\n * Sets a name attribute on the native (hidden) select element\n *\n * Setting the default value to `undefined` so the attribute isn't rendered if not explicitly provided to ensure backwards compatibility with MP django forms\n */\n name?: string;\n\n /**\n * The list of all options to select from.\n */\n options?: Option[] | readonly Option[];\n\n /**\n * Lazily evaluate and set component `options`. Will execute upon user mouseover.\n */\n lazy?: () => Option[];\n\n /**\n * Sets the currently-selected value(s) for the component.\n * Accepts an array of Objects, or a single Object (if `single` is true), or a string\n */\n modelValue?: SelectedOptions;\n\n /**\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: SelectedOptions | null;\n\n /**\n * Hides the search input\n */\n hideSearch?: boolean;\n\n /**\n * @deprecated The `searchable` prop is no longer supported; Use `hideSearch` instead.\n */\n searchable?: string | boolean | null;\n\n /**\n * If true, prevents the search term from being cleared when the drawer is dismissed.\n */\n preserveSearchTerm?: boolean;\n\n /**\n * Prevents the options from being filtered when the search term changes.\n * Allows the parent to filter the options.\n */\n disableFiltering?: boolean;\n\n /**\n * Functions as a single select, if true\n */\n single?: boolean;\n\n /**\n * Prevents the Selected Option from being truncated, if true\n */\n noTruncate?: boolean;\n\n /**\n * On an ajaxed request when the dropdown is open, we may be experiencing slower load times.\n * This flag will drop a loader into the dropdown. Note: This has never been design reviewed.\n */\n loading?: boolean;\n\n /**\n * @deprecated Instead, use the `@search` event (the onSearch prop) and ensure its event handler returns a Promise that resolves after the search is complete.\n */\n searchLoading?: boolean;\n\n /**\n * In the selection text we use this to give more visual distinction to what type of item(s) are selected\n * E.g. 2 customers selected. An empty string will result in `2 selected`.\n */\n selectItemType?: string;\n\n /**\n * Hides the \"check\" icon if truthy\n */\n hideCheck?: boolean;\n\n searchPlaceholder?: string;\n\n /**\n * Equivalent to emitting a \"search\" event, except this prop can be used to populate the list of `options` using an HTTP request because a prop's return value can be received and awaited, unlike an event.\n *\n * **Tip:** to show a loading indicator while searching, return a Promise that resolves after the search is complete.\n *\n * **Warning:** the search input is debounced so there is no need to debounce this function.\n */\n onSearch?: (searchTerm: string) => Promise<void> | void;\n\n /**\n * Uses the \"fuzzy search\" algorithm when searching, if true\n */\n useFuzzySearch?: boolean;\n\n /**\n * Sets the placement of the dropdown\n * @default 'bottom-start'\n */\n menuPlacement?: Placement;\n\n /**\n * Enables teleporting the dropdown\n * @default false\n */\n enableTeleport?: boolean;\n\n /**\n * The selector or element to which the dropdown should be teleported\n * @default `'#stash-menus-mount-node'`\n */\n teleportTo?: string | HTMLElement;\n }\n</script>\n\n<script setup lang=\"ts\">\n import { autoUpdate, flip, offset, type Placement, type Side, size, useFloating } from '@floating-ui/vue';\n import logger from '@leaflink/snitch';\n import debounce from 'lodash-es/debounce';\n import isEmpty from 'lodash-es/isEmpty';\n import isEqual from 'lodash-es/isEqual';\n import isPlainObject from 'lodash-es/isPlainObject';\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, nextTick, onMounted, onUnmounted, type Ref, ref, useAttrs, useSlots, watch } from 'vue';\n\n import useSearch from '../../composables/useSearch/useSearch';\n import { DEBOUNCE, KEY_CODES } from '../../constants';\n import vClickoutside from '../../directives/clickoutside/clickoutside';\n import { DEFAULT_MENUS_PLUGIN_NODE_ID } from '../../plugins/MenusPlugin';\n import Chip from '../Chip/Chip.vue';\n import Icon, { IconName } from '../Icon/Icon.vue';\n\n const GAP_HEIGHT = 6;\n\n const MENU_MAX_HEIGHT_PX = 300;\n\n defineOptions({\n name: 'll-select',\n });\n\n const props = withDefaults(defineProps<SelectProps>(), {\n preventEmpty: false,\n allowEmpty: null,\n displayBy: 'name',\n searchBy: () => [],\n trackBy: 'id',\n placeholder: 'Select option',\n error: '',\n hint: '',\n icon: 'caret-down',\n name: undefined,\n options: () => [],\n lazy: undefined,\n modelValue: () => [],\n value: null,\n hideSearch: false,\n searchable: null,\n preserveSearchTerm: false,\n disableFiltering: false,\n single: false,\n noTruncate: false,\n loading: false,\n searchLoading: false,\n selectItemType: '',\n hideCheck: false,\n searchPlaceholder: 'Search',\n onSearch: undefined,\n useFuzzySearch: false,\n menuPlacement: 'bottom-start',\n enableTeleport: false,\n teleportTo: `#${DEFAULT_MENUS_PLUGIN_NODE_ID}`,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', selectedOptions: SelectedOptions): void;\n /**\n * Emitted the selected value(s) a cleared.\n */\n (e: 'clear'): void;\n /**\n * Emitted when an option is added.\n */\n (e: 'add', optionAdded: Option): void;\n /**\n * Emitted when an option is removed.\n */\n (e: 'remove', optionRemoved: Option, index: number): void;\n /**\n * Emitted when the select is opened.\n */\n (e: 'opened'): void;\n /**\n * Emitted when the select is closed.\n */\n (e: 'closed', selectedOptions: SelectedOptions): void;\n }>();\n\n const attrs = useAttrs();\n const slots = useSlots();\n\n const selectRef = ref<HTMLDivElement | null>(null);\n const contentRef = ref<HTMLDivElement | null>(null);\n const optionsWrapperRef = ref<HTMLDivElement | null>(null);\n const chipsRef = ref<HTMLUListElement | null>(null);\n const searchRef = ref<HTMLInputElement | null>(null);\n\n const chipsHeight = ref(0);\n const contentHeight = ref(0);\n const internalValue = ref<Option[]>([]) as Ref<Option[]>;\n const searchFor = ref<(searchTerm: string) => Option[]>(() => []); // initialized in the watch handler for options\n const searchTerm = ref('');\n const activeIndex = ref(-1);\n const isActive = ref(false);\n const pendingSearchRequests = ref({});\n const shouldUseLazyOptions = ref(false);\n\n const { floatingStyles } = useFloating(selectRef, optionsWrapperRef, {\n whileElementsMounted: autoUpdate,\n placement: props.menuPlacement,\n middleware: [\n flip(),\n offset(({ rects }) => {\n if (props.menuPlacement.includes('bottom' satisfies Side) && chipsHeight.value > contentHeight.value) {\n return chipsHeight.value - rects.reference.height + GAP_HEIGHT;\n } else {\n return GAP_HEIGHT;\n }\n }),\n size({\n apply({ availableHeight, elements, rects }) {\n if (props.enableTeleport) {\n Object.assign(elements.floating.style, {\n maxWidth: `${rects.reference.width}px`,\n maxHeight: `${Math.min(availableHeight, MENU_MAX_HEIGHT_PX)}px`,\n });\n }\n },\n }),\n ],\n });\n\n const isSearchable = computed(() => !props.hideSearch && props.searchable !== 'false' && props.searchable !== false);\n\n const internalOptions = computed(() => {\n let OPTIONS = (shouldUseLazyOptions.value && props.lazy ? props.lazy() : props.options).filter(Boolean); // prevent [undefined]\n\n // if `options` is an array... but not an array of objects\n if (!isPlainObject(OPTIONS[0])) {\n OPTIONS = OPTIONS.map((name, id) => ({ name, id } as unknown as Option));\n }\n\n return OPTIONS;\n });\n\n const readOnlyValue = computed(() => {\n if (!internalValue.value.length) {\n return '';\n }\n\n return internalValue.value.map((option) => option[props.displayBy] || option).join(', ');\n });\n\n const isSearching = computed(() => props.searchLoading || Object.keys(pendingSearchRequests.value).length > 0);\n\n const filteredOptions = computed(() => {\n if (props.disableFiltering || !(searchTerm.value || '').trim()) {\n return internalOptions.value;\n }\n\n return searchFor.value(searchTerm.value);\n });\n\n const isChipsOneLine = computed(() => chipsHeight.value <= contentHeight.value);\n\n const shouldShowTotal = computed(\n () => !isActive.value && !props.single && !isChipsOneLine.value && internalValue.value.length,\n );\n\n const clearSelectionText = computed(() => {\n return [internalValue.value.length, props.selectItemType, 'selected'].filter(Boolean).join(' ');\n });\n\n const shouldPreventEmpty = computed(() => {\n return props.preventEmpty || props.allowEmpty === 'false' || props.allowEmpty === false;\n });\n\n watch(searchTerm, (currentValue, oldValue) => oldValue !== currentValue && setIndex(-1));\n\n watch(internalValue, () => updateChipsHeight(), { deep: true });\n\n watch(\n () => props.modelValue,\n (value) => {\n // multiple\n if (Array.isArray(value)) {\n const newValue = value.filter(Boolean); // sometimes modelValue is [undefined]\n\n if (!isEqual(newValue, internalValue.value)) {\n internalValue.value = newValue;\n }\n\n return;\n }\n\n // single; if value is a non-empty string or a non-empty object\n if (!isEmpty(value)) {\n if (!isEqual(value, internalValue.value[0])) {\n internalValue.value[0] = value;\n }\n\n return;\n }\n\n // if value is an empty object or falsy\n internalValue.value = [];\n },\n { immediate: true },\n );\n\n watch(\n () => props.options,\n () => {\n const { searchFor: searchForFn } = useSearch<Option>({\n items: computed(() => internalOptions.value),\n fieldNames: props.searchBy.length ? props.searchBy : [props.displayBy],\n trackBy: props.trackBy,\n });\n\n searchFor.value = (searchTerm) => searchForFn(searchTerm, { fuzzy: props.useFuzzySearch });\n },\n { immediate: true },\n );\n\n const clear = () => {\n internalValue.value = [];\n emit('update:model-value', props.single ? undefined : internalValue.value);\n emit('clear');\n };\n\n /**\n * Adds (or selects) an option.\n * @param {Option} option The selected option\n */\n const handleSelect = (option?: Option) => {\n if (props.disabled || !option || (typeof option === 'object' && 'disabled' in option && option?.disabled)) {\n return;\n }\n\n if (!isSelected(option)) {\n if (props.single) {\n internalValue.value = [];\n }\n\n internalValue.value.push(option);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('add', option);\n } else {\n remove(option);\n }\n\n if (props.single) {\n dismiss();\n }\n };\n\n const handleSelectChange = (evt) => {\n // If this is a single select, just pass along the option\n // This short-circuit is purely for readability, as the iterations for multi would also work for single selects\n if (props.single) {\n const selectedOption = internalOptions.value.find((opt) => opt[props.trackBy]?.toString() === evt.target.value);\n handleSelect(selectedOption);\n return;\n }\n\n // Spread the HTMLCollection (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) to array so we can invoke `find`\n const selectedOptions = [...evt.target.selectedOptions];\n\n // If it's a multiselect, the HTML select used for the screen reader can have multiple items\n // deselected and a new one selected in one change event.\n // We need to reconcile the new selected options with our internal state\n\n // 1. remove all the items in our internal state that are no longer present\n // (Spread this array so we can remove items from it while iterating)\n for (const internalOption of [...internalValue.value]) {\n const selectedOption = selectedOptions.find((opt) => opt.value === internalOption[props.trackBy]?.toString());\n\n if (!selectedOption) {\n remove(internalOption);\n }\n }\n\n // 2. add all the items that are now selected if needed\n for (const selectedOption of selectedOptions) {\n const internalOption = internalOptions.value.find(\n (opt) => opt[props.trackBy]?.toString() === selectedOption.value,\n );\n\n if (isSelected(internalOption)) {\n continue;\n } else {\n handleSelect(internalOption);\n }\n }\n };\n\n /**\n * Removes (or un-selects) an option.\n * @param {Option} option The option to be removed\n */\n const remove = (option: Option) => {\n if (props.disabled || (shouldPreventEmpty.value && internalValue.value.length === 1)) {\n return;\n }\n\n const index = internalValue.value.findIndex((opt) => opt[props.trackBy] === option[props.trackBy]);\n\n if (index === -1) {\n logger.warn('ll-select: could not find option to remove', option);\n return;\n }\n\n internalValue.value.splice(index, 1);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('remove', option, index);\n };\n\n /**\n * Opens the `options` drawer and provides focus.\n */\n const open = () => {\n if (isActive.value) {\n return;\n }\n\n updateChipsHeight();\n\n isActive.value = true;\n emit('opened');\n\n if (isSearchable.value) {\n nextTick(() => {\n searchRef.value?.focus({ preventScroll: true });\n });\n }\n };\n\n /**\n * Closes the drawer and cleans up a few settings.\n */\n const dismiss = () => {\n if (isActive.value) {\n emit('closed', internalValue.value);\n }\n\n setIndex(-1);\n isActive.value = false;\n\n if (!props.preserveSearchTerm) {\n searchTerm.value = '';\n }\n\n // The optional chaining here is required.\n // There are cases when the component is removed from the DOM and the ref is lost.\n // .ie when the component is used in a modal and the modal is dismissed.\n // Tracked in this ticket: F-1893\n contentRef.value?.blur(); // if the component remains in focus, clicking it again will not open the options list\n };\n\n /**\n * Scroll list items when up and down keys are pressed\n * @param {KeyboardEvent} e The native keydown event.\n */\n const scrollItems = async (e: KeyboardEvent) => {\n await nextTick();\n\n if (!contentRef.value) {\n return;\n }\n\n const allowedKeyCodes = [KEY_CODES.ENTER, KEY_CODES.ESCAPE] as number[];\n\n // Wait for highlighted item update after using keyboard (`onKeyDown`)\n if (!allowedKeyCodes.includes(e.keyCode)) {\n const el = contentRef.value.querySelector('.stash-select__option--highlighted');\n const elBCR = el?.getBoundingClientRect();\n const parentBCR = contentRef.value.getBoundingClientRect();\n\n if (!elBCR || !parentBCR) {\n return;\n }\n\n if (elBCR.bottom >= parentBCR.bottom || elBCR.top <= parentBCR.top + elBCR.height) {\n el?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n }\n };\n\n /**\n * Sets the `activeIndex` on hover, so that the user may then fine-tune\n * their selection with the arrow keys.\n */\n const setIndex = (index: number) => {\n activeIndex.value = index;\n };\n\n /**\n * Handles all user keyboard input on the Dropdown.\n * @param {KeyboardEvent} e The native keydown event.\n */\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.keyCode === KEY_CODES.ESCAPE) {\n dismiss();\n } else if (e.keyCode === KEY_CODES.DOWN && activeIndex.value < filteredOptions.value.length - 1) {\n isActive.value && activeIndex.value++;\n } else if (e.keyCode === KEY_CODES.UP && activeIndex.value > 0) {\n isActive.value && activeIndex.value--;\n\n // Scroll to top when search is available and first element is highlighted (Using 100 to be sure that it'll be sure that search is visible)\n if (optionsWrapperRef.value && isSearchable.value && activeIndex.value === 0) {\n optionsWrapperRef.value.scrollTop -= 100;\n }\n } else if (e.keyCode === KEY_CODES.ENTER && activeIndex.value !== -1) {\n isActive.value && handleSelect(filteredOptions.value[activeIndex.value]);\n } else {\n return;\n }\n\n e.preventDefault(); // if keyDown did something / we didn't return\n scrollItems(e);\n };\n\n const handleSearchInput = debounce(function () {\n search();\n }, DEBOUNCE.FAST);\n\n const search = async function () {\n if (!props.onSearch) {\n return;\n }\n\n const requestId = uniqueId('search-request-');\n\n try {\n pendingSearchRequests.value[requestId] = true;\n await props.onSearch(searchTerm.value);\n } finally {\n delete pendingSearchRequests.value[requestId];\n }\n };\n\n /**\n * Dynamically sets classes on an `option` in the drawer.\n * @param {Option} option The `option` for which to generate classes\n * @param {number} index The index of the `option` in the list of `options`\n */\n const optionClass = (option: Option, index: number) => {\n return [\n {\n 'stash-select__option': true,\n 'stash-select__option--disabled':\n option && typeof option === 'object' && 'disabled' in option && option?.disabled,\n 'stash-select__option--highlighted': activeIndex.value === index,\n 'stash-select__option--selected': isSelected(option),\n },\n\n // @deprecated\n {\n 'tw-bg-ice-200': activeIndex.value === index,\n 'tw-bg-blue-100 tw-text-ice-700': isSelected(option),\n },\n ];\n };\n\n /**\n * Finds out if the given item is already present in the selected options\n * @param {Option} option The item to check\n * @returns {boolean} Returns true if element is selected\n */\n const isSelected = (option?: Option | null): boolean => {\n if (option === undefined || option === null) {\n return false;\n }\n\n return internalValue.value.some((value) => value?.[props.trackBy] === option[props.trackBy]);\n };\n\n /**\n * Checks if the element is within the Select menu by checking both the content and the teleportTo element\n */\n function isElementWithinSelectMenu(element: Node | null) {\n if (!element) {\n return false;\n }\n\n const isElementInMenu = contentRef.value?.contains(element);\n const isElementInTeleportedMenu = optionsWrapperRef.value?.contains(element);\n\n return isElementInMenu || isElementInTeleportedMenu;\n }\n\n function handleFocusOut(focusEvent: FocusEvent) {\n const nextFocusedElement = focusEvent.relatedTarget as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(nextFocusedElement);\n\n if (!isFocusedElementInMenu && nextFocusedElement) {\n dismiss();\n }\n }\n\n function handleOutsideClick(event: MouseEvent) {\n const target = event.target as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(target);\n\n if (!isFocusedElementInMenu) {\n dismiss();\n }\n }\n\n /**\n * Handles the mouse leave of the select and clears item highlight\n */\n const handleMouseLeave = () => {\n setIndex(-1);\n };\n\n /**\n * Update height of chips container\n */\n const updateChipsHeight = async () => {\n // Wait for the DOM to update so we can get an accurate value for chips' clientHeight\n await nextTick();\n\n if (chipsRef.value && contentRef.value) {\n contentHeight.value = contentHeight.value === 0 ? contentRef.value.clientHeight : contentHeight.value;\n chipsHeight.value = chipsRef.value.clientHeight;\n }\n };\n\n onMounted(() => {\n if (attrs.onInput) {\n throw new Error('ll-select: use the @update:model-value event instead of @input.');\n }\n\n window.addEventListener('resize', updateChipsHeight);\n\n updateChipsHeight();\n });\n\n /**\n * Remove event listener to handle component width when resizing window\n */\n onUnmounted(() => {\n window.removeEventListener('resize', updateChipsHeight);\n });\n</script>\n\n<template>\n <Field\n v-bind=\"props\"\n class=\"input ll-select stash-select\"\n data-test=\"stash-select\"\n :class=\"attrs.class\"\n :error-text=\"props.errorText || props.error\"\n :hint-text=\"props.hintText || props.hint\"\n >\n <template v-if=\"props.isReadOnly\" #default=\"{ fieldId, labelId }\">\n <div class=\"tw-flex tw-h-input tw-items-center tw-text-sm\">\n <span :id=\"fieldId\" :aria-labelledby=\"labelId\" class=\"show-empty tw-h-min\">\n {{ readOnlyValue }}\n </span>\n </div>\n </template>\n <template v-else #default=\"{ fieldId, fieldErrorId, hasError }\">\n <!-- SCREEN READER ONLY -->\n <select\n :id=\"fieldId\"\n :aria-errormessage=\"fieldErrorId\"\n :aria-invalid=\"hasError\"\n class=\"tw-sr-only\"\n :disabled=\"props.disabled\"\n :multiple=\"!props.single\"\n :name=\"props.name\"\n @change=\"handleSelectChange\"\n >\n <option\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`srOnlyOption-${index}`\"\n :selected=\"isSelected(option)\"\n :value=\"option[props.trackBy]\"\n >\n {{ option[props.displayBy] || '' }}\n </option>\n\n <!-- Empty option to allow deselecting non-multiselects -->\n <option value=\"\"></option>\n </select>\n\n <!-- SELECT -->\n <div\n ref=\"selectRef\"\n v-clickoutside=\"handleOutsideClick\"\n role=\"listbox\"\n aria-hidden=\"true\"\n class=\"stash-select__content-wrapper\"\n :aria-controls=\"'listbox-' + fieldId\"\n :aria-expanded=\"isActive\"\n :aria-label=\"props.placeholder\"\n :aria-disabled=\"props.disabled || undefined\"\n :class=\"[\n {\n 'stash-select--disabled': !!props.disabled,\n 'stash-select--error': !!(props.errorText || props.error),\n 'stash-select--active': isActive,\n 'stash-select--single': props.single,\n },\n\n // @deprecated\n {\n 'is-active': isActive,\n 'is-single': props.single,\n 'is-disabled': props.disabled,\n },\n ]\"\n @keydown=\"onKeyDown\"\n @keyup.esc=\"dismiss\"\n >\n <!-- CONTENT -->\n <div\n :id=\"'listbox-' + fieldId\"\n ref=\"contentRef\"\n class=\"input-field stash-select__content\"\n tabindex=\"0\"\n @focusin=\"open\"\n @focusin.once=\"shouldUseLazyOptions = true\"\n @focusout=\"handleFocusOut\"\n @keydown=\"open\"\n @mouseleave=\"handleMouseLeave\"\n >\n <!-- CHIPS -->\n <ul ref=\"chipsRef\" class=\"stash-select__chips\">\n <!-- PLACEHOLDER -->\n <li\n v-if=\"!internalValue.length\"\n class=\"stash-select__placeholder tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n {{ props.placeholder }}\n </li>\n\n <template v-if=\"props.single\">\n <li\n v-for=\"option of internalValue\"\n :key=\"`chip-${option[props.trackBy]}`\"\n class=\"stash-select__selected tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n <!-- @slot Selected value(s) custom text. Exposes the option object. -->\n <slot name=\"selected\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n <button\n tabindex=\"-1\"\n class=\"stash-select__remove\"\n @keypress.enter.prevent=\"remove(option)\"\n @mousedown.prevent.stop=\"remove(option)\"\n >\n <Icon icon=\"close\" name=\"close\" size=\"small\" />\n </button>\n </slot>\n </li>\n </template>\n\n <template v-else>\n <li v-for=\"option of internalValue\" :key=\"`chip-${option[props.trackBy]}`\" class=\"tw-inline-block\">\n <Chip\n bg-color=\"blue-500\"\n is-removable\n text-color=\"white\"\n class=\"stash-select__chip\"\n @remove=\"remove(option)\"\n >\n {{ option[props.displayBy] || option }}\n </Chip>\n </li>\n </template>\n </ul>\n\n <!-- TOTAL -->\n <div v-if=\"shouldShowTotal\" class=\"stash-select__total\">\n <Chip bg-color=\"blue-500\" is-removable text-color=\"white\" class=\"stash-select__chip\" @remove=\"clear\">\n {{ clearSelectionText }}\n </Chip>\n </div>\n\n <!-- TOGGLE -->\n <Icon\n v-if=\"props.icon\"\n class=\"stash-select__icon\"\n data-test=\"stash-select|toggle-icon\"\n :name=\"(props.icon as IconName)\"\n :class=\"{ 'tw-text-ice-500': props.disabled }\"\n @mousedown.prevent=\"isActive && dismiss()\"\n />\n <!-- DRAWER -->\n <Teleport :to=\"props.teleportTo\" :disabled=\"!props.enableTeleport\">\n <transition name=\"fade\">\n <div\n v-show=\"isActive && !props.disabled\"\n ref=\"optionsWrapperRef\"\n class=\"stash-select__border-selector tw-w-full tw-shadow-2xl\"\n :style=\"floatingStyles\"\n @click.stop\n >\n <!-- SEARCH -->\n <div v-if=\"isSearchable\" class=\"tw-flex tw-items-center tw-border-b tw-border-blue-500 tw-pr-1.5\">\n <input\n ref=\"searchRef\"\n v-model=\"searchTerm\"\n type=\"text\"\n autocomplete=\"off\"\n class=\"stash-select__search\"\n :data-test=\"attrs['data-test'] ? attrs['data-test'] + '-search' : 'stash-select|search'\"\n :placeholder=\"props.searchPlaceholder\"\n :spellcheck=\"false\"\n @input=\"handleSearchInput\"\n />\n <Icon name=\"search\" class=\"tw-text-ice-500\" />\n </div>\n\n <!-- OPTIONS -->\n <ul class=\"stash-select__options options tw-my-1.5 tw-w-full tw-border-white tw-bg-white\">\n <li\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`option-${option[props.trackBy]}`\"\n :data-test=\"option[props.trackBy]\"\n :class=\"optionClass(option, index)\"\n @click=\"handleSelect(option)\"\n @mouseenter.self=\"setIndex(index)\"\n >\n <!-- @slot Select custom option text. Exposes the option object. -->\n <slot name=\"option\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n </slot>\n\n <Icon\n v-if=\"isSelected(option) && !props.hideCheck\"\n class=\"tw-ml-auto tw-text-blue-500\"\n name=\"check\"\n />\n </li>\n <li v-show=\"props.loading || isSearching\" class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\">\n <Icon\n data-test=\"stash-select|options-loading\"\n name=\"working\"\n class=\"tw-animate-spin tw-text-ice-500\"\n />\n </li>\n <li\n v-show=\"!props.loading && !isSearching && !filteredOptions.length\"\n class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\"\n data-test=\"stash-select|no-options\"\n >\n <!-- @slot No options text. -->\n <slot name=\"no-options\"> No options </slot>\n </li>\n </ul>\n </div>\n </transition>\n </Teleport>\n </div>\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style scoped>\n .stash-select {\n position: relative;\n }\n\n .stash-select__content,\n .stash-select__content-wrapper {\n height: theme('height.input');\n }\n\n .stash-select__content {\n cursor: pointer;\n overflow: hidden;\n position: absolute;\n background: theme('colors.white');\n border: 1px solid var(--color-ice-500);\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n outline: none;\n padding: 0 !important;\n width: 100%;\n\n &:hover {\n border-color: var(--color-ice-500);\n }\n\n &:focus-within {\n border-color: var(--color-blue-500) !important;\n }\n }\n\n .stash-select--disabled .stash-select__content {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none !important;\n }\n\n .stash-select--active .stash-select__content {\n cursor: default;\n height: auto;\n min-width: theme('width.select-menu');\n overflow: visible;\n z-index: theme('zIndex.control');\n box-shadow: theme('boxShadow.DEFAULT');\n }\n\n .stash-select--error :where(.stash-select__content, .stash-select__content:hover:not(:focus)) {\n border-color: var(--color-red-500) !important;\n }\n\n .stash-select__placeholder {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__placeholder {\n cursor: default;\n }\n\n .stash-select__icon {\n color: var(--color-ice-700);\n cursor: pointer;\n pointer-events: none;\n position: absolute;\n right: theme('spacing[1.5]');\n top: theme('spacing[1.5]');\n }\n\n .stash-select--disabled .stash-select__icon {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__icon {\n pointer-events: auto;\n }\n\n .stash-select__chips {\n /* distribute (or split?) the margin between chip and chips container. TODO? */\n --half-space: calc(theme('spacing[1.5]') / 2);\n\n padding: var(--half-space);\n padding-right: theme('spacing.9');\n }\n\n /* chip AND placeholder AND selected: */\n .stash-select__chips > li {\n cursor: pointer;\n height: theme('height.chip');\n margin: var(--half-space);\n max-width: 100%;\n }\n\n .stash-select--disabled .stash-select__chips > li {\n cursor: default;\n }\n\n .stash-select__content-wrapper .stash-select__chip {\n border-radius: theme('borderRadius.DEFAULT');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n height: theme('height.chip');\n max-width: inherit;\n text-transform: none;\n }\n\n .stash-select--disabled .stash-select__chip {\n background: var(--color-ice-500);\n color: theme('colors.white');\n }\n\n .stash-select__content-wrapper .stash-select__total .stash-select__chip {\n font-weight: theme('fontWeight.normal');\n }\n\n .stash-select__selected {\n color: var(--color-ice-700);\n }\n\n .stash-select--disabled .stash-select__selected {\n color: var(--color-ice-500);\n }\n\n .stash-select__selected :where(.stash-select__remove) {\n display: none;\n }\n\n .stash-select__total {\n display: block;\n font-weight: theme('fontWeight.semibold');\n line-height: theme('lineHeight.button');\n position: absolute;\n right: theme('spacing.6');\n white-space: nowrap;\n z-index: theme('zIndex.content');\n\n /* -- design TBD --- */\n top: 0;\n left: 0;\n bottom: 0;\n color: theme('colors.white');\n background: white;\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__search {\n position: relative;\n width: 100%;\n height: theme('height.input');\n background: theme('colors.white');\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n outline: none;\n padding: 0 theme('spacing.3');\n border: 0 !important;\n }\n\n .stash-select__border-selector {\n background: white;\n border-radius: theme('borderRadius.DEFAULT');\n border: 1px solid var(--color-blue-500);\n max-height: theme('maxHeight.select-menu');\n overflow: auto;\n z-index: theme('zIndex.dialog');\n }\n\n .stash-select__option {\n align-items: center;\n border-radius: theme('borderRadius.DEFAULT');\n cursor: pointer;\n display: flex;\n line-height: theme('spacing.6');\n margin: theme('spacing[1.5]');\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__option--disabled {\n color: var(--color-ice-500);\n cursor: not-allowed;\n }\n\n .stash-select__option--highlighted {\n background-color: var(--color-ice-200);\n }\n\n .stash-select__option--selected {\n background-color: var(--color-blue-100);\n color: var(--color-ice-700);\n }\n</style>\n"],"names":["props","__props","emit","__emit","attrs","useAttrs","slots","useSlots","selectRef","ref","contentRef","optionsWrapperRef","chipsRef","searchRef","chipsHeight","contentHeight","internalValue","searchFor","searchTerm","activeIndex","isActive","pendingSearchRequests","shouldUseLazyOptions","floatingStyles","useFloating","autoUpdate","flip","offset","rects","size","availableHeight","elements","isSearchable","computed","internalOptions","OPTIONS","isPlainObject","name","id","readOnlyValue","option","isSearching","filteredOptions","isChipsOneLine","shouldShowTotal","clearSelectionText","shouldPreventEmpty","watch","currentValue","oldValue","setIndex","updateChipsHeight","value","newValue","isEqual","isEmpty","searchForFn","useSearch","clear","handleSelect","isSelected","remove","dismiss","handleSelectChange","evt","selectedOption","opt","_a","selectedOptions","internalOption","index","logger","open","nextTick","scrollItems","KEY_CODES","el","elBCR","parentBCR","onKeyDown","handleSearchInput","debounce","search","DEBOUNCE","requestId","uniqueId","optionClass","isElementWithinSelectMenu","element","isElementInMenu","isElementInTeleportedMenu","_b","handleFocusOut","focusEvent","nextFocusedElement","handleOutsideClick","event","target","handleMouseLeave","onMounted","onUnmounted"],"mappings":"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"}
1
+ {"version":3,"file":"Select.js","sources":["../src/components/Select/Select.vue"],"sourcesContent":["<script lang=\"ts\">\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n // Note: the `trackBy` prop, the `single` prop, and the fact that options can be strings prevents us from knowing the type of the selected options\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type Option = any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type SelectedOptions = Option | Option[];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n export interface SelectProps extends FieldProps {\n /**\n * If there is only 1 option selected, it prevents that option from being de-selected\n */\n preventEmpty?: boolean;\n\n /**\n * @deprecated The `allowEmpty` prop is no longer supported; use `preventEmpty` instead.\n */\n allowEmpty?: string | boolean | null;\n\n /**\n * If `options` are an object, this is what prop to use for display.\n */\n displayBy?: string;\n\n /**\n * List of one or more fields to search on. When empty, displayBy will be used\n */\n searchBy?: string[];\n\n /**\n * Default field to track selected options by.\n */\n trackBy?: string;\n\n /**\n * Placeholder text.\n */\n placeholder?: string;\n\n /**\n * @deprecated Use the `error-text` prop instead\n */\n error?: string;\n\n /**\n * @deprecated Use the `hint-text` prop instead\n */\n hint?: string;\n\n /**\n * Sets a custom icon.\n */\n icon?: string | boolean;\n\n /**\n * Sets a name attribute on the native (hidden) select element\n *\n * Setting the default value to `undefined` so the attribute isn't rendered if not explicitly provided to ensure backwards compatibility with MP django forms\n */\n name?: string;\n\n /**\n * The list of all options to select from.\n */\n options?: Option[] | readonly Option[];\n\n /**\n * Lazily evaluate and set component `options`. Will execute upon user mouseover.\n */\n lazy?: () => Option[];\n\n /**\n * Sets the currently-selected value(s) for the component.\n * Accepts an array of Objects, or a single Object (if `single` is true), or a string\n */\n modelValue?: SelectedOptions;\n\n /**\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: SelectedOptions | null;\n\n /**\n * Hides the search input\n */\n hideSearch?: boolean;\n\n /**\n * @deprecated The `searchable` prop is no longer supported; Use `hideSearch` instead.\n */\n searchable?: string | boolean | null;\n\n /**\n * If true, prevents the search term from being cleared when the drawer is dismissed.\n */\n preserveSearchTerm?: boolean;\n\n /**\n * Prevents the options from being filtered when the search term changes.\n * Allows the parent to filter the options.\n */\n disableFiltering?: boolean;\n\n /**\n * Functions as a single select, if true\n */\n single?: boolean;\n\n /**\n * Prevents the Selected Option from being truncated, if true\n */\n noTruncate?: boolean;\n\n /**\n * On an ajaxed request when the dropdown is open, we may be experiencing slower load times.\n * This flag will drop a loader into the dropdown. Note: This has never been design reviewed.\n */\n loading?: boolean;\n\n /**\n * @deprecated Instead, use the `@search` event (the onSearch prop) and ensure its event handler returns a Promise that resolves after the search is complete.\n */\n searchLoading?: boolean;\n\n /**\n * In the selection text we use this to give more visual distinction to what type of item(s) are selected\n * E.g. 2 customers selected. An empty string will result in `2 selected`.\n */\n selectItemType?: string;\n\n /**\n * Hides the \"check\" icon if truthy\n */\n hideCheck?: boolean;\n\n searchPlaceholder?: string;\n\n /**\n * Equivalent to emitting a \"search\" event, except this prop can be used to populate the list of `options` using an HTTP request because a prop's return value can be received and awaited, unlike an event.\n *\n * **Tip:** to show a loading indicator while searching, return a Promise that resolves after the search is complete.\n *\n * **Warning:** the search input is debounced so there is no need to debounce this function.\n */\n onSearch?: (searchTerm: string) => Promise<void> | void;\n\n /**\n * Uses the \"fuzzy search\" algorithm when searching, if true\n */\n useFuzzySearch?: boolean;\n\n /**\n * Sets the placement of the dropdown\n * @default 'bottom-start'\n */\n menuPlacement?: Placement;\n\n /**\n * Enables teleporting the dropdown\n * @default false\n */\n enableTeleport?: boolean;\n\n /**\n * The selector or element to which the dropdown should be teleported\n * @default `'#stash-menus-mount-node'`\n */\n teleportTo?: string | HTMLElement;\n }\n</script>\n\n<script setup lang=\"ts\">\n import { autoUpdate, flip, offset, type Placement, type Side, size, useFloating } from '@floating-ui/vue';\n import logger from '@leaflink/snitch';\n import debounce from 'lodash-es/debounce';\n import isEmpty from 'lodash-es/isEmpty';\n import isEqual from 'lodash-es/isEqual';\n import isPlainObject from 'lodash-es/isPlainObject';\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, nextTick, onMounted, onUnmounted, type Ref, ref, useAttrs, useSlots, watch } from 'vue';\n\n import useSearch from '../../composables/useSearch/useSearch';\n import { DEBOUNCE, KEY_CODES } from '../../constants';\n import vClickoutside from '../../directives/clickoutside/clickoutside';\n import { DEFAULT_MENUS_PLUGIN_NODE_ID } from '../../plugins/MenusPlugin';\n import Chip from '../Chip/Chip.vue';\n import Icon, { IconName } from '../Icon/Icon.vue';\n\n const GAP_HEIGHT = 6;\n\n const MENU_MAX_HEIGHT_PX = 300;\n\n defineOptions({\n name: 'll-select',\n });\n\n const props = withDefaults(defineProps<SelectProps>(), {\n preventEmpty: false,\n allowEmpty: null,\n displayBy: 'name',\n searchBy: () => [],\n trackBy: 'id',\n placeholder: 'Select option',\n error: '',\n hint: '',\n icon: 'caret-down',\n name: undefined,\n options: () => [],\n lazy: undefined,\n modelValue: () => [],\n value: null,\n hideSearch: false,\n searchable: null,\n preserveSearchTerm: false,\n disableFiltering: false,\n single: false,\n noTruncate: false,\n loading: false,\n searchLoading: false,\n selectItemType: '',\n hideCheck: false,\n searchPlaceholder: 'Search',\n onSearch: undefined,\n useFuzzySearch: false,\n menuPlacement: 'bottom-start',\n enableTeleport: false,\n teleportTo: `#${DEFAULT_MENUS_PLUGIN_NODE_ID}`,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', selectedOptions: SelectedOptions): void;\n /**\n * Emitted the selected value(s) a cleared.\n */\n (e: 'clear'): void;\n /**\n * Emitted when an option is added.\n */\n (e: 'add', optionAdded: Option): void;\n /**\n * Emitted when an option is removed.\n */\n (e: 'remove', optionRemoved: Option, index: number): void;\n /**\n * Emitted when the select is opened.\n */\n (e: 'opened'): void;\n /**\n * Emitted when the select is closed.\n */\n (e: 'closed', selectedOptions: SelectedOptions): void;\n }>();\n\n const attrs = useAttrs();\n const slots = useSlots();\n\n const selectRef = ref<HTMLDivElement | null>(null);\n const contentRef = ref<HTMLDivElement | null>(null);\n const optionsWrapperRef = ref<HTMLDivElement | null>(null);\n const chipsRef = ref<HTMLUListElement | null>(null);\n const searchRef = ref<HTMLInputElement | null>(null);\n\n const chipsHeight = ref(0);\n const contentHeight = ref(0);\n const internalValue = ref<Option[]>([]) as Ref<Option[]>;\n const searchFor = ref<(searchTerm: string) => Option[]>(() => []); // initialized in the watch handler for options\n const searchTerm = ref('');\n const activeIndex = ref(-1);\n const isActive = ref(false);\n const pendingSearchRequests = ref({});\n const shouldUseLazyOptions = ref(false);\n\n const { floatingStyles } = useFloating(selectRef, optionsWrapperRef, {\n whileElementsMounted: autoUpdate,\n placement: props.menuPlacement,\n middleware: [\n flip(),\n offset(({ rects }) => {\n if (props.menuPlacement.includes('bottom' satisfies Side) && chipsHeight.value > contentHeight.value) {\n return chipsHeight.value - rects.reference.height + GAP_HEIGHT;\n } else {\n return GAP_HEIGHT;\n }\n }),\n size({\n apply({ availableHeight, elements, rects }) {\n if (props.enableTeleport) {\n Object.assign(elements.floating.style, {\n maxWidth: `${rects.reference.width}px`,\n maxHeight: `${Math.min(availableHeight, MENU_MAX_HEIGHT_PX)}px`,\n });\n }\n },\n }),\n ],\n });\n\n const isSearchable = computed(() => !props.hideSearch && props.searchable !== 'false' && props.searchable !== false);\n\n const internalOptions = computed(() => {\n let OPTIONS = (shouldUseLazyOptions.value && props.lazy ? props.lazy() : props.options).filter(Boolean); // prevent [undefined]\n\n // if `options` is an array... but not an array of objects\n if (!isPlainObject(OPTIONS[0])) {\n OPTIONS = OPTIONS.map((name, id) => ({ name, id } as unknown as Option));\n }\n\n return OPTIONS;\n });\n\n const readOnlyValue = computed(() => {\n if (!internalValue.value.length) {\n return '';\n }\n\n return internalValue.value.map((option) => option[props.displayBy] || option).join(', ');\n });\n\n const isSearching = computed(() => props.searchLoading || Object.keys(pendingSearchRequests.value).length > 0);\n\n const filteredOptions = computed(() => {\n if (props.disableFiltering || !(searchTerm.value || '').trim()) {\n return internalOptions.value;\n }\n\n return searchFor.value(searchTerm.value);\n });\n\n const isChipsOneLine = computed(() => chipsHeight.value <= contentHeight.value);\n\n const shouldShowTotal = computed(\n () => !isActive.value && !props.single && !isChipsOneLine.value && internalValue.value.length,\n );\n\n const clearSelectionText = computed(() => {\n return [internalValue.value.length, props.selectItemType, 'selected'].filter(Boolean).join(' ');\n });\n\n const shouldPreventEmpty = computed(() => {\n return props.preventEmpty || props.allowEmpty === 'false' || props.allowEmpty === false;\n });\n\n watch(searchTerm, (currentValue, oldValue) => oldValue !== currentValue && setIndex(-1));\n\n watch(internalValue, () => updateChipsHeight(), { deep: true });\n\n watch(\n () => props.modelValue,\n (value) => {\n // multiple\n if (Array.isArray(value)) {\n const newValue = value.filter(Boolean); // sometimes modelValue is [undefined]\n\n if (!isEqual(newValue, internalValue.value)) {\n internalValue.value = newValue;\n }\n\n return;\n }\n\n // single; if value is a non-empty string or a non-empty object\n if (!isEmpty(value)) {\n if (!isEqual(value, internalValue.value[0])) {\n internalValue.value[0] = value;\n }\n\n return;\n }\n\n // if value is an empty object or falsy\n internalValue.value = [];\n },\n { immediate: true },\n );\n\n watch(\n () => props.options,\n () => {\n const { searchFor: searchForFn } = useSearch<Option>({\n items: computed(() => internalOptions.value),\n fieldNames: props.searchBy.length ? props.searchBy : [props.displayBy],\n trackBy: props.trackBy,\n });\n\n searchFor.value = (searchTerm) => searchForFn(searchTerm, { fuzzy: props.useFuzzySearch });\n },\n { immediate: true },\n );\n\n const clear = () => {\n internalValue.value = [];\n emit('update:model-value', props.single ? undefined : internalValue.value);\n emit('clear');\n };\n\n /**\n * Adds (or selects) an option.\n * @param {Option} option The selected option\n */\n const handleSelect = (option?: Option) => {\n if (props.disabled || !option || (typeof option === 'object' && 'disabled' in option && option?.disabled)) {\n return;\n }\n\n if (!isSelected(option)) {\n if (props.single) {\n internalValue.value = [];\n }\n\n internalValue.value.push(option);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('add', option);\n } else {\n remove(option);\n }\n\n if (props.single) {\n dismiss();\n }\n };\n\n const handleSelectChange = (evt) => {\n // If this is a single select, just pass along the option\n // This short-circuit is purely for readability, as the iterations for multi would also work for single selects\n if (props.single) {\n const selectedOption = internalOptions.value.find((opt) => opt[props.trackBy]?.toString() === evt.target.value);\n handleSelect(selectedOption);\n return;\n }\n\n // Spread the HTMLCollection (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) to array so we can invoke `find`\n const selectedOptions = [...evt.target.selectedOptions];\n\n // If it's a multiselect, the HTML select used for the screen reader can have multiple items\n // deselected and a new one selected in one change event.\n // We need to reconcile the new selected options with our internal state\n\n // 1. remove all the items in our internal state that are no longer present\n // (Spread this array so we can remove items from it while iterating)\n for (const internalOption of [...internalValue.value]) {\n const selectedOption = selectedOptions.find((opt) => opt.value === internalOption[props.trackBy]?.toString());\n\n if (!selectedOption) {\n remove(internalOption);\n }\n }\n\n // 2. add all the items that are now selected if needed\n for (const selectedOption of selectedOptions) {\n const internalOption = internalOptions.value.find(\n (opt) => opt[props.trackBy]?.toString() === selectedOption.value,\n );\n\n if (isSelected(internalOption)) {\n continue;\n } else {\n handleSelect(internalOption);\n }\n }\n };\n\n /**\n * Removes (or un-selects) an option.\n * @param {Option} option The option to be removed\n */\n const remove = (option: Option) => {\n if (props.disabled || (shouldPreventEmpty.value && internalValue.value.length === 1)) {\n return;\n }\n\n const index = internalValue.value.findIndex((opt) => opt[props.trackBy] === option[props.trackBy]);\n\n if (index === -1) {\n logger.warn('ll-select: could not find option to remove', option);\n return;\n }\n\n internalValue.value.splice(index, 1);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('remove', option, index);\n };\n\n /**\n * Opens the `options` drawer and provides focus.\n */\n const open = () => {\n if (isActive.value) {\n return;\n }\n\n updateChipsHeight();\n\n isActive.value = true;\n emit('opened');\n\n if (isSearchable.value) {\n nextTick(() => {\n searchRef.value?.focus({ preventScroll: true });\n });\n }\n };\n\n /**\n * Closes the drawer and cleans up a few settings.\n */\n const dismiss = () => {\n if (isActive.value) {\n emit('closed', internalValue.value);\n }\n\n setIndex(-1);\n isActive.value = false;\n\n if (!props.preserveSearchTerm) {\n searchTerm.value = '';\n }\n\n // The optional chaining here is required.\n // There are cases when the component is removed from the DOM and the ref is lost.\n // .ie when the component is used in a modal and the modal is dismissed.\n // Tracked in this ticket: F-1893\n contentRef.value?.blur(); // if the component remains in focus, clicking it again will not open the options list\n };\n\n /**\n * Scroll list items when up and down keys are pressed\n * @param {KeyboardEvent} e The native keydown event.\n */\n const scrollItems = async (e: KeyboardEvent) => {\n await nextTick();\n\n if (!contentRef.value) {\n return;\n }\n\n const allowedKeyCodes = [KEY_CODES.ENTER, KEY_CODES.ESCAPE] as number[];\n\n // Wait for highlighted item update after using keyboard (`onKeyDown`)\n if (!allowedKeyCodes.includes(e.keyCode)) {\n const el = contentRef.value.querySelector('.stash-select__option--highlighted');\n const elBCR = el?.getBoundingClientRect();\n const parentBCR = contentRef.value.getBoundingClientRect();\n\n if (!elBCR || !parentBCR) {\n return;\n }\n\n if (elBCR.bottom >= parentBCR.bottom || elBCR.top <= parentBCR.top + elBCR.height) {\n el?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n }\n };\n\n /**\n * Sets the `activeIndex` on hover, so that the user may then fine-tune\n * their selection with the arrow keys.\n */\n const setIndex = (index: number) => {\n activeIndex.value = index;\n };\n\n /**\n * Handles all user keyboard input on the Dropdown.\n * @param {KeyboardEvent} e The native keydown event.\n */\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.keyCode === KEY_CODES.ESCAPE) {\n dismiss();\n } else if (e.keyCode === KEY_CODES.DOWN && activeIndex.value < filteredOptions.value.length - 1) {\n isActive.value && activeIndex.value++;\n } else if (e.keyCode === KEY_CODES.UP && activeIndex.value > 0) {\n isActive.value && activeIndex.value--;\n\n // Scroll to top when search is available and first element is highlighted (Using 100 to be sure that it'll be sure that search is visible)\n if (optionsWrapperRef.value && isSearchable.value && activeIndex.value === 0) {\n optionsWrapperRef.value.scrollTop -= 100;\n }\n } else if (e.keyCode === KEY_CODES.ENTER && activeIndex.value !== -1) {\n isActive.value && handleSelect(filteredOptions.value[activeIndex.value]);\n } else {\n return;\n }\n\n e.preventDefault(); // if keyDown did something / we didn't return\n scrollItems(e);\n };\n\n const handleSearchInput = debounce(function () {\n search();\n }, DEBOUNCE.FAST);\n\n const search = async function () {\n if (!props.onSearch) {\n return;\n }\n\n const requestId = uniqueId('search-request-');\n\n try {\n pendingSearchRequests.value[requestId] = true;\n await props.onSearch(searchTerm.value);\n } finally {\n delete pendingSearchRequests.value[requestId];\n }\n };\n\n /**\n * Dynamically sets classes on an `option` in the drawer.\n * @param {Option} option The `option` for which to generate classes\n * @param {number} index The index of the `option` in the list of `options`\n */\n const optionClass = (option: Option, index: number) => {\n return [\n {\n 'stash-select__option': true,\n 'stash-select__option--disabled':\n option && typeof option === 'object' && 'disabled' in option && option?.disabled,\n 'stash-select__option--highlighted': activeIndex.value === index,\n 'stash-select__option--selected': isSelected(option),\n },\n\n // @deprecated\n {\n 'tw-bg-ice-200': activeIndex.value === index,\n 'tw-bg-blue-100 tw-text-ice-700': isSelected(option),\n },\n ];\n };\n\n /**\n * Finds out if the given item is already present in the selected options\n * @param {Option} option The item to check\n * @returns {boolean} Returns true if element is selected\n */\n const isSelected = (option?: Option | null): boolean => {\n if (option === undefined || option === null) {\n return false;\n }\n\n return internalValue.value.some((value) => value?.[props.trackBy] === option[props.trackBy]);\n };\n\n /**\n * Checks if the element is within the Select menu by checking both the content and the teleportTo element\n */\n function isElementWithinSelectMenu(element: Node | null) {\n if (!element) {\n return false;\n }\n\n const isElementInMenu = contentRef.value?.contains(element);\n const isElementInTeleportedMenu = optionsWrapperRef.value?.contains(element);\n\n return isElementInMenu || isElementInTeleportedMenu;\n }\n\n function handleFocusOut(focusEvent: FocusEvent) {\n const nextFocusedElement = focusEvent.relatedTarget as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(nextFocusedElement);\n\n if (!isFocusedElementInMenu && nextFocusedElement) {\n dismiss();\n }\n }\n\n function handleOutsideClick(event: MouseEvent) {\n const target = event.target as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(target);\n\n if (!isFocusedElementInMenu) {\n dismiss();\n }\n }\n\n /**\n * Handles the mouse leave of the select and clears item highlight\n */\n const handleMouseLeave = () => {\n setIndex(-1);\n };\n\n /**\n * Update height of chips container\n */\n const updateChipsHeight = async () => {\n // Wait for the DOM to update so we can get an accurate value for chips' clientHeight\n await nextTick();\n\n if (chipsRef.value && contentRef.value) {\n contentHeight.value = contentHeight.value === 0 ? contentRef.value.clientHeight : contentHeight.value;\n chipsHeight.value = chipsRef.value.clientHeight;\n }\n };\n\n onMounted(() => {\n if (attrs.onInput) {\n throw new Error('ll-select: use the @update:model-value event instead of @input.');\n }\n\n window.addEventListener('resize', updateChipsHeight);\n\n updateChipsHeight();\n });\n\n /**\n * Remove event listener to handle component width when resizing window\n */\n onUnmounted(() => {\n window.removeEventListener('resize', updateChipsHeight);\n });\n</script>\n\n<template>\n <Field\n v-bind=\"props\"\n class=\"input ll-select stash-select\"\n data-test=\"stash-select\"\n :class=\"attrs.class\"\n :error-text=\"props.errorText || props.error\"\n :hint-text=\"props.hintText || props.hint\"\n >\n <template v-if=\"props.isReadOnly\" #default=\"{ fieldId, labelId }\">\n <div class=\"tw-flex tw-h-input tw-items-center tw-text-sm\">\n <span :id=\"fieldId\" :aria-labelledby=\"labelId\" class=\"show-empty tw-h-min\">\n {{ readOnlyValue }}\n </span>\n </div>\n </template>\n <template v-else #default=\"{ fieldId, fieldErrorId, hasError }\">\n <!-- SCREEN READER ONLY -->\n <select\n :id=\"fieldId\"\n :aria-errormessage=\"fieldErrorId\"\n :aria-invalid=\"hasError\"\n class=\"tw-sr-only\"\n :disabled=\"props.disabled\"\n :multiple=\"!props.single\"\n :name=\"props.name\"\n @change=\"handleSelectChange\"\n >\n <option\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`srOnlyOption-${index}`\"\n :selected=\"isSelected(option)\"\n :value=\"option[props.trackBy]\"\n >\n {{ option[props.displayBy] || '' }}\n </option>\n\n <!-- Empty option to allow deselecting non-multiselects -->\n <option value=\"\"></option>\n </select>\n\n <!-- SELECT -->\n <div\n ref=\"selectRef\"\n v-clickoutside=\"handleOutsideClick\"\n role=\"listbox\"\n aria-hidden=\"true\"\n class=\"stash-select__content-wrapper\"\n :aria-controls=\"'listbox-' + fieldId\"\n :aria-expanded=\"isActive\"\n :aria-label=\"props.placeholder\"\n :aria-disabled=\"props.disabled || undefined\"\n :class=\"[\n {\n 'stash-select--disabled': !!props.disabled,\n 'stash-select--error': !!(props.errorText || props.error),\n 'stash-select--active': isActive,\n 'stash-select--single': props.single,\n },\n\n // @deprecated\n {\n 'is-active': isActive,\n 'is-single': props.single,\n 'is-disabled': props.disabled,\n },\n ]\"\n @keydown=\"onKeyDown\"\n @keyup.esc=\"dismiss\"\n >\n <!-- CONTENT -->\n <div\n :id=\"'listbox-' + fieldId\"\n ref=\"contentRef\"\n class=\"input-field stash-select__content\"\n tabindex=\"0\"\n @focusin=\"open\"\n @focusin.once=\"shouldUseLazyOptions = true\"\n @focusout=\"handleFocusOut\"\n @keydown=\"open\"\n @mouseleave=\"handleMouseLeave\"\n >\n <!-- CHIPS -->\n <ul ref=\"chipsRef\" class=\"stash-select__chips\">\n <!-- PLACEHOLDER -->\n <li\n v-if=\"!internalValue.length\"\n class=\"stash-select__placeholder tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n {{ props.placeholder }}\n </li>\n\n <template v-if=\"props.single\">\n <li\n v-for=\"option of internalValue\"\n :key=\"`chip-${option[props.trackBy]}`\"\n class=\"stash-select__selected tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n <!-- @slot Selected value(s) custom text. Exposes the option object. -->\n <slot name=\"selected\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n <button\n tabindex=\"-1\"\n class=\"stash-select__remove\"\n @keypress.enter.prevent=\"remove(option)\"\n @mousedown.prevent.stop=\"remove(option)\"\n >\n <Icon icon=\"close\" name=\"close\" size=\"small\" />\n </button>\n </slot>\n </li>\n </template>\n\n <template v-else>\n <li v-for=\"option of internalValue\" :key=\"`chip-${option[props.trackBy]}`\" class=\"tw-inline-block\">\n <Chip\n bg-color=\"blue-500\"\n is-removable\n text-color=\"white\"\n class=\"stash-select__chip\"\n @remove=\"remove(option)\"\n >\n {{ option[props.displayBy] || option }}\n </Chip>\n </li>\n </template>\n </ul>\n\n <!-- TOTAL -->\n <div v-if=\"shouldShowTotal\" class=\"stash-select__total\">\n <Chip bg-color=\"blue-500\" is-removable text-color=\"white\" class=\"stash-select__chip\" @remove=\"clear\">\n {{ clearSelectionText }}\n </Chip>\n </div>\n\n <!-- TOGGLE -->\n <Icon\n v-if=\"props.icon\"\n class=\"stash-select__icon\"\n data-test=\"stash-select|toggle-icon\"\n :name=\"(props.icon as IconName)\"\n :class=\"{ 'tw-text-ice-500': props.disabled }\"\n @mousedown.prevent=\"isActive && dismiss()\"\n />\n <!-- DRAWER -->\n <Teleport :to=\"props.teleportTo\" :disabled=\"!props.enableTeleport\">\n <transition name=\"fade\">\n <div\n v-show=\"isActive && !props.disabled\"\n ref=\"optionsWrapperRef\"\n class=\"stash-select__border-selector tw-w-full tw-shadow-2xl\"\n :style=\"floatingStyles\"\n @click.stop\n >\n <!-- SEARCH -->\n <div v-if=\"isSearchable\" class=\"tw-flex tw-items-center tw-border-b tw-border-blue-500 tw-pr-1.5\">\n <input\n ref=\"searchRef\"\n v-model=\"searchTerm\"\n type=\"text\"\n autocomplete=\"off\"\n class=\"stash-select__search\"\n :data-test=\"attrs['data-test'] ? attrs['data-test'] + '-search' : 'stash-select|search'\"\n :placeholder=\"props.searchPlaceholder\"\n :spellcheck=\"false\"\n @input=\"handleSearchInput\"\n />\n <Icon name=\"search\" class=\"tw-text-ice-500\" />\n </div>\n\n <!-- OPTIONS -->\n <ul class=\"stash-select__options options tw-my-1.5 tw-w-full tw-border-white tw-bg-white\">\n <li\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`option-${option[props.trackBy]}`\"\n :data-test=\"option[props.trackBy]\"\n :class=\"optionClass(option, index)\"\n @click=\"handleSelect(option)\"\n @mouseenter.self=\"setIndex(index)\"\n >\n <!-- @slot Select custom option text. Exposes the option object. -->\n <slot name=\"option\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n </slot>\n\n <Icon\n v-if=\"isSelected(option) && !props.hideCheck\"\n class=\"tw-ml-auto tw-text-blue-500\"\n name=\"check\"\n />\n </li>\n <li v-show=\"props.loading || isSearching\" class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\">\n <Icon\n data-test=\"stash-select|options-loading\"\n name=\"working\"\n class=\"tw-animate-spin tw-text-ice-500\"\n />\n </li>\n <li\n v-show=\"!props.loading && !isSearching && !filteredOptions.length\"\n class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\"\n data-test=\"stash-select|no-options\"\n >\n <!-- @slot No options text. -->\n <slot name=\"no-options\"> No options </slot>\n </li>\n </ul>\n </div>\n </transition>\n </Teleport>\n </div>\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style scoped>\n .stash-select {\n position: relative;\n }\n\n .stash-select__content,\n .stash-select__content-wrapper {\n height: theme('height.input');\n }\n\n .stash-select__content {\n cursor: pointer;\n overflow: hidden;\n position: absolute;\n background: theme('colors.white');\n border: 1px solid var(--color-ice-500);\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n outline: none;\n padding: 0 !important;\n width: 100%;\n\n &:hover {\n border-color: var(--color-ice-500);\n }\n\n &:focus-within {\n border-color: var(--color-blue-500) !important;\n }\n }\n\n .stash-select--disabled .stash-select__content {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none !important;\n }\n\n .stash-select--active .stash-select__content {\n cursor: default;\n height: auto;\n min-width: theme('width.select-menu');\n overflow: visible;\n z-index: theme('zIndex.control');\n box-shadow: theme('boxShadow.DEFAULT');\n }\n\n .stash-select--error :where(.stash-select__content, .stash-select__content:hover:not(:focus)) {\n border-color: var(--color-red-500) !important;\n }\n\n .stash-select__placeholder {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__placeholder {\n cursor: default;\n }\n\n .stash-select__icon {\n color: var(--color-ice-700);\n cursor: pointer;\n pointer-events: none;\n position: absolute;\n right: theme('spacing[1.5]');\n top: theme('spacing[1.5]');\n }\n\n .stash-select--disabled .stash-select__icon {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__icon {\n pointer-events: auto;\n }\n\n .stash-select__chips {\n /* distribute (or split?) the margin between chip and chips container. TODO? */\n --half-space: calc(theme('spacing[1.5]') / 2);\n\n padding: var(--half-space);\n padding-right: theme('spacing.9');\n }\n\n /* chip AND placeholder AND selected: */\n .stash-select__chips > li {\n cursor: pointer;\n height: theme('height.chip');\n margin: var(--half-space);\n max-width: 100%;\n }\n\n .stash-select--disabled .stash-select__chips > li {\n cursor: default;\n }\n\n .stash-select__content-wrapper .stash-select__chip {\n border-radius: theme('borderRadius.DEFAULT');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n height: theme('height.chip');\n max-width: inherit;\n text-transform: none;\n }\n\n .stash-select--disabled .stash-select__chip {\n background: var(--color-ice-500);\n color: theme('colors.white');\n }\n\n .stash-select__content-wrapper .stash-select__total .stash-select__chip {\n font-weight: theme('fontWeight.normal');\n }\n\n .stash-select__selected {\n color: var(--color-ice-700);\n }\n\n .stash-select--disabled .stash-select__selected {\n color: var(--color-ice-500);\n }\n\n .stash-select__selected :where(.stash-select__remove) {\n display: none;\n }\n\n .stash-select__total {\n display: block;\n font-weight: theme('fontWeight.semibold');\n line-height: theme('lineHeight.button');\n position: absolute;\n right: theme('spacing.6');\n white-space: nowrap;\n z-index: theme('zIndex.content');\n\n /* -- design TBD --- */\n top: 0;\n left: 0;\n bottom: 0;\n color: theme('colors.white');\n background: white;\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__search {\n position: relative;\n width: 100%;\n height: theme('height.input');\n background: theme('colors.white');\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n outline: none;\n padding: 0 theme('spacing.3');\n border: 0 !important;\n }\n\n .stash-select__border-selector {\n background: white;\n border-radius: theme('borderRadius.DEFAULT');\n border: 1px solid var(--color-blue-500);\n max-height: theme('maxHeight.select-menu');\n overflow: auto;\n z-index: theme('zIndex.dialog');\n }\n\n .stash-select__option {\n align-items: center;\n border-radius: theme('borderRadius.DEFAULT');\n cursor: pointer;\n display: flex;\n line-height: theme('spacing.6');\n margin: theme('spacing[1.5]');\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__option--disabled {\n color: var(--color-ice-500);\n cursor: not-allowed;\n }\n\n .stash-select__option--highlighted {\n background-color: var(--color-ice-200);\n }\n\n .stash-select__option--selected {\n background-color: var(--color-blue-100);\n color: var(--color-ice-700);\n }\n</style>\n"],"names":["props","__props","emit","__emit","attrs","useAttrs","slots","useSlots","selectRef","ref","contentRef","optionsWrapperRef","chipsRef","searchRef","chipsHeight","contentHeight","internalValue","searchFor","searchTerm","activeIndex","isActive","pendingSearchRequests","shouldUseLazyOptions","floatingStyles","useFloating","autoUpdate","flip","offset","rects","size","availableHeight","elements","isSearchable","computed","internalOptions","OPTIONS","isPlainObject","name","id","readOnlyValue","option","isSearching","filteredOptions","isChipsOneLine","shouldShowTotal","clearSelectionText","shouldPreventEmpty","watch","currentValue","oldValue","setIndex","updateChipsHeight","value","newValue","isEqual","isEmpty","searchForFn","useSearch","clear","handleSelect","isSelected","remove","dismiss","handleSelectChange","evt","selectedOption","opt","_a","selectedOptions","internalOption","index","logger","open","nextTick","scrollItems","KEY_CODES","el","elBCR","parentBCR","onKeyDown","handleSearchInput","debounce","search","DEBOUNCE","requestId","uniqueId","optionClass","isElementWithinSelectMenu","element","isElementInMenu","isElementInTeleportedMenu","_b","handleFocusOut","focusEvent","nextFocusedElement","handleOutsideClick","event","target","handleMouseLeave","onMounted","onUnmounted"],"mappings":"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"}