@leaflink/stash 50.5.3 → 50.6.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":"DataViewFilters.js","sources":["../src/components/DataViewFilters/DataViewFilters.types.ts","../src/components/DataViewFilters/useFilters.ts","../src/components/DataViewFilters/DataViewFilters.vue"],"sourcesContent":["import { UseFiltersReturnType } from './useFilters';\n\n/**\n * A function that is called when the user clicks one of the \"Apply\" buttons in DataViewFilters.\n *\n * A return value of `{ preventDismiss: true }` can be used to prevent the DataViewFilters drawer or a FilterDropdown from closing, such as when some filters have invalid values.\n */\nexport type OnApplyFilters = () => Promise<{ preventDismiss?: boolean } | void> | { preventDismiss?: boolean } | void;\n\nexport enum DrawerStyle {\n /** Displays all groups and their fields without the need for navigating submenus. */\n Cascade = 'cascade',\n /** Displays only group names or fields; navigation between the group names and a submenu of fields is required. */\n Nested = 'nested',\n}\n\nexport type DrawerStyles = `${DrawerStyle}`;\n\nexport interface DataViewFiltersUtilsInjection {\n useFiltersInstance?: UseFiltersReturnType;\n drawerStyle?: DrawerStyles;\n}\n","import cloneDeep from 'lodash-es/cloneDeep';\nimport { computed, ComputedRef, Ref, ref } from 'vue';\n\nimport isDefined from '../../composables/useValidation/utils/isDefined';\nimport DataView from '../DataView/DataView.vue';\n\ntype DataViewInstance = InstanceType<typeof DataView>;\n\n/**\n * Contains metadata and configuration for the filters.\n * @see https://www.typescriptlang.org/docs/handbook/2/mapped-types.html\n */\nexport type UseFiltersSchema<Values extends object, Groups extends string> = {\n [Property in keyof Values]: {\n defaultValue?: Values[Property];\n group?: Groups;\n isActive?: (value: Values[Property]) => boolean;\n };\n};\n\nexport interface UseFiltersArgs<Values extends object, Groups extends string> {\n schema: UseFiltersSchema<Values, Groups>;\n /** A ref for an instance of DataView */\n dataViewRef: Ref<unknown>; // Note: using `Ref<InstanceType<typeof DataView>>` here causes type errors when providing a value for this argument\n}\n\nexport interface UseFiltersReturnType<Values = object, Groups extends string = string> {\n applyFilters: () => void;\n resetAllFilters: () => void;\n resetFilterGroup: (group: string) => void; // Note: group is intentionally not typed as `Groups` since there is no way to pass in a Groups type to UseFiltersReturnType within DataViewFilters.vue\n undoWorkingFilters: () => void;\n activeFiltersCounts: ComputedRef<Record<Groups, number>>;\n totalActiveFiltersCount: ComputedRef<number>;\n appliedFilters: Ref<Values>;\n workingFilters: Ref<Values>;\n}\n\n/**\n * Provides utility functions for working with `DataViewFilters`.\n */\nexport function useFilters<Values extends object, Groups extends string>({\n schema,\n dataViewRef,\n}: UseFiltersArgs<Values, Groups>): UseFiltersReturnType<Values, Groups> {\n const appliedFilters = ref({}) as Ref<Values>;\n const workingFilters = ref({}) as Ref<Values>;\n\n for (const filterName in schema) {\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n appliedFilters.value[filterName] = schema[filterName].defaultValue;\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n workingFilters.value[filterName] = schema[filterName].defaultValue;\n }\n\n const dvRef = dataViewRef as Ref<DataViewInstance>;\n\n /**\n * For when an \"Apply\" button is clicked. It does the following:\n * 1) applies the working filter state\n * 2) sets the current page to 1\n * 3) emits the \"update\" event from DataView\n */\n function applyFilters() {\n appliedFilters.value = cloneDeep(workingFilters.value);\n dvRef.value.updateCurrentFilters(appliedFilters.value, { shouldEmit: true });\n }\n\n /**\n * For when a \"Reset all\" button is clicked. It does the following:\n * 1) applies the defaultValue filter values to all filters\n * 2) sets the current page to 1\n * 3) emits the \"update\" event from DataView\n */\n function resetAllFilters() {\n for (const filterName in schema) {\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n appliedFilters.value[filterName] = schema[filterName].defaultValue;\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n workingFilters.value[filterName] = schema[filterName].defaultValue;\n }\n\n dvRef.value.updateCurrentFilters(appliedFilters.value, { shouldEmit: true });\n }\n\n /**\n * For when a \"Reset\" button is clicked. It does the following:\n * 1) applies the defaultValue filter values to the given filter group\n * 2) sets the current page to 1\n * 3) emits the \"update\" event from DataView\n */\n function resetFilterGroup(group: Groups) {\n for (const filterName in schema) {\n if (schema[filterName].group === group) {\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n appliedFilters.value[filterName] = schema[filterName].defaultValue;\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n workingFilters.value[filterName] = schema[filterName].defaultValue;\n }\n }\n\n dvRef.value.updateCurrentFilters(appliedFilters.value, { shouldEmit: true });\n }\n\n /**\n * Resets the `workingFilters` to match the `appliedFilters`. This can be used when the FilterDrawer or a FilterDropdown is dismissed without clicking \"Reset\" or \"Apply\".\n */\n function undoWorkingFilters() {\n workingFilters.value = cloneDeep(appliedFilters.value);\n }\n\n const activeFiltersCounts = computed(() => {\n const counts = {} as Record<Groups, number>;\n\n for (const filterName in schema) {\n const config = schema[filterName];\n const value = appliedFilters.value[filterName];\n const isActive = config.isActive?.(value) || isDefined(value);\n\n if (isActive && config.group) {\n counts[config.group] = (counts[config.group] ?? 0) + 1;\n }\n }\n\n return counts;\n });\n\n const totalActiveFiltersCount = computed(() => {\n let count = 0;\n\n for (const filterName in schema) {\n const config = schema[filterName];\n const value = appliedFilters.value[filterName];\n const isActive = config.isActive?.(value) || isDefined(value);\n\n if (isActive) {\n count += 1;\n }\n }\n\n return count;\n });\n\n return {\n applyFilters,\n resetAllFilters,\n // @ts-expect-error \"could be instantiated with a different subtype\": TODO: figure out how to resolve the types\n resetFilterGroup,\n undoWorkingFilters,\n activeFiltersCounts,\n totalActiveFiltersCount,\n appliedFilters,\n workingFilters,\n };\n}\n\nexport default useFilters;\n","<script lang=\"ts\">\n export * from './DataViewFilters.keys';\n export * from './DataViewFilters.types';\n export * from './useFilters';\n</script>\n\n<script setup lang=\"ts\">\n import { computed, inject, provide, ref, watch } from 'vue';\n\n import useMediaQuery from '../../composables/useMediaQuery/useMediaQuery';\n import { SCREEN_SIZES } from '../../constants';\n import { t } from '../../locale';\n import Box from '../Box/Box.vue';\n import Button from '../Button/Button.vue';\n import { DATA_VIEW_INJECTION } from '../DataView/DataView.vue';\n import FilterChip from '../FilterChip/FilterChip.vue';\n import Icon from '../Icon/Icon.vue';\n import Label from '../Label/Label.vue';\n import Modal, { type ModalProps } from '../Modal/Modal.vue';\n import SearchBar, { SearchBarProps } from '../SearchBar/SearchBar.vue';\n import { DATA_VIEW_FILTERS_UTILS_INJECTION } from './DataViewFilters.keys';\n import type { DrawerStyles, OnApplyFilters } from './DataViewFilters.types';\n import type { UseFiltersReturnType } from './useFilters';\n\n export interface DataViewFiltersProps {\n filtersLabelText?: string;\n /**\n * Props to pass to the `SearchBar` component.\n */\n searchBarProps?: SearchBarProps;\n showSearch?: boolean;\n /** 'cascade' displays all fields within every filter group; 'nested' displays only the group names and requires clicking a group to view its fields. */\n drawerStyle?: DrawerStyles;\n drawerProps?: ModalProps;\n /**\n * @deprecated The `activeGroup` prop is a sufficient replacement for this prop. A falsy `activeGroup` will hide the button and a truthy `activeGroup` will show it (when the `drawerStyle` is 'nested').\n *\n * **Note:** This prop has no effect when using a \"cascade\" `drawerStyle`.\n */\n showDrawerPreviousButton?: boolean;\n /**\n * Required when using filters. This prop should contain the return value of the `useFilters()` composable.\n */\n useFiltersInstance?: UseFiltersReturnType;\n onApply?: OnApplyFilters;\n /**\n * The name of the active filter group. The active filter group is determined by which instance of FilterDropdown or FilterDrawerItem is open.\n *\n * **Note:** This prop is required when using a \"nested\" `drawerStyle`, but has no effect when using a \"cascade\" `drawerStyle`.\n */\n activeGroup?: string;\n }\n\n export interface DataViewFiltersSlots {\n default?: void;\n drawer?: void;\n 'filters-label'?: void;\n }\n\n const props = withDefaults(defineProps<DataViewFiltersProps>(), {\n filtersLabelText: t('ll.filterBy'),\n isLoading: false,\n drawerStyle: 'cascade',\n drawerProps: undefined,\n searchBarProps: undefined,\n showDrawerPreviousButton: false,\n showSearch: true,\n useFiltersInstance: undefined,\n onApply: undefined,\n activeGroup: undefined,\n });\n\n const emit = defineEmits<{\n /** When the drawer is opened */\n (e: 'open-drawer'): void;\n /** When the drawer is dismissed */\n (e: 'dismiss'): void;\n /** When the \"Previous\" button in the header is clicked */\n (e: 'previous'): void;\n /** When the \"Reset\" button is clicked while viewing a filter group */\n (e: 'reset-group'): void;\n /** When one of the \"Reset All\" buttons is clicked */\n (e: 'reset-all'): void;\n }>();\n\n const slots = defineSlots<DataViewFiltersSlots>();\n\n const isDesktop = useMediaQuery(`(min-width: ${SCREEN_SIZES.lg})`);\n const isViewingGroupsMenu = computed(() => props.drawerStyle === 'nested' && !props.activeGroup);\n const isViewingSingleGroup = computed(() => props.drawerStyle === 'nested' && props.activeGroup);\n\n const {\n density,\n isLoading: isDataViewLoading,\n isWithinModule,\n currentSearch,\n updateCurrentSearch,\n } = inject(DATA_VIEW_INJECTION.key, DATA_VIEW_INJECTION.defaults);\n\n provide(DATA_VIEW_FILTERS_UTILS_INJECTION.key, {\n useFiltersInstance: props.useFiltersInstance,\n drawerStyle: props.drawerStyle,\n });\n\n const totalActiveFiltersCount = computed(() => props.useFiltersInstance?.totalActiveFiltersCount.value ?? 0);\n /** The number of active filters in the currently active group. This is only used when the drawerStyle is 'nested'. */\n const activeGroupActiveFiltersCount = computed(\n () => Number(props.activeGroup && props.useFiltersInstance?.activeFiltersCounts.value[props.activeGroup]) || 0,\n );\n const isDrawerOpen = ref(false);\n\n async function handleApplyClick() {\n const { preventDismiss } = (await props.onApply?.()) || props.useFiltersInstance?.applyFilters() || {};\n\n if (!preventDismiss) {\n isDrawerOpen.value = false;\n }\n }\n\n function handleResetGroupClick() {\n if (!props.activeGroup) {\n return;\n }\n\n props.useFiltersInstance?.resetFilterGroup(props.activeGroup);\n emit('reset-group');\n isDrawerOpen.value = false;\n }\n\n function handleResetAllClick() {\n props.useFiltersInstance?.resetAllFilters();\n emit('reset-all');\n isDrawerOpen.value = false;\n }\n\n function onDismiss() {\n props.useFiltersInstance?.undoWorkingFilters();\n isDrawerOpen.value = false;\n emit('dismiss');\n }\n\n watch(isDrawerOpen, () => {\n if (isDrawerOpen.value) {\n emit('open-drawer');\n }\n });\n</script>\n\n<template>\n <Box\n class=\"stash-data-view-filters tw-grid tw-grid-cols-12 tw-gap-6 tw-p-3\"\n :class=\"{ 'lg:tw-p-6': density === 'comfortable', 'tw-mb-3': !isWithinModule }\"\n :radius=\"isWithinModule ? 'none' : 'rounded'\"\n data-test=\"stash-data-view-filters\"\n disable-padding\n >\n <SearchBar\n v-if=\"props.showSearch\"\n class=\"tw-col-span-12 md:tw-col-span-6 lg:tw-col-span-4\"\n data-test=\"stash-data-view-filters|search-bar\"\n :is-loading=\"isDataViewLoading\"\n :label=\"t('ll.search')\"\n :model-value=\"currentSearch\"\n v-bind=\"props.searchBarProps\"\n @search=\"(searchTerm) => updateCurrentSearch(searchTerm, { shouldEmit: true })\"\n />\n <div\n v-if=\"slots.default\"\n class=\"tw-col-span-12 tw-row-start-2 md:tw-col-start-7 md:tw-row-start-1 lg:tw-col-span-8 lg:tw-col-start-5\"\n >\n <div class=\"tw-hidden md:tw-block\">\n <slot name=\"filters-label\">\n <Label>{{ props.filtersLabelText }}</Label>\n </slot>\n </div>\n <div class=\"tw-flex tw-gap-4\">\n <FilterChip\n secondary\n class=\"!tw-flex tw-w-full tw-justify-center tw-gap-4 md:!tw-inline-flex md:tw-w-auto\"\n data-test=\"stash-data-view-filters|drawer-toggle-button\"\n :filter-count=\"totalActiveFiltersCount\"\n @click=\"isDrawerOpen = true\"\n >\n <span class=\"tw-inline-flex tw-items-center tw-gap-3\">\n <Icon name=\"filter-line\" class=\"tw-text-ice-700\" />\n <span>{{ t('ll.filters') }}</span>\n </span>\n </FilterChip>\n <slot v-if=\"isDesktop\"></slot>\n <Button v-if=\"totalActiveFiltersCount > 0 && isDesktop\" inline @click=\"handleResetAllClick\">\n {{ t('ll.resetAll') }}\n </Button>\n </div>\n </div>\n <Modal\n v-if=\"slots.drawer\"\n data-test=\"stash-data-view-filters|drawer\"\n disable-body-padding\n position=\"right\"\n size=\"narrow\"\n :is-open=\"isDrawerOpen\"\n :title=\"t('ll.allFilters')\"\n v-bind=\"props.drawerProps\"\n @dismiss=\"onDismiss\"\n >\n <template #headerAction>\n <Button\n v-if=\"isViewingSingleGroup\"\n icon\n class=\"tw-text-ice-100\"\n data-test=\"stash-data-view-filters|drawer-previous-button\"\n :aria-label=\"t('ll.previous')\"\n :title=\"t('ll.previous')\"\n @click=\"emit('previous')\"\n >\n <Icon name=\"chevron-left\" />\n </Button>\n </template>\n\n <slot name=\"drawer\"></slot>\n\n <template #footer>\n <div class=\"tw-flex tw-justify-end tw-gap-gutter\">\n <Button v-if=\"totalActiveFiltersCount === 0\" secondary @click=\"onDismiss\">\n {{ t('ll.cancel') }}\n </Button>\n <Button\n v-if=\"(isViewingGroupsMenu || props.drawerStyle === 'cascade') && totalActiveFiltersCount > 0\"\n secondary\n :disabled=\"isDataViewLoading\"\n @click=\"handleResetAllClick\"\n >\n {{ t('ll.resetAll') }}\n </Button>\n <Button\n v-if=\"isViewingSingleGroup && activeGroupActiveFiltersCount > 0\"\n secondary\n :disabled=\"isDataViewLoading\"\n @click=\"handleResetGroupClick\"\n >\n {{ t('ll.reset') }}\n </Button>\n <Button\n v-if=\"isViewingSingleGroup || props.drawerStyle === 'cascade'\"\n :disabled=\"isDataViewLoading\"\n @click=\"handleApplyClick\"\n >\n {{ t('ll.apply') }}\n </Button>\n </div>\n </template>\n </Modal>\n </Box>\n</template>\n"],"names":["DrawerStyle","useFilters","schema","dataViewRef","appliedFilters","ref","workingFilters","filterName","dvRef","applyFilters","cloneDeep","resetAllFilters","resetFilterGroup","group","undoWorkingFilters","activeFiltersCounts","computed","counts","config","value","_a","isDefined","totalActiveFiltersCount","count","props","__props","emit","__emit","slots","_useSlots","isDesktop","useMediaQuery","SCREEN_SIZES","isViewingGroupsMenu","isViewingSingleGroup","density","isDataViewLoading","isWithinModule","currentSearch","updateCurrentSearch","inject","DATA_VIEW_INJECTION","provide","DATA_VIEW_FILTERS_UTILS_INJECTION","activeGroupActiveFiltersCount","isDrawerOpen","handleApplyClick","preventDismiss","_b","handleResetGroupClick","handleResetAllClick","onDismiss","watch"],"mappings":"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"}
1
+ {"version":3,"file":"DataViewFilters.js","sources":["../src/components/DataViewFilters/DataViewFilters.types.ts","../src/components/DataViewFilters/useFilters.ts","../src/components/DataViewFilters/DataViewFilters.vue"],"sourcesContent":["import { UseFiltersReturnType } from './useFilters';\n\n/**\n * A function that is called when the user clicks one of the \"Apply\" buttons in DataViewFilters.\n *\n * A return value of `{ preventDismiss: true }` can be used to prevent the DataViewFilters drawer or a FilterDropdown from closing, such as when some filters have invalid values.\n */\nexport type OnApplyFilters = () => Promise<{ preventDismiss?: boolean } | void> | { preventDismiss?: boolean } | void;\n\nexport enum DrawerStyle {\n /** Displays all groups and their fields without the need for navigating submenus. */\n Cascade = 'cascade',\n /** Displays only group names or fields; navigation between the group names and a submenu of fields is required. */\n Nested = 'nested',\n}\n\nexport type DrawerStyles = `${DrawerStyle}`;\n\nexport interface DataViewFiltersUtilsInjection {\n useFiltersInstance?: UseFiltersReturnType;\n drawerStyle?: DrawerStyles;\n}\n","import cloneDeep from 'lodash-es/cloneDeep';\nimport { computed, ComputedRef, Ref, ref } from 'vue';\n\nimport isDefined from '../../composables/useValidation/utils/isDefined';\nimport DataView from '../DataView/DataView.vue';\n\ntype DataViewInstance = InstanceType<typeof DataView>;\n\n/**\n * Contains metadata and configuration for the filters.\n * @see https://www.typescriptlang.org/docs/handbook/2/mapped-types.html\n */\nexport type UseFiltersSchema<Values extends object, Groups extends string> = {\n [Property in keyof Values]: {\n defaultValue?: Values[Property];\n group?: Groups;\n isActive?: (value: Values[Property]) => boolean;\n };\n};\n\nexport interface UseFiltersArgs<Values extends object, Groups extends string> {\n schema: UseFiltersSchema<Values, Groups>;\n /** A ref for an instance of DataView */\n dataViewRef: Ref<unknown>; // Note: using `Ref<InstanceType<typeof DataView>>` here causes type errors when providing a value for this argument\n}\n\nexport interface UseFiltersReturnType<Values = object, Groups extends string = string> {\n applyFilters: () => void;\n resetAllFilters: () => void;\n resetFilterGroup: (group: string) => void; // Note: group is intentionally not typed as `Groups` since there is no way to pass in a Groups type to UseFiltersReturnType within DataViewFilters.vue\n undoWorkingFilters: () => void;\n activeFiltersCounts: ComputedRef<Record<Groups, number>>;\n totalActiveFiltersCount: ComputedRef<number>;\n appliedFilters: Ref<Values>;\n workingFilters: Ref<Values>;\n}\n\n/**\n * Provides utility functions for working with `DataViewFilters`.\n */\nexport function useFilters<Values extends object, Groups extends string>({\n schema,\n dataViewRef,\n}: UseFiltersArgs<Values, Groups>): UseFiltersReturnType<Values, Groups> {\n const appliedFilters = ref({}) as Ref<Values>;\n const workingFilters = ref({}) as Ref<Values>;\n\n for (const filterName in schema) {\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n appliedFilters.value[filterName] = schema[filterName].defaultValue;\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n workingFilters.value[filterName] = schema[filterName].defaultValue;\n }\n\n const dvRef = dataViewRef as Ref<DataViewInstance>;\n\n /**\n * For when an \"Apply\" button is clicked. It does the following:\n * 1) applies the working filter state\n * 2) sets the current page to 1\n * 3) emits the \"update\" event from DataView\n */\n function applyFilters() {\n appliedFilters.value = cloneDeep(workingFilters.value);\n dvRef.value.updateCurrentFilters(appliedFilters.value, { shouldEmit: true });\n }\n\n /**\n * For when a \"Reset all\" button is clicked. It does the following:\n * 1) applies the defaultValue filter values to all filters\n * 2) sets the current page to 1\n * 3) emits the \"update\" event from DataView\n */\n function resetAllFilters() {\n for (const filterName in schema) {\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n appliedFilters.value[filterName] = schema[filterName].defaultValue;\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n workingFilters.value[filterName] = schema[filterName].defaultValue;\n }\n\n dvRef.value.updateCurrentFilters(appliedFilters.value, { shouldEmit: true });\n }\n\n /**\n * For when a \"Reset\" button is clicked. It does the following:\n * 1) applies the defaultValue filter values to the given filter group\n * 2) sets the current page to 1\n * 3) emits the \"update\" event from DataView\n */\n function resetFilterGroup(group: Groups) {\n for (const filterName in schema) {\n if (schema[filterName].group === group) {\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n appliedFilters.value[filterName] = schema[filterName].defaultValue;\n // @ts-expect-error \"could be instantiated with an arbitrary type\"; TODO: figure out how to resolve the types\n workingFilters.value[filterName] = schema[filterName].defaultValue;\n }\n }\n\n dvRef.value.updateCurrentFilters(appliedFilters.value, { shouldEmit: true });\n }\n\n /**\n * Resets the `workingFilters` to match the `appliedFilters`. This can be used when the FilterDrawer or a FilterDropdown is dismissed without clicking \"Reset\" or \"Apply\".\n */\n function undoWorkingFilters() {\n workingFilters.value = cloneDeep(appliedFilters.value);\n }\n\n const activeFiltersCounts = computed(() => {\n const counts = {} as Record<Groups, number>;\n\n for (const filterName in schema) {\n const config = schema[filterName];\n const value = appliedFilters.value[filterName];\n const isActive = config.isActive?.(value) || isDefined(value);\n\n if (isActive && config.group) {\n counts[config.group] = (counts[config.group] ?? 0) + 1;\n }\n }\n\n return counts;\n });\n\n const totalActiveFiltersCount = computed(() => {\n let count = 0;\n\n for (const filterName in schema) {\n const config = schema[filterName];\n const value = appliedFilters.value[filterName];\n const isActive = config.isActive?.(value) || isDefined(value);\n\n if (isActive) {\n count += 1;\n }\n }\n\n return count;\n });\n\n return {\n applyFilters,\n resetAllFilters,\n // @ts-expect-error \"could be instantiated with a different subtype\": TODO: figure out how to resolve the types\n resetFilterGroup,\n undoWorkingFilters,\n activeFiltersCounts,\n totalActiveFiltersCount,\n appliedFilters,\n workingFilters,\n };\n}\n\nexport default useFilters;\n","<script lang=\"ts\">\n export * from './DataViewFilters.keys';\n export * from './DataViewFilters.types';\n export * from './useFilters';\n</script>\n\n<script setup lang=\"ts\">\n import { computed, inject, provide, ref, watch } from 'vue';\n\n import useMediaQuery from '../../composables/useMediaQuery/useMediaQuery';\n import { SCREEN_SIZES } from '../../constants';\n import { t } from '../../locale';\n import Box from '../Box/Box.vue';\n import Button from '../Button/Button.vue';\n import { DATA_VIEW_INJECTION } from '../DataView/DataView.vue';\n import FilterChip from '../FilterChip/FilterChip.vue';\n import Icon from '../Icon/Icon.vue';\n import Label from '../Label/Label.vue';\n import Modal, { type ModalProps } from '../Modal/Modal.vue';\n import SearchBar, { SearchBarProps } from '../SearchBar/SearchBar.vue';\n import { DATA_VIEW_FILTERS_UTILS_INJECTION } from './DataViewFilters.keys';\n import type { DrawerStyles, OnApplyFilters } from './DataViewFilters.types';\n import type { UseFiltersReturnType } from './useFilters';\n\n export interface DataViewFiltersProps {\n filtersLabelText?: string;\n /**\n * Props to pass to the `SearchBar` component.\n */\n searchBarProps?: SearchBarProps;\n showSearch?: boolean;\n /** 'cascade' displays all fields within every filter group; 'nested' displays only the group names and requires clicking a group to view its fields. */\n drawerStyle?: DrawerStyles;\n drawerProps?: ModalProps;\n /**\n * @deprecated The `activeGroup` prop is a sufficient replacement for this prop. A falsy `activeGroup` will hide the button and a truthy `activeGroup` will show it (when the `drawerStyle` is 'nested').\n *\n * **Note:** This prop has no effect when using a \"cascade\" `drawerStyle`.\n */\n showDrawerPreviousButton?: boolean;\n /**\n * Required when using filters. This prop should contain the return value of the `useFilters()` composable.\n */\n useFiltersInstance?: UseFiltersReturnType;\n onApply?: OnApplyFilters;\n /**\n * The name of the active filter group. The active filter group is determined by which instance of FilterDropdown or FilterDrawerItem is open.\n *\n * **Note:** This prop is required when using a \"nested\" `drawerStyle`, but has no effect when using a \"cascade\" `drawerStyle`.\n */\n activeGroup?: string;\n }\n\n export interface DataViewFiltersSlots {\n default?: () => unknown;\n drawer?: () => unknown;\n 'filters-label'?: () => unknown;\n }\n\n const props = withDefaults(defineProps<DataViewFiltersProps>(), {\n filtersLabelText: t('ll.filterBy'),\n isLoading: false,\n drawerStyle: 'cascade',\n drawerProps: undefined,\n searchBarProps: undefined,\n showDrawerPreviousButton: false,\n showSearch: true,\n useFiltersInstance: undefined,\n onApply: undefined,\n activeGroup: undefined,\n });\n\n const emit = defineEmits<{\n /** When the drawer is opened */\n (e: 'open-drawer'): void;\n /** When the drawer is dismissed */\n (e: 'dismiss'): void;\n /** When the \"Previous\" button in the header is clicked */\n (e: 'previous'): void;\n /** When the \"Reset\" button is clicked while viewing a filter group */\n (e: 'reset-group'): void;\n /** When one of the \"Reset All\" buttons is clicked */\n (e: 'reset-all'): void;\n }>();\n\n const slots = defineSlots<DataViewFiltersSlots>();\n\n const isDesktop = useMediaQuery(`(min-width: ${SCREEN_SIZES.lg})`);\n const isViewingGroupsMenu = computed(() => props.drawerStyle === 'nested' && !props.activeGroup);\n const isViewingSingleGroup = computed(() => props.drawerStyle === 'nested' && props.activeGroup);\n\n const {\n density,\n isLoading: isDataViewLoading,\n isWithinModule,\n currentSearch,\n updateCurrentSearch,\n } = inject(DATA_VIEW_INJECTION.key, DATA_VIEW_INJECTION.defaults);\n\n provide(DATA_VIEW_FILTERS_UTILS_INJECTION.key, {\n useFiltersInstance: props.useFiltersInstance,\n drawerStyle: props.drawerStyle,\n });\n\n const totalActiveFiltersCount = computed(() => props.useFiltersInstance?.totalActiveFiltersCount.value ?? 0);\n /** The number of active filters in the currently active group. This is only used when the drawerStyle is 'nested'. */\n const activeGroupActiveFiltersCount = computed(\n () => Number(props.activeGroup && props.useFiltersInstance?.activeFiltersCounts.value[props.activeGroup]) || 0,\n );\n const isDrawerOpen = ref(false);\n\n async function handleApplyClick() {\n const { preventDismiss } = (await props.onApply?.()) || props.useFiltersInstance?.applyFilters() || {};\n\n if (!preventDismiss) {\n isDrawerOpen.value = false;\n }\n }\n\n function handleResetGroupClick() {\n if (!props.activeGroup) {\n return;\n }\n\n props.useFiltersInstance?.resetFilterGroup(props.activeGroup);\n emit('reset-group');\n isDrawerOpen.value = false;\n }\n\n function handleResetAllClick() {\n props.useFiltersInstance?.resetAllFilters();\n emit('reset-all');\n isDrawerOpen.value = false;\n }\n\n function onDismiss() {\n props.useFiltersInstance?.undoWorkingFilters();\n isDrawerOpen.value = false;\n emit('dismiss');\n }\n\n watch(isDrawerOpen, () => {\n if (isDrawerOpen.value) {\n emit('open-drawer');\n }\n });\n</script>\n\n<template>\n <Box\n class=\"stash-data-view-filters tw-grid tw-grid-cols-12 tw-gap-6 tw-p-3\"\n :class=\"{ 'lg:tw-p-6': density === 'comfortable', 'tw-mb-3': !isWithinModule }\"\n :radius=\"isWithinModule ? 'none' : 'rounded'\"\n data-test=\"stash-data-view-filters\"\n disable-padding\n >\n <SearchBar\n v-if=\"props.showSearch\"\n class=\"tw-col-span-12 md:tw-col-span-6 lg:tw-col-span-4\"\n data-test=\"stash-data-view-filters|search-bar\"\n :is-loading=\"isDataViewLoading\"\n :label=\"t('ll.search')\"\n :model-value=\"currentSearch\"\n v-bind=\"props.searchBarProps\"\n @search=\"(searchTerm) => updateCurrentSearch(searchTerm, { shouldEmit: true })\"\n />\n <div\n v-if=\"slots.default\"\n class=\"tw-col-span-12 tw-row-start-2 md:tw-col-start-7 md:tw-row-start-1 lg:tw-col-span-8 lg:tw-col-start-5\"\n >\n <div class=\"tw-hidden md:tw-block\">\n <slot name=\"filters-label\">\n <Label>{{ props.filtersLabelText }}</Label>\n </slot>\n </div>\n <div class=\"tw-flex tw-gap-4\">\n <FilterChip\n secondary\n class=\"!tw-flex tw-w-full tw-justify-center tw-gap-4 md:!tw-inline-flex md:tw-w-auto\"\n data-test=\"stash-data-view-filters|drawer-toggle-button\"\n :filter-count=\"totalActiveFiltersCount\"\n @click=\"isDrawerOpen = true\"\n >\n <span class=\"tw-inline-flex tw-items-center tw-gap-3\">\n <Icon name=\"filter-line\" class=\"tw-text-ice-700\" />\n <span>{{ t('ll.filters') }}</span>\n </span>\n </FilterChip>\n <slot v-if=\"isDesktop\"></slot>\n <Button v-if=\"totalActiveFiltersCount > 0 && isDesktop\" inline @click=\"handleResetAllClick\">\n {{ t('ll.resetAll') }}\n </Button>\n </div>\n </div>\n <Modal\n v-if=\"slots.drawer\"\n data-test=\"stash-data-view-filters|drawer\"\n disable-body-padding\n position=\"right\"\n size=\"narrow\"\n :is-open=\"isDrawerOpen\"\n :title=\"t('ll.allFilters')\"\n v-bind=\"props.drawerProps\"\n @dismiss=\"onDismiss\"\n >\n <template #headerAction>\n <Button\n v-if=\"isViewingSingleGroup\"\n icon\n class=\"tw-text-ice-100\"\n data-test=\"stash-data-view-filters|drawer-previous-button\"\n :aria-label=\"t('ll.previous')\"\n :title=\"t('ll.previous')\"\n @click=\"emit('previous')\"\n >\n <Icon name=\"chevron-left\" />\n </Button>\n </template>\n\n <slot name=\"drawer\"></slot>\n\n <template #footer>\n <div class=\"tw-flex tw-justify-end tw-gap-gutter\">\n <Button v-if=\"totalActiveFiltersCount === 0\" secondary @click=\"onDismiss\">\n {{ t('ll.cancel') }}\n </Button>\n <Button\n v-if=\"(isViewingGroupsMenu || props.drawerStyle === 'cascade') && totalActiveFiltersCount > 0\"\n secondary\n :disabled=\"isDataViewLoading\"\n @click=\"handleResetAllClick\"\n >\n {{ t('ll.resetAll') }}\n </Button>\n <Button\n v-if=\"isViewingSingleGroup && activeGroupActiveFiltersCount > 0\"\n secondary\n :disabled=\"isDataViewLoading\"\n @click=\"handleResetGroupClick\"\n >\n {{ t('ll.reset') }}\n </Button>\n <Button\n v-if=\"isViewingSingleGroup || props.drawerStyle === 'cascade'\"\n :disabled=\"isDataViewLoading\"\n @click=\"handleApplyClick\"\n >\n {{ t('ll.apply') }}\n </Button>\n </div>\n </template>\n </Modal>\n </Box>\n</template>\n"],"names":["DrawerStyle","useFilters","schema","dataViewRef","appliedFilters","ref","workingFilters","filterName","dvRef","applyFilters","cloneDeep","resetAllFilters","resetFilterGroup","group","undoWorkingFilters","activeFiltersCounts","computed","counts","config","value","_a","isDefined","totalActiveFiltersCount","count","props","__props","emit","__emit","slots","_useSlots","isDesktop","useMediaQuery","SCREEN_SIZES","isViewingGroupsMenu","isViewingSingleGroup","density","isDataViewLoading","isWithinModule","currentSearch","updateCurrentSearch","inject","DATA_VIEW_INJECTION","provide","DATA_VIEW_FILTERS_UTILS_INJECTION","activeGroupActiveFiltersCount","isDrawerOpen","handleApplyClick","preventDismiss","_b","handleResetGroupClick","handleResetAllClick","onDismiss","watch"],"mappings":"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"}
@@ -67,9 +67,9 @@ export declare interface DataViewFiltersProps {
67
67
  }
68
68
 
69
69
  export declare interface DataViewFiltersSlots {
70
- default?: void;
71
- drawer?: void;
72
- 'filters-label'?: void;
70
+ default?: () => unknown;
71
+ drawer?: () => unknown;
72
+ 'filters-label'?: () => unknown;
73
73
  }
74
74
 
75
75
  export declare interface DataViewFiltersUtilsInjection {
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.js","sources":["../src/components/EmptyState/EmptyState.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { useCssModule } from 'vue';\n\n import { t } from '../../locale';\n import { VignetteNames } from '../Illustration/Illustration.models';\n import Illustration from '../Illustration/Illustration.vue';\n\n export interface EmptyStateProps {\n /**\n * @deprecated Use illustration instead.\n */\n image?: {\n alt?: string;\n src: string;\n };\n\n /**\n * @deprecated Use title instead.\n */\n text?: string;\n\n /**\n * the title text of the empty state\n */\n title?: string;\n\n /**\n * the subtitle text of the empty state\n */\n subtitle?: string;\n\n /**\n * The name of the vignette illustration to render\n */\n vignette?: VignetteNames;\n }\n\n export interface EmptyStateSlots {\n title?: void;\n subtitle?: void;\n button?: void;\n image?: void;\n }\n\n defineOptions({\n name: 'll-empty-state',\n });\n\n const props = withDefaults(defineProps<EmptyStateProps>(), {\n /**\n * An optional image to render\n */\n image: () => ({\n alt: '',\n src: '',\n }),\n\n /**\n * The custom message to render\n */\n text: '',\n\n title: '',\n subtitle: '',\n vignette: undefined,\n });\n\n const classes = useCssModule();\n const slots = defineSlots<EmptyStateSlots>();\n</script>\n\n<template>\n <div key=\"empty\" class=\"stash-empty-state\" data-test=\"stash-empty-state\">\n <template v-if=\"props.title || slots.title || props.subtitle || slots.subtitle || props.vignette\">\n <div class=\"tw-flex tw-flex-col tw-items-center tw-text-center md:tw-flex-row md:tw-text-left\">\n <Illustration\n v-if=\"props.vignette\"\n data-test=\"stash-empty-state|illustration\"\n type=\"vignette\"\n class=\"tw-mx-3\"\n :name=\"props.vignette\"\n :size=\"300\"\n />\n <section>\n <slot name=\"title\">\n <h3 class=\"tw-mb-1.5\">\n {{ props.title }}\n </h3>\n </slot>\n\n <slot name=\"subtitle\">\n <p v-if=\"props.subtitle || slots.subtitle\" :class=\"{ ' tw-mb-8': slots.button, 'tw-mb-0': !slots.button }\">\n {{ props.subtitle }}\n </p>\n </slot>\n\n <slot name=\"button\"></slot>\n </section>\n </div>\n </template>\n\n <template v-else>\n <div\n class=\"tw-mx-auto tw-my-0 tw-flex tw-items-center tw-justify-center tw-py-12\"\n :class=\"[classes.root, { 'tw-text-center': !props.image.src && !slots.image }]\"\n >\n <slot name=\"image\">\n <img\n v-if=\"props.image.src\"\n class=\"tw-mr-6\"\n :alt=\"props.image.alt\"\n :class=\"classes.image\"\n :src=\"props.image.src\"\n data-test=\"stash-empty-state__img\"\n />\n </slot>\n\n {{ props.text || t('ll.emptyState.noResults') }}\n </div>\n </template>\n </div>\n</template>\n\n<style module>\n .root {\n max-width: 600px;\n }\n\n .image {\n max-width: 60px;\n }\n</style>\n"],"names":["props","__props","classes","useCssModule","slots","_useSlots"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgDE,UAAMA,IAAQC,GAmBRC,IAAUC,EAAa,GACvBC,IAAQC,EAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"EmptyState.js","sources":["../src/components/EmptyState/EmptyState.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { useCssModule } from 'vue';\n\n import { t } from '../../locale';\n import { VignetteNames } from '../Illustration/Illustration.models';\n import Illustration from '../Illustration/Illustration.vue';\n\n export interface EmptyStateProps {\n /**\n * @deprecated Use illustration instead.\n */\n image?: {\n alt?: string;\n src: string;\n };\n\n /**\n * @deprecated Use title instead.\n */\n text?: string;\n\n /**\n * the title text of the empty state\n */\n title?: string;\n\n /**\n * the subtitle text of the empty state\n */\n subtitle?: string;\n\n /**\n * The name of the vignette illustration to render\n */\n vignette?: VignetteNames;\n }\n\n export interface EmptyStateSlots {\n title?: () => unknown;\n subtitle?: () => unknown;\n button?: () => unknown;\n image?: () => unknown;\n }\n\n defineOptions({\n name: 'll-empty-state',\n });\n\n const props = withDefaults(defineProps<EmptyStateProps>(), {\n /**\n * An optional image to render\n */\n image: () => ({\n alt: '',\n src: '',\n }),\n\n /**\n * The custom message to render\n */\n text: '',\n\n title: '',\n subtitle: '',\n vignette: undefined,\n });\n\n const classes = useCssModule();\n const slots = defineSlots<EmptyStateSlots>();\n</script>\n\n<template>\n <div key=\"empty\" class=\"stash-empty-state\" data-test=\"stash-empty-state\">\n <template v-if=\"props.title || slots.title || props.subtitle || slots.subtitle || props.vignette\">\n <div class=\"tw-flex tw-flex-col tw-items-center tw-text-center md:tw-flex-row md:tw-text-left\">\n <Illustration\n v-if=\"props.vignette\"\n data-test=\"stash-empty-state|illustration\"\n type=\"vignette\"\n class=\"tw-mx-3\"\n :name=\"props.vignette\"\n :size=\"300\"\n />\n <section>\n <slot name=\"title\">\n <h3 class=\"tw-mb-1.5\">\n {{ props.title }}\n </h3>\n </slot>\n\n <slot name=\"subtitle\">\n <p v-if=\"props.subtitle || slots.subtitle\" :class=\"{ ' tw-mb-8': slots.button, 'tw-mb-0': !slots.button }\">\n {{ props.subtitle }}\n </p>\n </slot>\n\n <slot name=\"button\"></slot>\n </section>\n </div>\n </template>\n\n <template v-else>\n <div\n class=\"tw-mx-auto tw-my-0 tw-flex tw-items-center tw-justify-center tw-py-12\"\n :class=\"[classes.root, { 'tw-text-center': !props.image.src && !slots.image }]\"\n >\n <slot name=\"image\">\n <img\n v-if=\"props.image.src\"\n class=\"tw-mr-6\"\n :alt=\"props.image.alt\"\n :class=\"classes.image\"\n :src=\"props.image.src\"\n data-test=\"stash-empty-state__img\"\n />\n </slot>\n\n {{ props.text || t('ll.emptyState.noResults') }}\n </div>\n </template>\n </div>\n</template>\n\n<style module>\n .root {\n max-width: 600px;\n }\n\n .image {\n max-width: 60px;\n }\n</style>\n"],"names":["props","__props","classes","useCssModule","slots","_useSlots"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgDE,UAAMA,IAAQC,GAmBRC,IAAUC,EAAa,GACvBC,IAAQC,EAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -101,10 +101,10 @@ export declare interface EmptyStateProps {
101
101
  }
102
102
 
103
103
  export declare interface EmptyStateSlots {
104
- title?: void;
105
- subtitle?: void;
106
- button?: void;
107
- image?: void;
104
+ title?: () => unknown;
105
+ subtitle?: () => unknown;
106
+ button?: () => unknown;
107
+ image?: () => unknown;
108
108
  }
109
109
 
110
110
  declare enum VignetteName {
@@ -1 +1 @@
1
- {"version":3,"file":"FilterDrawerItem.js","sources":["../src/components/FilterDrawerItem/FilterDrawerItem.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, inject } from 'vue';\n\n import { t } from '../../locale';\n import Chip from '../Chip/Chip.vue';\n import { DATA_VIEW_FILTERS_UTILS_INJECTION } from '../DataViewFilters/DataViewFilters.keys';\n import Icon from '../Icon/Icon.vue';\n\n export interface FilterDrawerItemProps {\n /** The name of a filter group */\n group: string;\n title: string;\n description?: string;\n }\n const props = defineProps<FilterDrawerItemProps>();\n\n export interface FilterDrawerItemEmits {\n (e: 'navigate');\n }\n const emit = defineEmits<FilterDrawerItemEmits>();\n\n export interface FilterDrawerItemSlots {\n default?: void;\n }\n\n const slots = defineSlots<FilterDrawerItemSlots>();\n\n const dataViewFiltersUtils = inject(DATA_VIEW_FILTERS_UTILS_INJECTION.key);\n\n if (!dataViewFiltersUtils?.useFiltersInstance) {\n throw new Error(\n 'FilterDropdown must be used within a <DataViewFilters> that receives an instance of useFilters().',\n );\n }\n\n const { activeFiltersCounts } = dataViewFiltersUtils.useFiltersInstance;\n\n const activeCount = computed(() => activeFiltersCounts.value[props.group]);\n const isDrawerCascade = computed(() => dataViewFiltersUtils.drawerStyle === 'cascade');\n</script>\n\n<template>\n <div class=\"stash-filter-drawer-item\">\n <component\n :is=\"isDrawerCascade ? 'div' : 'button'\"\n data-test=\"stash-filter-drawer-item|dynamic-component\"\n class=\"tw-w-full tw-border-b tw-border-ice-500 tw-py-4 tw-outline-none focus:tw-outline-blue-500\"\n @click=\"isDrawerCascade ? undefined : emit('navigate')\"\n >\n <div class=\"tw-flex tw-items-center tw-justify-between tw-self-stretch\">\n <div class=\"tw-flex tw-flex-col tw-items-start\">\n <h4>{{ props.title }}</h4>\n <div v-if=\"props.description\" class=\"tw-text-xs\" data-test=\"description\">\n {{ props.description }}\n </div>\n </div>\n <div class=\"tw-inline-flex tw-items-center tw-gap-6\" :class=\"{ 'tw-mb-0.5 tw-mr-2': isDrawerCascade }\">\n <Chip\n v-if=\"activeCount\"\n color=\"blue\"\n radius=\"pill\"\n shade=\"main\"\n :aria-label=\"t('ll.numberOfActiveFilters')\"\n :title=\"t('ll.numberOfActiveFilters')\"\n >\n {{ activeCount }}\n </Chip>\n <Icon v-if=\"!isDrawerCascade\" name=\"chevron-right\" :title=\"t('ll.viewFilterGroup')\" />\n </div>\n </div>\n </component>\n <div v-if=\"isDrawerCascade && slots.default\" class=\"tw-gap-3 tw-p-6\">\n <slot></slot>\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","slots","_useSlots","dataViewFiltersUtils","inject","DATA_VIEW_FILTERS_UTILS_INJECTION","activeFiltersCounts","activeCount","computed","isDrawerCascade"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAcE,UAAMA,IAAQC,GAKRC,IAAOC,GAMPC,IAAQC,EAAoC,GAE5CC,IAAuBC,EAAOC,EAAkC,GAAG;AAErE,QAAA,EAACF,KAAA,QAAAA,EAAsB;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAGI,UAAA,EAAE,qBAAAG,MAAwBH,EAAqB,oBAE/CI,IAAcC,EAAS,MAAMF,EAAoB,MAAMT,EAAM,KAAK,CAAC,GACnEY,IAAkBD,EAAS,MAAML,EAAqB,gBAAgB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"FilterDrawerItem.js","sources":["../src/components/FilterDrawerItem/FilterDrawerItem.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, inject } from 'vue';\n\n import { t } from '../../locale';\n import Chip from '../Chip/Chip.vue';\n import { DATA_VIEW_FILTERS_UTILS_INJECTION } from '../DataViewFilters/DataViewFilters.keys';\n import Icon from '../Icon/Icon.vue';\n\n export interface FilterDrawerItemProps {\n /** The name of a filter group */\n group: string;\n title: string;\n description?: string;\n }\n const props = defineProps<FilterDrawerItemProps>();\n\n export interface FilterDrawerItemEmits {\n (e: 'navigate');\n }\n const emit = defineEmits<FilterDrawerItemEmits>();\n\n export interface FilterDrawerItemSlots {\n default?: () => unknown;\n }\n\n const slots = defineSlots<FilterDrawerItemSlots>();\n\n const dataViewFiltersUtils = inject(DATA_VIEW_FILTERS_UTILS_INJECTION.key);\n\n if (!dataViewFiltersUtils?.useFiltersInstance) {\n throw new Error(\n 'FilterDropdown must be used within a <DataViewFilters> that receives an instance of useFilters().',\n );\n }\n\n const { activeFiltersCounts } = dataViewFiltersUtils.useFiltersInstance;\n\n const activeCount = computed(() => activeFiltersCounts.value[props.group]);\n const isDrawerCascade = computed(() => dataViewFiltersUtils.drawerStyle === 'cascade');\n</script>\n\n<template>\n <div class=\"stash-filter-drawer-item\">\n <component\n :is=\"isDrawerCascade ? 'div' : 'button'\"\n data-test=\"stash-filter-drawer-item|dynamic-component\"\n class=\"tw-w-full tw-border-b tw-border-ice-500 tw-py-4 tw-outline-none focus:tw-outline-blue-500\"\n @click=\"isDrawerCascade ? undefined : emit('navigate')\"\n >\n <div class=\"tw-flex tw-items-center tw-justify-between tw-self-stretch\">\n <div class=\"tw-flex tw-flex-col tw-items-start\">\n <h4>{{ props.title }}</h4>\n <div v-if=\"props.description\" class=\"tw-text-xs\" data-test=\"description\">\n {{ props.description }}\n </div>\n </div>\n <div class=\"tw-inline-flex tw-items-center tw-gap-6\" :class=\"{ 'tw-mb-0.5 tw-mr-2': isDrawerCascade }\">\n <Chip\n v-if=\"activeCount\"\n color=\"blue\"\n radius=\"pill\"\n shade=\"main\"\n :aria-label=\"t('ll.numberOfActiveFilters')\"\n :title=\"t('ll.numberOfActiveFilters')\"\n >\n {{ activeCount }}\n </Chip>\n <Icon v-if=\"!isDrawerCascade\" name=\"chevron-right\" :title=\"t('ll.viewFilterGroup')\" />\n </div>\n </div>\n </component>\n <div v-if=\"isDrawerCascade && slots.default\" class=\"tw-gap-3 tw-p-6\">\n <slot></slot>\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","slots","_useSlots","dataViewFiltersUtils","inject","DATA_VIEW_FILTERS_UTILS_INJECTION","activeFiltersCounts","activeCount","computed","isDrawerCascade"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAcE,UAAMA,IAAQC,GAKRC,IAAOC,GAMPC,IAAQC,EAAoC,GAE5CC,IAAuBC,EAAOC,EAAkC,GAAG;AAErE,QAAA,EAACF,KAAA,QAAAA,EAAsB;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAGI,UAAA,EAAE,qBAAAG,MAAwBH,EAAqB,oBAE/CI,IAAcC,EAAS,MAAMF,EAAoB,MAAMT,EAAM,KAAK,CAAC,GACnEY,IAAkBD,EAAS,MAAML,EAAqB,gBAAgB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -41,7 +41,7 @@ export declare interface FilterDrawerItemProps {
41
41
  }
42
42
 
43
43
  export declare interface FilterDrawerItemSlots {
44
- default?: void;
44
+ default?: () => unknown;
45
45
  }
46
46
 
47
47
  export { }
@@ -1065,6 +1065,9 @@ validator(value: unknown): true;
1065
1065
  };
1066
1066
  hasError: BooleanConstructor;
1067
1067
  id: {
1068
+ /**
1069
+ * Whether to disable the Clear button
1070
+ */
1068
1071
  type: StringConstructor;
1069
1072
  required: true;
1070
1073
  };
@@ -1073,10 +1076,14 @@ type: StringConstructor;
1073
1076
  default: string;
1074
1077
  };
1075
1078
  }>, {
1076
- classes: Record<string, string>;
1079
+ classes: Record<string, string>; /**
1080
+ * Filters schema
1081
+ */
1077
1082
  }, {}, {
1078
1083
  internalValue: {
1079
- get(): string | number | undefined;
1084
+ get(): string | number | undefined; /**
1085
+ * Validation schema function that returns an object
1086
+ */
1080
1087
  set(value: any): void;
1081
1088
  };
1082
1089
  }, {}, ComponentOptionsMixin, ComponentOptionsMixin, "update:checked"[], "update:checked", PublicProps, Readonly<ExtractPropTypes< {
@@ -1091,6 +1098,9 @@ validator(value: unknown): true;
1091
1098
  };
1092
1099
  hasError: BooleanConstructor;
1093
1100
  id: {
1101
+ /**
1102
+ * Whether to disable the Clear button
1103
+ */
1094
1104
  type: StringConstructor;
1095
1105
  required: true;
1096
1106
  };
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 { 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,EAAS,GACjBC,IAAUC,EAAa,GACvBC,IAAQC,EAAS,GAIjBC,IAAUC,EAAsB;AAIzB,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,EAAM;AAE7B,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,IAAA;AAQ7D,aAASJ,EAAOE,GAAwB;AACtC,aAAIP,EAAS,QACJW,EAAgBJ,GAAOX,CAAgB,IAGzCW;AAAA,IAAA;AAMT,aAASK,IAAe;AAEtB,YAAMC,IAAcb,EAAS,QAAQM,EAAYZ,EAAc,KAAK,IAAIA,EAAc;AACtF,MAAAX,EAAK,UAAU8B,CAAW;AAAA,IAAA;AAM5B,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,IAAA;AAGxC,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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CE,UAAMA,IAAQC,GAQRC,IAAOC,GAmBPC,IAAQC,EAAS,GACjBC,IAAUC,EAAa,GACvBC,IAAQC,EAAS,GAIjBC,IAAUC,EAAsB;AAIzB,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,EAAM;AAE7B,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,IAAA;AAQ7D,aAASJ,EAAOE,GAAwB;AACtC,aAAIP,EAAS,QACJW,EAAgBJ,GAAOX,CAAgB,IAGzCW;AAAA,IAAA;AAMT,aAASK,IAAe;AAEtB,YAAMC,IAAcb,EAAS,QAAQM,EAAYZ,EAAc,KAAK,IAAIA,EAAc;AACtF,MAAAX,EAAK,UAAU8B,CAAW;AAAA,IAAA;AAM5B,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,IAAA;AAGxC,WAAAG,EAAU,MAAM;AACV,UAAAnC,EAAM,UAAU;AACZ,cAAA,IAAI,MAAM,0DAA0D;AAG5E,UAAIQ,EAAM;AACF,cAAA,IAAI,MAAM,+DAA+D;AAAA,IACjF,CACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -150,6 +150,4 @@ export declare interface InputProps extends FieldProps {
150
150
  placeholder?: string;
151
151
  }
152
152
 
153
- export declare type InputValue = string | number | undefined;
154
-
155
153
  export { }
@@ -4685,10 +4685,10 @@ declare type DropdownOffset = {
4685
4685
  };
4686
4686
 
4687
4687
  declare interface EmptyStateSlots {
4688
- title?: void;
4689
- subtitle?: void;
4690
- button?: void;
4691
- image?: void;
4688
+ title?: () => unknown;
4689
+ subtitle?: () => unknown;
4690
+ button?: () => unknown;
4691
+ image?: () => unknown;
4692
4692
  }
4693
4693
 
4694
4694
  declare type ExpandOuterElement = 'div' | 'td' | 'li';
package/dist/Radio.js CHANGED
@@ -16,7 +16,7 @@ const p = "_root_ul6uq_2", b = "_label_ul6uq_7", k = "_error_ul6uq_40", y = "_in
16
16
  default: void 0
17
17
  },
18
18
  /**
19
- * Use :checked or v-model:checked instead of :model-value and v-model.
19
+ * @deprecated Use :checked or v-model:checked instead of :model-value and v-model.
20
20
  */
21
21
  modelValue: {
22
22
  type: [String, Number, null],
package/dist/Radio.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Radio.js","sources":["../src/components/Radio/Radio.vue"],"sourcesContent":["<script>\n import { useCssModule } from 'vue';\n\n /**\n * @deprecated use components/RadioNew/Radio instead.\n * if you need multiple Radio components considering using RadioGroup instead.\n */\n export default {\n name: 'll-radio',\n\n props: {\n /**\n * The checked/selected state of the radio button\n */\n checked: {\n type: [String, Number],\n default: undefined,\n },\n\n /**\n * Use :checked or v-model:checked instead of :model-value and v-model.\n */\n modelValue: {\n type: [String, Number, null],\n default: null,\n validator(value) {\n if (value !== null) {\n throw new Error('ll-radio: use :checked or v-model:checked instead of :model-value and v-model.');\n }\n\n return true;\n },\n },\n\n /**\n * Adds error styling\n */\n hasError: Boolean,\n\n /**\n * A unique string to distinguish one Radio instance from another\n * and to link the label to the input; required for accessibility\n */\n id: {\n type: String,\n required: true,\n },\n\n /**\n * The description which appears to the right of the radio button\n */\n label: {\n type: String,\n default: '',\n },\n },\n\n emits: ['update:checked'],\n\n setup() {\n return {\n classes: useCssModule(),\n };\n },\n\n computed: {\n internalValue: {\n get() {\n return this.checked;\n },\n set(value) {\n this.$emit('update:checked', value);\n },\n },\n },\n\n created() {\n if (this.$attrs.onChange) {\n throw new Error('ll-radio: use the @update:checked event instead of @input');\n }\n },\n };\n</script>\n\n<template>\n <div class=\"stash-radio\" data-test=\"ll-radio\" :class=\"[classes.root, { error: hasError }]\">\n <input :id=\"id\" v-model=\"internalValue\" class=\"tw-sr-only\" :class=\"classes.input\" type=\"radio\" v-bind=\"$attrs\" />\n\n <label :for=\"id\" :class=\"classes.label\">\n {{ label }}\n </label>\n </div>\n</template>\n\n<style module>\n .root {\n position: relative;\n margin: theme('spacing[1.5]') 0;\n }\n\n .label {\n color: var(--color-ice-700);\n cursor: pointer;\n display: inline-block;\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n line-height: theme('spacing.6');\n min-height: theme('spacing.9');\n overflow: visible;\n padding: theme('spacing[1.5]') 0 theme('spacing[1.5]') calc(20px + theme('spacing.3'));\n position: relative;\n vertical-align: top;\n }\n\n .label::before {\n background: var(--color-white);\n border: 1px solid var(--color-ice-500);\n content: '';\n display: inline-block;\n vertical-align: top;\n }\n\n .label::before,\n .label::after {\n height: 20px;\n left: 0;\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n width: 20px;\n border-radius: 50%;\n }\n\n .error .label {\n color: var(--color-red-500);\n }\n\n .error .label::before,\n .error .input {\n border-color: var(--color-red-500);\n }\n\n .input:checked + .label::after {\n background: var(--color-blue-500);\n content: '';\n height: 14px;\n left: 3px;\n width: 14px;\n }\n\n .input:checked:disabled + .label::after {\n background: var(--color-ice-500);\n }\n\n .input:not(:disabled, :checked) + .label:hover::before {\n border-color: var(--color-blue-500);\n }\n\n @media screen('lg') {\n .root input {\n margin: 0;\n }\n }\n</style>\n"],"names":["_sfc_main","value","useCssModule","_hoisted_1","_hoisted_2","_createElementBlock","_normalizeClass","$setup","$props","_withDirectives","_createElementVNode","_mergeProps","_cache","$event","$options","_ctx","_toDisplayString"],"mappings":";;;;;;;GAOOA,IAAU;AAAA,EACb,MAAM;AAAA,EAEN,OAAO;AAAA;AAAA;AAAA;AAAA,IAIL,SAAS;AAAA,MACP,MAAM,CAAC,QAAQ,MAAM;AAAA,MACrB,SAAS;AAAA,IACV;AAAA;AAAA;AAAA;AAAA,IAKD,YAAY;AAAA,MACV,MAAM,CAAC,QAAQ,QAAQ,IAAI;AAAA,MAC3B,SAAS;AAAA,MACT,UAAUC,GAAO;AACf,YAAIA,MAAU;AACZ,gBAAM,IAAI,MAAM,gFAAgF;AAGlG,eAAO;AAAA,MACR;AAAA,IACF;AAAA;AAAA;AAAA;AAAA,IAKD,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACX;AAAA;AAAA;AAAA;AAAA,IAKD,OAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,IACV;AAAA,EACF;AAAA,EAED,OAAO,CAAC,gBAAgB;AAAA,EAExB,QAAQ;AACN,WAAO;AAAA,MACL,SAASC,EAAc;AAAA,IACxB;AAAA,EACF;AAAA,EAED,UAAU;AAAA,IACR,eAAe;AAAA,MACb,MAAM;AACJ,eAAO,KAAK;AAAA,MACb;AAAA,MACD,IAAID,GAAO;AACT,aAAK,MAAM,kBAAkBA,CAAK;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA,EAED,UAAU;AACR,QAAI,KAAK,OAAO;AACd,YAAM,IAAI,MAAM,2DAA2D;AAAA,EAE9E;AACF,GAjFHE,IAAA,CAAA,IAAA,GAAAC,IAAA,CAAA,KAAA;;cAqFEC,EAMM,OAAA;AAAA,IAND,OArFPC,GAqFa,eAAa,CAA+BC,UAAQ,eAAeC,EAAQ,SAAA,CAAA,CAAA,CAAA;AAAA,IAA7D,aAAU;AAAA;IACjCC,EAAAC,EAAiH,SAAjHC,EAAiH;AAAA,MAAzG,IAAIH,EAAE;AAAA,MAtFlB,uBAAAI,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAsF6BC,EAAa,gBAAAD;AAAA,MAAE,OAAM,CAAA,cAAqBN,EAAA,QAAQ,KAAK;AAAA,MAAE,MAAK;AAAA,IAAgB,GAAAQ,EAAA,MAAM,GAtFjH,MAAA,IAAAZ,CAAA,GAAA;AAAA,UAsF6BW,EAAa,aAAA;AAAA;IAEtCJ,EAEQ,SAAA;AAAA,MAFA,KAAKF,EAAE;AAAA,MAAG,OAxFtBF,EAwF6BC,EAAO,QAAC,KAAK;AAAA,IACjC,GAAAS,EAAAR,EAAA,KAAK,GAzFd,IAAAJ,CAAA;AAAA;;;;;"}
1
+ {"version":3,"file":"Radio.js","sources":["../src/components/Radio/Radio.vue"],"sourcesContent":["<script>\n import { useCssModule } from 'vue';\n\n /**\n * @deprecated use components/RadioNew/Radio instead.\n * if you need multiple Radio components considering using RadioGroup instead.\n */\n export default {\n name: 'll-radio',\n\n props: {\n /**\n * The checked/selected state of the radio button\n */\n checked: {\n type: [String, Number],\n default: undefined,\n },\n\n /**\n * @deprecated Use :checked or v-model:checked instead of :model-value and v-model.\n */\n modelValue: {\n type: [String, Number, null],\n default: null,\n validator(value) {\n if (value !== null) {\n throw new Error('ll-radio: use :checked or v-model:checked instead of :model-value and v-model.');\n }\n\n return true;\n },\n },\n\n /**\n * Adds error styling\n */\n hasError: Boolean,\n\n /**\n * A unique string to distinguish one Radio instance from another\n * and to link the label to the input; required for accessibility\n */\n id: {\n type: String,\n required: true,\n },\n\n /**\n * The description which appears to the right of the radio button\n */\n label: {\n type: String,\n default: '',\n },\n },\n\n emits: ['update:checked'],\n\n setup() {\n return {\n classes: useCssModule(),\n };\n },\n\n computed: {\n internalValue: {\n get() {\n return this.checked;\n },\n set(value) {\n this.$emit('update:checked', value);\n },\n },\n },\n\n created() {\n if (this.$attrs.onChange) {\n throw new Error('ll-radio: use the @update:checked event instead of @input');\n }\n },\n };\n</script>\n\n<template>\n <div class=\"stash-radio\" data-test=\"ll-radio\" :class=\"[classes.root, { error: hasError }]\">\n <input :id=\"id\" v-model=\"internalValue\" class=\"tw-sr-only\" :class=\"classes.input\" type=\"radio\" v-bind=\"$attrs\" />\n\n <label :for=\"id\" :class=\"classes.label\">\n {{ label }}\n </label>\n </div>\n</template>\n\n<style module>\n .root {\n position: relative;\n margin: theme('spacing[1.5]') 0;\n }\n\n .label {\n color: var(--color-ice-700);\n cursor: pointer;\n display: inline-block;\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n line-height: theme('spacing.6');\n min-height: theme('spacing.9');\n overflow: visible;\n padding: theme('spacing[1.5]') 0 theme('spacing[1.5]') calc(20px + theme('spacing.3'));\n position: relative;\n vertical-align: top;\n }\n\n .label::before {\n background: var(--color-white);\n border: 1px solid var(--color-ice-500);\n content: '';\n display: inline-block;\n vertical-align: top;\n }\n\n .label::before,\n .label::after {\n height: 20px;\n left: 0;\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n width: 20px;\n border-radius: 50%;\n }\n\n .error .label {\n color: var(--color-red-500);\n }\n\n .error .label::before,\n .error .input {\n border-color: var(--color-red-500);\n }\n\n .input:checked + .label::after {\n background: var(--color-blue-500);\n content: '';\n height: 14px;\n left: 3px;\n width: 14px;\n }\n\n .input:checked:disabled + .label::after {\n background: var(--color-ice-500);\n }\n\n .input:not(:disabled, :checked) + .label:hover::before {\n border-color: var(--color-blue-500);\n }\n\n @media screen('lg') {\n .root input {\n margin: 0;\n }\n }\n</style>\n"],"names":["_sfc_main","value","useCssModule","_hoisted_1","_hoisted_2","_createElementBlock","_normalizeClass","$setup","$props","_withDirectives","_createElementVNode","_mergeProps","_cache","$event","$options","_ctx","_toDisplayString"],"mappings":";;;;;;;GAOOA,IAAU;AAAA,EACb,MAAM;AAAA,EAEN,OAAO;AAAA;AAAA;AAAA;AAAA,IAIL,SAAS;AAAA,MACP,MAAM,CAAC,QAAQ,MAAM;AAAA,MACrB,SAAS;AAAA,IACV;AAAA;AAAA;AAAA;AAAA,IAKD,YAAY;AAAA,MACV,MAAM,CAAC,QAAQ,QAAQ,IAAI;AAAA,MAC3B,SAAS;AAAA,MACT,UAAUC,GAAO;AACf,YAAIA,MAAU;AACZ,gBAAM,IAAI,MAAM,gFAAgF;AAGlG,eAAO;AAAA,MACR;AAAA,IACF;AAAA;AAAA;AAAA;AAAA,IAKD,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAMV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACX;AAAA;AAAA;AAAA;AAAA,IAKD,OAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,IACV;AAAA,EACF;AAAA,EAED,OAAO,CAAC,gBAAgB;AAAA,EAExB,QAAQ;AACN,WAAO;AAAA,MACL,SAASC,EAAc;AAAA,IACxB;AAAA,EACF;AAAA,EAED,UAAU;AAAA,IACR,eAAe;AAAA,MACb,MAAM;AACJ,eAAO,KAAK;AAAA,MACb;AAAA,MACD,IAAID,GAAO;AACT,aAAK,MAAM,kBAAkBA,CAAK;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA,EAED,UAAU;AACR,QAAI,KAAK,OAAO;AACd,YAAM,IAAI,MAAM,2DAA2D;AAAA,EAE9E;AACF,GAjFHE,IAAA,CAAA,IAAA,GAAAC,IAAA,CAAA,KAAA;;cAqFEC,EAMM,OAAA;AAAA,IAND,OArFPC,GAqFa,eAAa,CAA+BC,UAAQ,eAAeC,EAAQ,SAAA,CAAA,CAAA,CAAA;AAAA,IAA7D,aAAU;AAAA;IACjCC,EAAAC,EAAiH,SAAjHC,EAAiH;AAAA,MAAzG,IAAIH,EAAE;AAAA,MAtFlB,uBAAAI,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAC,MAsF6BC,EAAa,gBAAAD;AAAA,MAAE,OAAM,CAAA,cAAqBN,EAAA,QAAQ,KAAK;AAAA,MAAE,MAAK;AAAA,IAAgB,GAAAQ,EAAA,MAAM,GAtFjH,MAAA,IAAAZ,CAAA,GAAA;AAAA,UAsF6BW,EAAa,aAAA;AAAA;IAEtCJ,EAEQ,SAAA;AAAA,MAFA,KAAKF,EAAE;AAAA,MAAG,OAxFtBF,EAwF6BC,EAAO,QAAC,KAAK;AAAA,IACjC,GAAAS,EAAAR,EAAA,KAAK,GAzFd,IAAAJ,CAAA;AAAA;;;;;"}
@@ -13,7 +13,7 @@ type: (NumberConstructor | StringConstructor)[];
13
13
  default: undefined;
14
14
  };
15
15
  /**
16
- * Use :checked or v-model:checked instead of :model-value and v-model.
16
+ * @deprecated Use :checked or v-model:checked instead of :model-value and v-model.
17
17
  */
18
18
  modelValue: {
19
19
  type: (NumberConstructor | StringConstructor | null)[];
@@ -55,7 +55,7 @@ type: (NumberConstructor | StringConstructor)[];
55
55
  default: undefined;
56
56
  };
57
57
  /**
58
- * Use :checked or v-model:checked instead of :model-value and v-model.
58
+ * @deprecated Use :checked or v-model:checked instead of :model-value and v-model.
59
59
  */
60
60
  modelValue: {
61
61
  type: (NumberConstructor | StringConstructor | null)[];
package/dist/Textarea.js CHANGED
@@ -1,7 +1,7 @@
1
- import { defineComponent as E, useAttrs as M, useSlots as k, useCssModule as H, ref as b, computed as w, watch as A, onMounted as I, nextTick as P, onBeforeUnmount as V, openBlock as $, createBlock as q, mergeProps as x, unref as p, createSlots as D, withCtx as y, createElementVNode as j, renderSlot as F } from "vue";
1
+ import { defineComponent as E, useAttrs as M, useSlots as k, useCssModule as H, ref as w, computed as x, watch as A, onMounted as I, nextTick as P, onBeforeUnmount as V, openBlock as $, createBlock as q, mergeProps as y, unref as p, createSlots as D, withCtx as b, createElementVNode as j, renderSlot as F } from "vue";
2
2
  import { _ as L } from "./Field.vue_vue_type_script_setup_true_lang-DEizIcDo.js";
3
3
  import { _ as N } from "./_plugin-vue_export-helper-CHgC5LLL.js";
4
- const U = ["id", "value", "placeholder", "disabled", "readonly"], W = /* @__PURE__ */ E({
4
+ const U = ["id", "value", "placeholder", "disabled", "readonly", "rows"], W = /* @__PURE__ */ E({
5
5
  name: "ll-textarea",
6
6
  __name: "Textarea",
7
7
  props: {
@@ -9,6 +9,7 @@ const U = ["id", "value", "placeholder", "disabled", "readonly"], W = /* @__PURE
9
9
  value: { default: null },
10
10
  resize: { type: [Boolean, Object], default: !1 },
11
11
  placeholder: { default: void 0 },
12
+ rows: {},
12
13
  addBottomSpace: { type: Boolean },
13
14
  errorText: {},
14
15
  hintText: {},
@@ -24,28 +25,28 @@ const U = ["id", "value", "placeholder", "disabled", "readonly"], W = /* @__PURE
24
25
  },
25
26
  emits: ["update:model-value"],
26
27
  setup(_, { emit: g }) {
27
- const c = M(), B = k(), m = H(), o = _, z = g, u = b(), l = b(), R = w(() => o.isReadOnly || "readonly" in c && c.readonly !== !1), T = w(() => {
28
+ const c = M(), B = k(), m = H(), t = _, z = g, u = w(), l = w(), R = x(() => t.isReadOnly || "readonly" in c && c.readonly !== !1), T = x(() => {
28
29
  const e = { ...c };
29
30
  return delete e["data-test"], delete e.class, e;
30
31
  });
31
32
  A(
32
- () => o.resize,
33
+ () => t.resize,
33
34
  (e) => {
34
- var t;
35
- e ? h() : (t = l.value) == null || t.disconnect();
35
+ var o;
36
+ e ? h() : (o = l.value) == null || o.disconnect();
36
37
  }
37
38
  );
38
39
  const O = (e) => {
39
40
  z("update:model-value", e.target.value);
40
41
  }, h = () => {
41
42
  l.value || !u.value || (l.value = new ResizeObserver(([e]) => {
42
- const { target: t } = e, s = v(u.value) || document.documentElement, { scrollTop: a } = s;
43
+ const { target: o } = e, s = v(u.value) || document.documentElement, { scrollTop: a } = s;
43
44
  let n = 0;
44
45
  if (s === document.documentElement) {
45
- const { top: r, height: i } = C(t), { innerHeight: d } = window;
46
+ const { top: r, height: i } = C(o), { innerHeight: d } = window;
46
47
  n = Math.max(r + i - (d + a), 0);
47
48
  } else {
48
- const { top: r, height: i } = t.getBoundingClientRect(), { top: d } = s.getBoundingClientRect(), { offsetHeight: f } = s, S = r - d;
49
+ const { top: r, height: i } = o.getBoundingClientRect(), { top: d } = s.getBoundingClientRect(), { offsetHeight: f } = s, S = r - d;
49
50
  n = Math.max(S + i - f, 0);
50
51
  }
51
52
  n && requestAnimationFrame(() => {
@@ -53,13 +54,13 @@ const U = ["id", "value", "placeholder", "disabled", "readonly"], W = /* @__PURE
53
54
  });
54
55
  }), l.value.observe(u.value));
55
56
  }, v = (e) => {
56
- const t = e.parentElement;
57
- if (!t)
57
+ const o = e.parentElement;
58
+ if (!o)
58
59
  return null;
59
- const { overflowY: s } = getComputedStyle(t);
60
- return s !== "visible" ? t : t === document.body ? document.documentElement : v(t);
60
+ const { overflowY: s } = getComputedStyle(o);
61
+ return s !== "visible" ? o : o === document.body ? document.documentElement : v(o);
61
62
  }, C = (e) => {
62
- const { offsetWidth: t, offsetHeight: s } = e;
63
+ const { offsetWidth: o, offsetHeight: s } = e;
63
64
  let a = 0, n = 0;
64
65
  const r = function({ offsetLeft: i, offsetTop: d, offsetParent: f }) {
65
66
  a += i, n += d, f && r(f);
@@ -67,26 +68,26 @@ const U = ["id", "value", "placeholder", "disabled", "readonly"], W = /* @__PURE
67
68
  return r(e), {
68
69
  top: n,
69
70
  left: a,
70
- width: t,
71
+ width: o,
71
72
  height: s
72
73
  };
73
74
  };
74
75
  return I(async () => {
75
76
  var e;
76
- if (o.value !== null)
77
+ if (t.value !== null)
77
78
  throw new Error("ll-input: use :model-value or v-model instead of :value.");
78
79
  if (c.onInput)
79
80
  throw new Error("ll-input: use the @update:model-value event instead of @input");
80
- (typeof o.resize == "boolean" && o.resize || (e = o.resize) != null && e.forceBrowserScroll) && (await P(), h());
81
+ (typeof t.resize == "boolean" && t.resize || (e = t.resize) != null && e.forceBrowserScroll) && (await P(), h());
81
82
  }), V(() => {
82
83
  var e;
83
84
  (e = l.value) == null || e.disconnect();
84
- }), (e, t) => ($(), q(L, x(o, {
85
+ }), (e, o) => ($(), q(L, y(t, {
85
86
  class: ["stash-textarea", [p(m).root]],
86
87
  "data-test": "stash-textarea"
87
88
  }), D({
88
- default: y(({ fieldId: s, hasError: a }) => [
89
- j("textarea", x({
89
+ default: b(({ fieldId: s, hasError: a }) => [
90
+ j("textarea", y({
90
91
  id: s,
91
92
  ref_key: "textareaRef",
92
93
  ref: u,
@@ -95,17 +96,19 @@ const U = ["id", "value", "placeholder", "disabled", "readonly"], W = /* @__PURE
95
96
  "tw-border tw-border-ice-500",
96
97
  {
97
98
  "stash-textarea--error tw-border-red-500 tw-text-red-500": a,
98
- "tw-text-ice-700 hover:tw-border-ice-500 focus:tw-border-blue-500 active:tw-border-blue-500": !a && !o.disabled,
99
- "tw-resize-y": o.resize,
100
- "tw-resize-none": !o.resize
99
+ "tw-text-ice-700 hover:tw-border-ice-500 focus:tw-border-blue-500 active:tw-border-blue-500": !a && !t.disabled,
100
+ "tw-resize-y": t.resize,
101
+ "tw-min-h-[100px]": !t.rows,
102
+ "tw-resize-none": !t.resize
101
103
  }
102
104
  ],
103
- value: o.modelValue,
105
+ value: t.modelValue,
104
106
  "data-test": "stash-textarea|textarea",
105
- placeholder: o.placeholder
107
+ placeholder: t.placeholder
106
108
  }, T.value, {
107
- disabled: o.disabled,
109
+ disabled: t.disabled,
108
110
  readonly: R.value,
111
+ rows: t.rows,
109
112
  onInput: O
110
113
  }), null, 16, U)
111
114
  ]),
@@ -113,14 +116,14 @@ const U = ["id", "value", "placeholder", "disabled", "readonly"], W = /* @__PURE
113
116
  }, [
114
117
  p(B).hint ? {
115
118
  name: "hint",
116
- fn: y(() => [
119
+ fn: b(() => [
117
120
  F(e.$slots, "hint")
118
121
  ]),
119
122
  key: "0"
120
123
  } : void 0
121
124
  ]), 1040, ["class"]));
122
125
  }
123
- }), Y = "_root_fulrr_2", G = "_textarea_fulrr_7", J = {
126
+ }), Y = "_root_slydx_2", G = "_textarea_slydx_7", J = {
124
127
  root: Y,
125
128
  textarea: G
126
129
  }, K = {
@@ -1 +1 @@
1
- {"version":3,"file":"Textarea.js","sources":["../src/components/Textarea/Textarea.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, nextTick, onBeforeUnmount, onMounted, ref, useAttrs, useCssModule, useSlots, watch } from 'vue';\n\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n export interface TextareaResizeOptions {\n /**\n * It will automatically scroll the page down when it reaches the bottom of the viewport/element\n */\n forceBrowserScroll: boolean;\n }\n\n export interface TextAreaProps extends FieldProps {\n /**\n * Value for the textarea element.\n */\n modelValue?: string;\n\n /**\n * Deprecated. Use :model-value or v-model instead of :value.\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: string | number | null;\n\n /**\n * Allow textarea to be resizable vertically.\n * Alternatively if you want to disable automatic scroll when resizing, you can set `{ forceBrowserScroll: false }`\n */\n resize?: boolean | TextareaResizeOptions;\n\n /**\n * Placeholder text for the textarea element.\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-textarea',\n });\n\n const attrs = useAttrs();\n const slots = useSlots();\n const classes = useCssModule();\n\n const props = withDefaults(defineProps<TextAreaProps>(), {\n modelValue: '',\n value: null,\n resize: false,\n placeholder: undefined,\n });\n\n const emits = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', value: string): void;\n }>();\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const observer = ref<ResizeObserver>();\n const isReadOnly = computed(() => props.isReadOnly || ('readonly' in attrs && attrs.readonly !== false));\n\n const inputAttrs = computed(() => {\n const allAttrs = { ...attrs };\n\n delete allAttrs['data-test'];\n delete allAttrs.class;\n\n return allAttrs;\n });\n\n watch(\n () => props.resize,\n (v) => {\n v ? setupResizeObserver() : observer.value?.disconnect();\n },\n );\n\n const onInput = (event: Event) => {\n emits('update:model-value', (event.target as HTMLTextAreaElement).value);\n };\n\n const setupResizeObserver = () => {\n if (observer.value || !textareaRef.value) {\n return;\n }\n\n // the ResizeObserver will be in charge to detect if page needs to scroll when resizing the component\n observer.value = new ResizeObserver(([entry]) => {\n const { target } = entry;\n const parent = findParentScrollable(textareaRef.value as HTMLTextAreaElement) || document.documentElement;\n\n const { scrollTop: scrollPosition } = parent;\n let offsetDiff = 0;\n\n // checks if the closest parent element scrollable is the document page\n if (parent === document.documentElement) {\n const { top, height } = getOffsetClipRect(target as HTMLElement);\n const { innerHeight: viewportHeight } = window;\n\n offsetDiff = Math.max(top + height - (viewportHeight + scrollPosition), 0);\n } else {\n const { top, height } = (target as HTMLElement).getBoundingClientRect();\n const { top: parentTop } = parent.getBoundingClientRect();\n const { offsetHeight: parentHeight } = parent;\n const offsetTop = top - parentTop;\n\n offsetDiff = Math.max(offsetTop + height - parentHeight, 0);\n }\n\n if (offsetDiff) {\n requestAnimationFrame(() => {\n parent.scrollTop = scrollPosition + offsetDiff;\n });\n }\n });\n\n observer.value.observe(textareaRef.value);\n };\n\n /**\n * Retrieve the closest parent that has a scroll. Defaults to the document page.\n */\n const findParentScrollable = (el: HTMLElement): HTMLElement | null => {\n const parent = el.parentElement as HTMLElement;\n if (!parent) {\n return null;\n }\n\n const { overflowY } = getComputedStyle(parent);\n if (overflowY !== 'visible') {\n return parent;\n }\n\n if (parent === document.body) {\n return document.documentElement;\n }\n\n return findParentScrollable(parent);\n };\n\n /**\n * Retrieve element absolute positioning relative to the page.\n */\n const getOffsetClipRect = (el: HTMLElement) => {\n const { offsetWidth: width, offsetHeight: height } = el;\n\n let left = 0;\n let top = 0;\n\n const findPos = function ({ offsetLeft, offsetTop, offsetParent }: HTMLElement) {\n left += offsetLeft;\n top += offsetTop;\n\n if (offsetParent) {\n findPos(offsetParent as HTMLElement);\n }\n };\n\n findPos(el);\n\n return {\n top,\n left,\n width,\n height,\n };\n };\n\n onMounted(async () => {\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 if (\n (typeof props.resize === 'boolean' && props.resize) ||\n (props.resize as TextareaResizeOptions)?.forceBrowserScroll\n ) {\n await nextTick();\n setupResizeObserver();\n }\n });\n\n onBeforeUnmount(() => {\n observer.value?.disconnect();\n });\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-textarea\" :class=\"[classes.root]\" data-test=\"stash-textarea\">\n <template #default=\"{ fieldId, hasError }\">\n <textarea\n :id=\"fieldId\"\n ref=\"textareaRef\"\n :class=\"[\n classes.textarea,\n 'tw-border tw-border-ice-500',\n {\n 'stash-textarea--error tw-border-red-500 tw-text-red-500': hasError,\n 'tw-text-ice-700 hover:tw-border-ice-500 focus:tw-border-blue-500 active:tw-border-blue-500':\n !hasError && !props.disabled,\n 'tw-resize-y': props.resize,\n 'tw-resize-none': !props.resize,\n },\n ]\"\n :value=\"props.modelValue\"\n data-test=\"stash-textarea|textarea\"\n :placeholder=\"props.placeholder\"\n v-bind=\"inputAttrs\"\n :disabled=\"props.disabled\"\n :readonly=\"isReadOnly\"\n @input=\"onInput\"\n ></textarea>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Hint content -->\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .root {\n position: relative;\n width: 100%;\n }\n\n .textarea {\n background: var(--color-white);\n border-radius: theme('borderRadius.DEFAULT');\n display: block;\n min-height: 100px;\n outline: none;\n padding: theme('spacing[1.5]');\n width: 100%;\n\n &::placeholder {\n color: var(--color-ice-500);\n opacity: 1;\n }\n\n &[disabled] {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none;\n }\n\n &[disabled]:active,\n &[disabled]:focus {\n box-shadow: none;\n }\n\n &[disabled]::placeholder {\n text-transform: none;\n color: var(--color-ice-500);\n }\n\n &[readonly] {\n border-color: transparent;\n background-color: transparent;\n padding-left: 0;\n padding-right: 0;\n min-height: unset;\n }\n }\n</style>\n"],"names":["attrs","useAttrs","slots","useSlots","classes","useCssModule","props","__props","emits","__emit","textareaRef","ref","observer","isReadOnly","computed","inputAttrs","allAttrs","watch","v","setupResizeObserver","_a","onInput","event","entry","target","parent","findParentScrollable","scrollPosition","offsetDiff","top","height","getOffsetClipRect","viewportHeight","parentTop","parentHeight","offsetTop","el","overflowY","width","left","findPos","offsetLeft","offsetParent","onMounted","nextTick","onBeforeUnmount"],"mappings":"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"}
1
+ {"version":3,"file":"Textarea.js","sources":["../src/components/Textarea/Textarea.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, nextTick, onBeforeUnmount, onMounted, ref, useAttrs, useCssModule, useSlots, watch } from 'vue';\n\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n export interface TextareaResizeOptions {\n /**\n * It will automatically scroll the page down when it reaches the bottom of the viewport/element\n */\n forceBrowserScroll: boolean;\n }\n\n export interface TextAreaProps extends FieldProps {\n /**\n * Value for the textarea element.\n */\n modelValue?: string;\n\n /**\n * Deprecated. Use :model-value or v-model instead of :value.\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: string | number | null;\n\n /**\n * Allow textarea to be resizable vertically.\n * Alternatively if you want to disable automatic scroll when resizing, you can set `{ forceBrowserScroll: false }`\n */\n resize?: boolean | TextareaResizeOptions;\n\n /**\n * Placeholder text for the textarea element.\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 * Number of rows to display in the textarea.\n */\n rows?: number;\n }\n\n defineOptions({\n name: 'll-textarea',\n });\n\n const attrs = useAttrs();\n const slots = useSlots();\n const classes = useCssModule();\n\n const props = withDefaults(defineProps<TextAreaProps>(), {\n modelValue: '',\n value: null,\n resize: false,\n placeholder: undefined,\n });\n\n const emits = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', value: string): void;\n }>();\n\n const textareaRef = ref<HTMLTextAreaElement>();\n const observer = ref<ResizeObserver>();\n const isReadOnly = computed(() => props.isReadOnly || ('readonly' in attrs && attrs.readonly !== false));\n\n const inputAttrs = computed(() => {\n const allAttrs = { ...attrs };\n\n delete allAttrs['data-test'];\n delete allAttrs.class;\n\n return allAttrs;\n });\n\n watch(\n () => props.resize,\n (v) => {\n v ? setupResizeObserver() : observer.value?.disconnect();\n },\n );\n\n const onInput = (event: Event) => {\n emits('update:model-value', (event.target as HTMLTextAreaElement).value);\n };\n\n const setupResizeObserver = () => {\n if (observer.value || !textareaRef.value) {\n return;\n }\n\n // the ResizeObserver will be in charge to detect if page needs to scroll when resizing the component\n observer.value = new ResizeObserver(([entry]) => {\n const { target } = entry;\n const parent = findParentScrollable(textareaRef.value as HTMLTextAreaElement) || document.documentElement;\n\n const { scrollTop: scrollPosition } = parent;\n let offsetDiff = 0;\n\n // checks if the closest parent element scrollable is the document page\n if (parent === document.documentElement) {\n const { top, height } = getOffsetClipRect(target as HTMLElement);\n const { innerHeight: viewportHeight } = window;\n\n offsetDiff = Math.max(top + height - (viewportHeight + scrollPosition), 0);\n } else {\n const { top, height } = (target as HTMLElement).getBoundingClientRect();\n const { top: parentTop } = parent.getBoundingClientRect();\n const { offsetHeight: parentHeight } = parent;\n const offsetTop = top - parentTop;\n\n offsetDiff = Math.max(offsetTop + height - parentHeight, 0);\n }\n\n if (offsetDiff) {\n requestAnimationFrame(() => {\n parent.scrollTop = scrollPosition + offsetDiff;\n });\n }\n });\n\n observer.value.observe(textareaRef.value);\n };\n\n /**\n * Retrieve the closest parent that has a scroll. Defaults to the document page.\n */\n const findParentScrollable = (el: HTMLElement): HTMLElement | null => {\n const parent = el.parentElement as HTMLElement;\n if (!parent) {\n return null;\n }\n\n const { overflowY } = getComputedStyle(parent);\n if (overflowY !== 'visible') {\n return parent;\n }\n\n if (parent === document.body) {\n return document.documentElement;\n }\n\n return findParentScrollable(parent);\n };\n\n /**\n * Retrieve element absolute positioning relative to the page.\n */\n const getOffsetClipRect = (el: HTMLElement) => {\n const { offsetWidth: width, offsetHeight: height } = el;\n\n let left = 0;\n let top = 0;\n\n const findPos = function ({ offsetLeft, offsetTop, offsetParent }: HTMLElement) {\n left += offsetLeft;\n top += offsetTop;\n\n if (offsetParent) {\n findPos(offsetParent as HTMLElement);\n }\n };\n\n findPos(el);\n\n return {\n top,\n left,\n width,\n height,\n };\n };\n\n onMounted(async () => {\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 if (\n (typeof props.resize === 'boolean' && props.resize) ||\n (props.resize as TextareaResizeOptions)?.forceBrowserScroll\n ) {\n await nextTick();\n setupResizeObserver();\n }\n });\n\n onBeforeUnmount(() => {\n observer.value?.disconnect();\n });\n</script>\n\n<template>\n <Field v-bind=\"props\" class=\"stash-textarea\" :class=\"[classes.root]\" data-test=\"stash-textarea\">\n <template #default=\"{ fieldId, hasError }\">\n <textarea\n :id=\"fieldId\"\n ref=\"textareaRef\"\n :class=\"[\n classes.textarea,\n 'tw-border tw-border-ice-500',\n {\n 'stash-textarea--error tw-border-red-500 tw-text-red-500': hasError,\n 'tw-text-ice-700 hover:tw-border-ice-500 focus:tw-border-blue-500 active:tw-border-blue-500':\n !hasError && !props.disabled,\n 'tw-resize-y': props.resize,\n 'tw-min-h-[100px]': !props.rows,\n 'tw-resize-none': !props.resize,\n },\n ]\"\n :value=\"props.modelValue\"\n data-test=\"stash-textarea|textarea\"\n :placeholder=\"props.placeholder\"\n v-bind=\"inputAttrs\"\n :disabled=\"props.disabled\"\n :readonly=\"isReadOnly\"\n :rows=\"props.rows\"\n @input=\"onInput\"\n ></textarea>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Hint content -->\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style module>\n .root {\n position: relative;\n width: 100%;\n }\n\n .textarea {\n background: var(--color-white);\n border-radius: theme('borderRadius.DEFAULT');\n display: block;\n outline: none;\n padding: theme('spacing[1.5]');\n width: 100%;\n\n &::placeholder {\n color: var(--color-ice-500);\n opacity: 1;\n }\n\n &[disabled] {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none;\n }\n\n &[disabled]:active,\n &[disabled]:focus {\n box-shadow: none;\n }\n\n &[disabled]::placeholder {\n text-transform: none;\n color: var(--color-ice-500);\n }\n\n &[readonly] {\n border-color: transparent;\n background-color: transparent;\n padding-left: 0;\n padding-right: 0;\n min-height: unset;\n }\n }\n</style>\n"],"names":["attrs","useAttrs","slots","useSlots","classes","useCssModule","props","__props","emits","__emit","textareaRef","ref","observer","isReadOnly","computed","inputAttrs","allAttrs","watch","v","setupResizeObserver","_a","onInput","event","entry","target","parent","findParentScrollable","scrollPosition","offsetDiff","top","height","getOffsetClipRect","viewportHeight","parentTop","parentHeight","offsetTop","el","overflowY","width","left","findPos","offsetLeft","offsetParent","onMounted","nextTick","onBeforeUnmount"],"mappings":"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"}
@@ -129,6 +129,10 @@ export declare interface TextAreaProps extends FieldProps {
129
129
  * **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.
130
130
  */
131
131
  placeholder?: string;
132
+ /**
133
+ * Number of rows to display in the textarea.
134
+ */
135
+ rows?: number;
132
136
  }
133
137
 
134
138
  export declare interface TextareaResizeOptions {