@milaboratories/uikit 2.6.5 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +47 -35
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/components/PlAccordion/ExpandTransition.vue3.js +1 -1
- package/dist/components/PlAccordion/PlAccordionSection.vue2.js +1 -1
- package/dist/components/PlAutocomplete/PlAutocomplete.vue.d.ts +1 -5
- package/dist/components/PlAutocomplete/PlAutocomplete.vue.js +46 -47
- package/dist/components/PlAutocomplete/PlAutocomplete.vue.js.map +1 -1
- package/dist/components/PlAutocompleteMulti/PlAutocompleteMulti.vue.js +29 -30
- package/dist/components/PlAutocompleteMulti/PlAutocompleteMulti.vue.js.map +1 -1
- package/package.json +4 -4
- package/src/components/PlAutocomplete/PlAutocomplete.vue +3 -10
- package/src/components/PlAutocompleteMulti/PlAutocompleteMulti.vue +8 -7
- package/src/components/PlAutocompleteMulti/__tests__/PlAutocompleteMulti.spec.ts +0 -6
- package/dist/components/PlAccordion/ExpandTransition.vue.js +0 -27
- package/dist/components/PlAccordion/ExpandTransition.vue.js.map +0 -1
- package/dist/components/PlAutocompleteMulti/PlAutocompleteMulti.vue.d.ts +0 -111
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlAutocomplete.vue.js","sources":["../../../src/components/PlAutocomplete/PlAutocomplete.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * A component for selecting one value from a big list of options using string search request\n */\nexport default {\n name: 'PlAutocomplete',\n};\n</script>\n\n<script lang=\"ts\" setup generic=\"M extends null | undefined | string | number = null | undefined | string\">\nimport './pl-autocomplete.scss';\nimport { computed, reactive, ref, unref, useTemplateRef, watch, watchPostEffect } from 'vue';\nimport { tap } from '../../helpers/functions';\nimport { PlTooltip } from '../PlTooltip';\nimport DoubleContour from '../../utils/DoubleContour.vue';\nimport { useLabelNotch } from '../../utils/useLabelNotch';\nimport type { ListOption, ListOptionNormalized } from '../../types';\nimport { deepEqual } from '../../helpers/objects';\nimport DropdownListItem from '../DropdownListItem.vue';\nimport LongText from '../LongText.vue';\nimport { normalizeListOptions } from '../../helpers/utils';\nimport { PlIcon16 } from '../PlIcon16';\nimport { PlMaskIcon24 } from '../PlMaskIcon24';\nimport { DropdownOverlay } from '../../utils/DropdownOverlay';\nimport { refDebounced } from '@vueuse/core';\nimport { useWatchFetch } from '../../composition/useWatchFetch.ts';\nimport { getErrorMessage } from '../../helpers/error.ts';\nimport type { ListOptionBase } from '@platforma-sdk/model';\nimport { PlSvg } from '../PlSvg';\nimport SvgRequired from '../../assets/images/required.svg?raw';\n\n/**\n * The current selected value.\n */\nconst model = defineModel<M>({ required: true });\n\nconst props = withDefaults(\n defineProps<{\n /**\n * Lambda for requesting of available options for the dropdown by search string.\n */\n optionsSearch: (s: string) => Promise<ListOption<M>[]>;\n /**\n * Lambda for requesting of corresponding option for current model value. If empty, optionsSearch is used for this.\n */\n modelSearch?: (v: NonNullable<M>) => Promise<ListOption<NonNullable<M>>>;\n /**\n * The label text for the dropdown field (optional)\n */\n label?: string;\n /**\n * A helper text displayed below the dropdown when there are no errors (optional).\n */\n helper?: string;\n /**\n * A helper text displayed below the dropdown when there are no options yet or options is undefined (optional).\n */\n loadingOptionsHelper?: string;\n /**\n * Error message displayed below the dropdown (optional)\n */\n error?: unknown;\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n /**\n * Enables a button to clear the selected value (default: false)\n */\n clearable?: boolean;\n /**\n * If `true`, the dropdown component is marked as required.\n */\n required?: boolean;\n /**\n * If `true`, the dropdown component is disabled and cannot be interacted with.\n */\n disabled?: boolean;\n /**\n * Custom icon (16px) class for the dropdown arrow (optional)\n */\n arrowIcon?: string;\n /**\n * Custom icon (24px) class for the dropdown arrow (optional)\n */\n arrowIconLarge?: string;\n /**\n * Option list item size\n */\n optionSize?: 'small' | 'medium';\n /**\n * Formatter for the selected value if its label is absent\n */\n formatValue?: (value: M) => string;\n /**\n * Makes some of corners not rounded\n * */\n groupPosition?: 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'middle';\n }>(),\n {\n modelSearch: undefined,\n label: '',\n helper: undefined,\n loadingOptionsHelper: undefined,\n error: undefined,\n placeholder: '...',\n clearable: false,\n required: false,\n disabled: false,\n arrowIcon: undefined,\n arrowIconLarge: undefined,\n optionSize: 'small',\n formatValue: (v: M) => String(v),\n groupPosition: undefined,\n },\n);\n\nconst slots = defineSlots<{\n [key: string]: unknown;\n}>();\n\nconst rootRef = ref<HTMLElement | undefined>();\nconst input = ref<HTMLInputElement | undefined>();\n\nconst overlayRef = useTemplateRef('overlay');\n\nconst search = ref<string | null>(null);\nconst data = reactive({\n activeIndex: -1,\n open: false,\n});\n\nconst findActiveIndex = () =>\n tap(\n renderedOptionsRef.value.findIndex((o) => deepEqual(o.value, model.value)),\n (v) => (v < 0 ? 0 : v),\n );\n\nconst updateActive = () => (data.activeIndex = findActiveIndex());\n\nconst loadedOptionsRef = ref<ListOption<M>[]>([]);\nconst modelOptionRef = ref<ListOptionNormalized<M> | undefined>(); // list of 1 option that is selected or empty, to keep selected label\n\nconst renderedOptionsRef = computed(() => {\n return normalizeListOptions(loadedOptionsRef.value).map((opt, index) => ({\n ...opt,\n index,\n isSelected: index === selectedIndex.value,\n isActive: index === data.activeIndex,\n })) as (ListOptionBase<M> & { index: number; isSelected: boolean; isActive: boolean })[];\n});\nconst isLoadingOptions = ref<boolean>(true);\nconst isLoadingError = ref<boolean>(false);\n\nconst isDisabled = computed(() => {\n return props.disabled;\n});\n\nconst selectedIndex = computed(() => {\n return loadedOptionsRef.value.findIndex((o) => deepEqual(o.value, model.value));\n});\n\nconst computedError = computed(() => {\n if (isLoadingOptions.value) {\n return undefined;\n }\n\n if (props.error) {\n return getErrorMessage(props.error);\n }\n\n if (isLoadingError.value) {\n return 'Data loading error';\n }\n\n return undefined;\n});\n\nconst textValue = computed(() => {\n const modelOption = unref(modelOptionRef);\n const options = unref(renderedOptionsRef);\n\n const item: ListOptionNormalized | undefined = modelOption ?? options.find((o) => deepEqual(o.value, model.value)) ?? options.find((o) => deepEqual(o.value, model.value));\n\n return item?.label || (model.value ? props.formatValue(model.value) : '');\n});\n\nconst computedPlaceholder = computed(() => {\n if (!data.open && model.value) {\n return '';\n }\n\n return model.value ? String(textValue.value) : props.placeholder;\n});\n\nconst hasValue = computed(() => {\n return model.value !== undefined && model.value !== null;\n});\n\nconst tabindex = computed(() => (isDisabled.value ? undefined : '0'));\n\nconst selectOption = (v: ListOptionBase<M> & { index: number } | undefined) => {\n model.value = v?.value as M;\n modelOptionRef.value = v;\n search.value = null;\n data.open = false;\n rootRef?.value?.focus();\n};\n\nconst clear = () => {\n model.value = undefined as M;\n modelOptionRef.value = undefined;\n};\n\nconst setFocusOnInput = () => input.value?.focus();\n\nconst toggleOpen = () => {\n data.open = !data.open;\n};\n\nwatch(() => data.open, (v) => {\n search.value = v ? '' : null;\n});\n\nconst onInputFocus = () => {\n data.open = true;\n};\n\nconst onFocusOut = (event: FocusEvent) => {\n const relatedTarget = event.relatedTarget as Node | null;\n\n if (!rootRef.value?.contains(relatedTarget) && !overlayRef.value?.listRef?.contains(relatedTarget)) {\n search.value = null;\n data.open = false;\n }\n};\n\nconst handleKeydown = (e: { code: string; preventDefault(): void }) => {\n if (!['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.code)) {\n return;\n } else {\n e.preventDefault();\n }\n\n const { open, activeIndex } = data;\n\n if (!open) {\n if (e.code === 'Enter') {\n data.open = true;\n search.value = '';\n }\n return;\n }\n\n if (e.code === 'Escape') {\n data.open = false;\n search.value = null;\n rootRef.value?.focus();\n }\n\n const options = unref(renderedOptionsRef);\n\n const { length } = options;\n\n if (!length) {\n return;\n }\n\n if (e.code === 'Enter') {\n selectOption(options.find((it) => it.index === activeIndex));\n }\n\n const localIndex = options.findIndex((it) => it.index === activeIndex) ?? -1;\n\n const delta = e.code === 'ArrowDown' ? 1 : e.code === 'ArrowUp' ? -1 : 0;\n\n const newIndex = Math.abs(localIndex + delta + length) % length;\n\n data.activeIndex = renderedOptionsRef.value[newIndex].index ?? -1;\n};\n\nuseLabelNotch(rootRef);\n\nwatch(() => model.value, updateActive, { immediate: true });\n\nwatch(\n () => data.open,\n (open) => (open ? input.value?.focus() : ''),\n);\n\nwatchPostEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n search.value; // to watch\n\n if (data.activeIndex >= 0 && data.open) {\n overlayRef.value?.scrollIntoActive();\n }\n});\n\nconst searchDebounced = refDebounced(search, 300, { maxWait: 1000 });\n\nconst optionsRequest = useWatchFetch(() => searchDebounced.value, async (v) => {\n if (v !== null) { // search is null when dropdown is closed;\n return props.optionsSearch(v);\n }\n return undefined;\n});\n\nconst modelOptionRequest = useWatchFetch(() => model.value, async (v) => {\n if (v != null && !deepEqual(modelOptionRef.value?.value, v)) { // load label for selected value if it was updated from outside the component\n if (props.modelSearch) {\n return props.modelSearch(v);\n }\n return (await props.optionsSearch(String(v)))?.[0];\n }\n return modelOptionRef.value;\n});\n\nwatch(() => optionsRequest.value, (result) => {\n if (result) {\n loadedOptionsRef.value = result;\n if (search.value !== null) {\n isLoadingError.value = false;\n }\n }\n});\n\nwatch(() => modelOptionRequest.value, (result) => {\n if (result) {\n modelOptionRef.value = normalizeListOptions([result])[0];\n }\n});\n\nwatch(() => optionsRequest.error, (err) => {\n if (err) {\n isLoadingError.value = Boolean(err);\n }\n});\n\nwatch(() => optionsRequest.loading || modelOptionRequest.loading, (loading) => {\n isLoadingOptions.value = loading;\n});\n</script>\n\n<template>\n <div class=\"pl-autocomplete__envelope\" @click.stop=\"setFocusOnInput\">\n <div\n ref=\"rootRef\"\n :tabindex=\"tabindex\"\n class=\"pl-autocomplete\"\n :class=\"{ open: data.open, error: Boolean(computedError), disabled: isDisabled }\"\n @keydown=\"handleKeydown\"\n @focusout=\"onFocusOut\"\n >\n <div class=\"pl-autocomplete__container\">\n <div class=\"pl-autocomplete__field\">\n <input\n ref=\"input\"\n v-model=\"search\"\n type=\"text\"\n tabindex=\"-1\"\n :disabled=\"isDisabled\"\n :placeholder=\"computedPlaceholder\"\n spellcheck=\"false\"\n autocomplete=\"chrome-off\"\n @focus=\"onInputFocus\"\n />\n\n <div v-if=\"!data.open\" class=\"input-value\">\n <LongText> {{ textValue }} </LongText>\n </div>\n\n <div class=\"pl-autocomplete__controls\">\n <PlMaskIcon24 v-if=\"isLoadingOptions\" name=\"loading\" />\n <PlIcon16 v-if=\"clearable && hasValue\" class=\"clear\" name=\"delete-clear\" @click.stop=\"clear\" />\n <slot name=\"append\" />\n <div class=\"pl-autocomplete__arrow-wrapper\" @click.stop=\"toggleOpen\">\n <div v-if=\"arrowIconLarge\" class=\"arrow-icon\" :class=\"[`icon-24 ${arrowIconLarge}`]\" />\n <div v-else-if=\"arrowIcon\" class=\"arrow-icon\" :class=\"[`icon-16 ${arrowIcon}`]\" />\n <div v-else class=\"arrow-icon arrow-icon-default\" />\n </div>\n </div>\n </div>\n <label v-if=\"label\">\n <PlSvg v-if=\"required\" :uri=\"SvgRequired\" />\n <span>{{ label }}</span>\n <PlTooltip v-if=\"slots.tooltip\" class=\"info\" position=\"top\">\n <template #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlTooltip>\n </label>\n <DropdownOverlay v-if=\"data.open\" ref=\"overlay\" :root=\"rootRef\" class=\"pl-autocomplete__options\" tabindex=\"-1\" :gap=\"3\">\n <DropdownListItem\n v-for=\"(item, index) in renderedOptionsRef\"\n :key=\"index\"\n :option=\"item\"\n :is-selected=\"item.isSelected\"\n :is-hovered=\"item.isActive\"\n :size=\"optionSize\"\n @click.stop=\"selectOption(item)\"\n />\n <div v-if=\"!renderedOptionsRef.length\" class=\"nothing-found\">Nothing found</div>\n </DropdownOverlay>\n <DoubleContour class=\"pl-autocomplete__contour\" :group-position=\"groupPosition\" />\n </div>\n </div>\n <div v-if=\"computedError\" class=\"pl-autocomplete__error\">{{ computedError }}</div>\n <div v-else-if=\"isLoadingOptions && loadingOptionsHelper\" class=\"pl-autocomplete__helper\">{{ loadingOptionsHelper }}</div>\n <div v-else-if=\"helper\" class=\"pl-autocomplete__helper\">{{ helper }}</div>\n </div>\n</template>\n"],"names":["__default__","model","_useModel","props","__props","slots","_useSlots","rootRef","ref","input","overlayRef","useTemplateRef","search","data","reactive","findActiveIndex","tap","renderedOptionsRef","o","deepEqual","v","updateActive","loadedOptionsRef","modelOptionRef","computed","normalizeListOptions","opt","index","selectedIndex","isLoadingOptions","isLoadingError","isDisabled","computedError","getErrorMessage","textValue","modelOption","unref","options","item","computedPlaceholder","hasValue","tabindex","selectOption","_a","clear","setFocusOnInput","toggleOpen","watch","onInputFocus","onFocusOut","event","relatedTarget","_c","_b","handleKeydown","open","activeIndex","length","it","localIndex","delta","newIndex","useLabelNotch","watchPostEffect","searchDebounced","refDebounced","optionsRequest","useWatchFetch","modelOptionRequest","result","err","loading","_createElementBlock","_createElementVNode","_normalizeClass","_hoisted_2","_hoisted_3","$event","_openBlock","_hoisted_5","_createVNode","LongText","_hoisted_6","_createBlock","_unref","PlMaskIcon24","PlIcon16","_renderSlot","_ctx","_hoisted_7","_hoisted_8","PlSvg","SvgRequired","PlTooltip","DropdownOverlay","_Fragment","_renderList","DropdownListItem","_withModifiers","_hoisted_9","DoubleContour","_hoisted_10","_toDisplayString","_hoisted_11","_hoisted_12"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PlAutocomplete.vue.js","sources":["../../../src/components/PlAutocomplete/PlAutocomplete.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * A component for selecting one value from a big list of options using string search request\n */\nexport default {\n name: 'PlAutocomplete',\n};\n</script>\n\n<script lang=\"ts\" setup generic=\"M extends null | undefined | string | number = null | undefined | string\">\nimport './pl-autocomplete.scss';\nimport { computed, reactive, ref, unref, useTemplateRef, watch, watchPostEffect } from 'vue';\nimport { tap } from '../../helpers/functions';\nimport { PlTooltip } from '../PlTooltip';\nimport DoubleContour from '../../utils/DoubleContour.vue';\nimport { useLabelNotch } from '../../utils/useLabelNotch';\nimport type { ListOption, ListOptionNormalized } from '../../types';\nimport { deepEqual } from '../../helpers/objects';\nimport DropdownListItem from '../DropdownListItem.vue';\nimport LongText from '../LongText.vue';\nimport { normalizeListOptions } from '../../helpers/utils';\nimport { PlIcon16 } from '../PlIcon16';\nimport { PlMaskIcon24 } from '../PlMaskIcon24';\nimport { DropdownOverlay } from '../../utils/DropdownOverlay';\nimport { refDebounced } from '@vueuse/core';\nimport { useWatchFetch } from '../../composition/useWatchFetch.ts';\nimport { getErrorMessage } from '../../helpers/error.ts';\nimport type { ListOptionBase } from '@platforma-sdk/model';\nimport { PlSvg } from '../PlSvg';\nimport SvgRequired from '../../assets/images/required.svg?raw';\n\n/**\n * The current selected value.\n */\nconst model = defineModel<M>({ required: true });\n\nconst props = withDefaults(\n defineProps<{\n /**\n * Lambda for requesting of available options for the dropdown by search string.\n */\n optionsSearch: (string: string, type: 'value' | 'label') => Promise<ListOption<M>[]>;\n /**\n * The label text for the dropdown field (optional)\n */\n label?: string;\n /**\n * A helper text displayed below the dropdown when there are no errors (optional).\n */\n helper?: string;\n /**\n * A helper text displayed below the dropdown when there are no options yet or options is undefined (optional).\n */\n loadingOptionsHelper?: string;\n /**\n * Error message displayed below the dropdown (optional)\n */\n error?: unknown;\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n /**\n * Enables a button to clear the selected value (default: false)\n */\n clearable?: boolean;\n /**\n * If `true`, the dropdown component is marked as required.\n */\n required?: boolean;\n /**\n * If `true`, the dropdown component is disabled and cannot be interacted with.\n */\n disabled?: boolean;\n /**\n * Custom icon (16px) class for the dropdown arrow (optional)\n */\n arrowIcon?: string;\n /**\n * Custom icon (24px) class for the dropdown arrow (optional)\n */\n arrowIconLarge?: string;\n /**\n * Option list item size\n */\n optionSize?: 'small' | 'medium';\n /**\n * Formatter for the selected value if its label is absent\n */\n formatValue?: (value: M) => string;\n /**\n * Makes some of corners not rounded\n * */\n groupPosition?: 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'middle';\n }>(),\n {\n modelSearch: undefined,\n label: '',\n helper: undefined,\n loadingOptionsHelper: undefined,\n error: undefined,\n placeholder: '...',\n clearable: false,\n required: false,\n disabled: false,\n arrowIcon: undefined,\n arrowIconLarge: undefined,\n optionSize: 'small',\n formatValue: (v: M) => String(v),\n groupPosition: undefined,\n },\n);\n\nconst slots = defineSlots<{\n [key: string]: unknown;\n}>();\n\nconst rootRef = ref<HTMLElement | undefined>();\nconst input = ref<HTMLInputElement | undefined>();\n\nconst overlayRef = useTemplateRef('overlay');\n\nconst search = ref<string | null>(null);\nconst data = reactive({\n activeIndex: -1,\n open: false,\n});\n\nconst findActiveIndex = () =>\n tap(\n renderedOptionsRef.value.findIndex((o) => deepEqual(o.value, model.value)),\n (v) => (v < 0 ? 0 : v),\n );\n\nconst updateActive = () => (data.activeIndex = findActiveIndex());\n\nconst loadedOptionsRef = ref<ListOption<M>[]>([]);\nconst modelOptionRef = ref<ListOptionNormalized<M> | undefined>(); // list of 1 option that is selected or empty, to keep selected label\n\nconst renderedOptionsRef = computed(() => {\n return normalizeListOptions(loadedOptionsRef.value).map((opt, index) => ({\n ...opt,\n index,\n isSelected: index === selectedIndex.value,\n isActive: index === data.activeIndex,\n })) as (ListOptionBase<M> & { index: number; isSelected: boolean; isActive: boolean })[];\n});\nconst isLoadingOptions = ref<boolean>(true);\nconst isLoadingError = ref<boolean>(false);\n\nconst isDisabled = computed(() => {\n return props.disabled;\n});\n\nconst selectedIndex = computed(() => {\n return loadedOptionsRef.value.findIndex((o) => deepEqual(o.value, model.value));\n});\n\nconst computedError = computed(() => {\n if (isLoadingOptions.value) {\n return undefined;\n }\n\n if (props.error) {\n return getErrorMessage(props.error);\n }\n\n if (isLoadingError.value) {\n return 'Data loading error';\n }\n\n return undefined;\n});\n\nconst textValue = computed(() => {\n const modelOption = unref(modelOptionRef);\n const options = unref(renderedOptionsRef);\n\n const item: ListOptionNormalized | undefined = modelOption ?? options.find((o) => deepEqual(o.value, model.value)) ?? options.find((o) => deepEqual(o.value, model.value));\n\n return item?.label || (model.value ? props.formatValue(model.value) : '');\n});\n\nconst computedPlaceholder = computed(() => {\n if (!data.open && model.value) {\n return '';\n }\n\n return model.value ? String(textValue.value) : props.placeholder;\n});\n\nconst hasValue = computed(() => {\n return model.value !== undefined && model.value !== null;\n});\n\nconst tabindex = computed(() => (isDisabled.value ? undefined : '0'));\n\nconst selectOption = (v: ListOptionBase<M> & { index: number } | undefined) => {\n model.value = v?.value as M;\n modelOptionRef.value = v;\n search.value = null;\n data.open = false;\n rootRef?.value?.focus();\n};\n\nconst clear = () => {\n model.value = undefined as M;\n modelOptionRef.value = undefined;\n};\n\nconst setFocusOnInput = () => input.value?.focus();\n\nconst toggleOpen = () => {\n data.open = !data.open;\n};\n\nwatch(() => data.open, (v) => {\n search.value = v ? '' : null;\n});\n\nconst onInputFocus = () => {\n data.open = true;\n};\n\nconst onFocusOut = (event: FocusEvent) => {\n const relatedTarget = event.relatedTarget as Node | null;\n\n if (!rootRef.value?.contains(relatedTarget) && !overlayRef.value?.listRef?.contains(relatedTarget)) {\n search.value = null;\n data.open = false;\n }\n};\n\nconst handleKeydown = (e: { code: string; preventDefault(): void }) => {\n if (!['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.code)) {\n return;\n } else {\n e.preventDefault();\n }\n\n const { open, activeIndex } = data;\n\n if (!open) {\n if (e.code === 'Enter') {\n data.open = true;\n search.value = '';\n }\n return;\n }\n\n if (e.code === 'Escape') {\n data.open = false;\n search.value = null;\n rootRef.value?.focus();\n }\n\n const options = unref(renderedOptionsRef);\n\n const { length } = options;\n\n if (!length) {\n return;\n }\n\n if (e.code === 'Enter') {\n selectOption(options.find((it) => it.index === activeIndex));\n }\n\n const localIndex = options.findIndex((it) => it.index === activeIndex) ?? -1;\n\n const delta = e.code === 'ArrowDown' ? 1 : e.code === 'ArrowUp' ? -1 : 0;\n\n const newIndex = Math.abs(localIndex + delta + length) % length;\n\n data.activeIndex = renderedOptionsRef.value[newIndex].index ?? -1;\n};\n\nuseLabelNotch(rootRef);\n\nwatch(() => model.value, updateActive, { immediate: true });\n\nwatch(\n () => data.open,\n (open) => (open ? input.value?.focus() : ''),\n);\n\nwatchPostEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n search.value; // to watch\n\n if (data.activeIndex >= 0 && data.open) {\n overlayRef.value?.scrollIntoActive();\n }\n});\n\nconst searchDebounced = refDebounced(search, 300, { maxWait: 1000 });\n\nconst optionsRequest = useWatchFetch(() => searchDebounced.value, async (v) => {\n if (v !== null) { // search is null when dropdown is closed;\n return props.optionsSearch(v, 'label');\n }\n return undefined;\n});\n\nconst modelOptionRequest = useWatchFetch(() => model.value, async (v) => {\n if (v != null && !deepEqual(modelOptionRef.value?.value, v)) { // load label for selected value if it was updated from outside the component\n return (await props.optionsSearch(String(v), 'value'))?.[0];\n }\n return modelOptionRef.value;\n});\n\nwatch(() => optionsRequest.value, (result) => {\n if (result) {\n loadedOptionsRef.value = result;\n if (search.value !== null) {\n isLoadingError.value = false;\n }\n }\n});\n\nwatch(() => modelOptionRequest.value, (result) => {\n if (result) {\n modelOptionRef.value = normalizeListOptions([result])[0];\n }\n});\n\nwatch(() => optionsRequest.error, (err) => {\n if (err) {\n isLoadingError.value = Boolean(err);\n }\n});\n\nwatch(() => optionsRequest.loading || modelOptionRequest.loading, (loading) => {\n isLoadingOptions.value = loading;\n});\n</script>\n\n<template>\n <div class=\"pl-autocomplete__envelope\" @click.stop=\"setFocusOnInput\">\n <div\n ref=\"rootRef\"\n :tabindex=\"tabindex\"\n class=\"pl-autocomplete\"\n :class=\"{ open: data.open, error: Boolean(computedError), disabled: isDisabled }\"\n @keydown=\"handleKeydown\"\n @focusout=\"onFocusOut\"\n >\n <div class=\"pl-autocomplete__container\">\n <div class=\"pl-autocomplete__field\">\n <input\n ref=\"input\"\n v-model=\"search\"\n type=\"text\"\n tabindex=\"-1\"\n :disabled=\"isDisabled\"\n :placeholder=\"computedPlaceholder\"\n spellcheck=\"false\"\n autocomplete=\"chrome-off\"\n @focus=\"onInputFocus\"\n />\n\n <div v-if=\"!data.open\" class=\"input-value\">\n <LongText> {{ textValue }} </LongText>\n </div>\n\n <div class=\"pl-autocomplete__controls\">\n <PlMaskIcon24 v-if=\"isLoadingOptions\" name=\"loading\" />\n <PlIcon16 v-if=\"clearable && hasValue\" class=\"clear\" name=\"delete-clear\" @click.stop=\"clear\" />\n <slot name=\"append\" />\n <div class=\"pl-autocomplete__arrow-wrapper\" @click.stop=\"toggleOpen\">\n <div v-if=\"arrowIconLarge\" class=\"arrow-icon\" :class=\"[`icon-24 ${arrowIconLarge}`]\" />\n <div v-else-if=\"arrowIcon\" class=\"arrow-icon\" :class=\"[`icon-16 ${arrowIcon}`]\" />\n <div v-else class=\"arrow-icon arrow-icon-default\" />\n </div>\n </div>\n </div>\n <label v-if=\"label\">\n <PlSvg v-if=\"required\" :uri=\"SvgRequired\" />\n <span>{{ label }}</span>\n <PlTooltip v-if=\"slots.tooltip\" class=\"info\" position=\"top\">\n <template #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlTooltip>\n </label>\n <DropdownOverlay v-if=\"data.open\" ref=\"overlay\" :root=\"rootRef\" class=\"pl-autocomplete__options\" tabindex=\"-1\" :gap=\"3\">\n <DropdownListItem\n v-for=\"(item, index) in renderedOptionsRef\"\n :key=\"index\"\n :option=\"item\"\n :is-selected=\"item.isSelected\"\n :is-hovered=\"item.isActive\"\n :size=\"optionSize\"\n @click.stop=\"selectOption(item)\"\n />\n <div v-if=\"!renderedOptionsRef.length\" class=\"nothing-found\">Nothing found</div>\n </DropdownOverlay>\n <DoubleContour class=\"pl-autocomplete__contour\" :group-position=\"groupPosition\" />\n </div>\n </div>\n <div v-if=\"computedError\" class=\"pl-autocomplete__error\">{{ computedError }}</div>\n <div v-else-if=\"isLoadingOptions && loadingOptionsHelper\" class=\"pl-autocomplete__helper\">{{ loadingOptionsHelper }}</div>\n <div v-else-if=\"helper\" class=\"pl-autocomplete__helper\">{{ helper }}</div>\n </div>\n</template>\n"],"names":["__default__","model","_useModel","props","__props","slots","_useSlots","rootRef","ref","input","overlayRef","useTemplateRef","search","data","reactive","findActiveIndex","tap","renderedOptionsRef","o","deepEqual","v","updateActive","loadedOptionsRef","modelOptionRef","computed","normalizeListOptions","opt","index","selectedIndex","isLoadingOptions","isLoadingError","isDisabled","computedError","getErrorMessage","textValue","modelOption","unref","options","item","computedPlaceholder","hasValue","tabindex","selectOption","_a","clear","setFocusOnInput","toggleOpen","watch","onInputFocus","onFocusOut","event","relatedTarget","_c","_b","handleKeydown","open","activeIndex","length","it","localIndex","delta","newIndex","useLabelNotch","watchPostEffect","searchDebounced","refDebounced","optionsRequest","useWatchFetch","modelOptionRequest","result","err","loading","_createElementBlock","_createElementVNode","_normalizeClass","_hoisted_2","_hoisted_3","$event","_openBlock","_hoisted_5","_createVNode","LongText","_hoisted_6","_createBlock","_unref","PlMaskIcon24","PlIcon16","_renderSlot","_ctx","_hoisted_7","_hoisted_8","PlSvg","SvgRequired","PlTooltip","DropdownOverlay","_Fragment","_renderList","DropdownListItem","_withModifiers","_hoisted_9","DoubleContour","_hoisted_10","_toDisplayString","_hoisted_11","_hoisted_12"],"mappings":"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"}
|
|
@@ -14,12 +14,12 @@ import _e from "../DropdownListItem.vue.js";
|
|
|
14
14
|
import j from "../PlChip/PlChip.vue.js";
|
|
15
15
|
import ye from "../PlIcon24/PlIcon24.vue.js";
|
|
16
16
|
import ge from "../PlTooltip/PlTooltip.vue.js";
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we = { class: "pl-autocomplete-multi__field" }, Re = ["disabled", "placeholder"],
|
|
17
|
+
import be from "../../assets/images/required.svg.js";
|
|
18
|
+
import ke from "../PlSvg/PlSvg.vue.js";
|
|
19
|
+
const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we = { class: "pl-autocomplete-multi__field" }, Re = ["disabled", "placeholder"], xe = {
|
|
20
20
|
key: 0,
|
|
21
21
|
class: "chips-container"
|
|
22
|
-
},
|
|
22
|
+
}, Ce = { class: "pl-autocomplete-multi__controls" }, Se = { key: 0 }, $e = { class: "pl-autocomplete-multi__open-chips-container" }, Ae = {
|
|
23
23
|
key: 0,
|
|
24
24
|
class: "nothing-found"
|
|
25
25
|
}, Ee = {
|
|
@@ -35,7 +35,6 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
35
35
|
props: {
|
|
36
36
|
modelValue: { default: () => [] },
|
|
37
37
|
optionsSearch: {},
|
|
38
|
-
modelSearch: {},
|
|
39
38
|
sourceId: { default: void 0 },
|
|
40
39
|
label: { default: void 0 },
|
|
41
40
|
helper: { default: void 0 },
|
|
@@ -50,7 +49,7 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
50
49
|
},
|
|
51
50
|
emits: ["update:modelValue"],
|
|
52
51
|
setup(p, { emit: G }) {
|
|
53
|
-
const J = G, M = (e) => J("update:modelValue", e), Q = re(), n = p,
|
|
52
|
+
const J = G, M = (e) => J("update:modelValue", e), Q = re(), n = p, b = z(), k = z(), w = se("overlay"), l = ae({
|
|
54
53
|
search: "",
|
|
55
54
|
activeOption: -1,
|
|
56
55
|
open: !1,
|
|
@@ -59,10 +58,10 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
59
58
|
L(() => l.open, (e) => {
|
|
60
59
|
e || (l.search = "");
|
|
61
60
|
}, { flush: "sync" });
|
|
62
|
-
const O = u(() => Array.isArray(n.modelValue) ? n.modelValue : []), X = u(() => l.open && n.modelValue.length > 0 ? n.placeholder : n.modelValue.length > 0 ? "" : n.placeholder), D = ie(n, "debounce"), h = W(() => [l.search, l.open, n.sourceId], async ([e, t]) => n.optionsSearch(e), {
|
|
61
|
+
const O = u(() => Array.isArray(n.modelValue) ? n.modelValue : []), X = u(() => l.open && n.modelValue.length > 0 ? n.placeholder : n.modelValue.length > 0 ? "" : n.placeholder), D = ie(n, "debounce"), h = W(() => [l.search, l.open, n.sourceId], async ([e, t]) => n.optionsSearch(e, "label"), {
|
|
63
62
|
filterWatchResult: ([e, t]) => t,
|
|
64
63
|
debounce: D
|
|
65
|
-
}), _ = W(() => [n.modelValue, n.sourceId], async ([e]) => n.
|
|
64
|
+
}), _ = W(() => [n.modelValue, n.sourceId], async ([e]) => n.optionsSearch(e, "value"), {
|
|
66
65
|
debounce: D
|
|
67
66
|
}), Y = u(() => {
|
|
68
67
|
const e = _.value ?? [], t = h.value ?? [], o = /* @__PURE__ */ new Set(), r = [], a = (A) => {
|
|
@@ -74,25 +73,25 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
74
73
|
return a(e), a(t), r;
|
|
75
74
|
}), R = u(() => O.value.map((e) => Y.value.find((t) => I(t.value, e))).filter(
|
|
76
75
|
(e) => e !== void 0
|
|
77
|
-
)),
|
|
76
|
+
)), x = u(() => {
|
|
78
77
|
const e = i(O);
|
|
79
78
|
return [...h.value ?? []].map((o) => ({
|
|
80
79
|
...o,
|
|
81
80
|
selected: H(e, o.value)
|
|
82
81
|
}));
|
|
83
|
-
}),
|
|
82
|
+
}), C = u(() => h.loading || _.loading), S = u(() => _.value === void 0 ? !0 : n.disabled), Z = u(() => S.value ? void 0 : "0"), ee = () => {
|
|
84
83
|
l.activeOption = 0;
|
|
85
84
|
}, N = (e) => {
|
|
86
85
|
var o;
|
|
87
86
|
const t = i(O);
|
|
88
|
-
M(H(t, e) ? t.filter((r) => !I(r, e)) : [...t, e]), n.resetSearchOnSelect && (l.search = ""), (o =
|
|
87
|
+
M(H(t, e) ? t.filter((r) => !I(r, e)) : [...t, e]), n.resetSearchOnSelect && (l.search = ""), (o = k.value) == null || o.focus();
|
|
89
88
|
}, q = (e) => M(i(O).filter((t) => !I(t, e))), oe = () => {
|
|
90
89
|
var e;
|
|
91
|
-
return (e =
|
|
90
|
+
return (e = k.value) == null ? void 0 : e.focus();
|
|
92
91
|
}, te = () => l.open = !l.open, P = (e) => {
|
|
93
92
|
var o, r, a;
|
|
94
93
|
const t = e.relatedTarget;
|
|
95
|
-
!((o =
|
|
94
|
+
!((o = b.value) != null && o.contains(t)) && !((a = (r = w.value) == null ? void 0 : r.listRef) != null && a.contains(t)) && (l.open = !1);
|
|
96
95
|
}, le = (e) => {
|
|
97
96
|
var y;
|
|
98
97
|
const { open: t, activeOption: o } = l;
|
|
@@ -100,8 +99,8 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
100
99
|
e.code === "Enter" && (l.open = !0);
|
|
101
100
|
return;
|
|
102
101
|
}
|
|
103
|
-
e.code === "Escape" && (l.open = !1, (y =
|
|
104
|
-
const r = i(
|
|
102
|
+
e.code === "Escape" && (l.open = !1, (y = k.value) == null || y.focus());
|
|
103
|
+
const r = i(x), { length: a } = r;
|
|
105
104
|
if (!a)
|
|
106
105
|
return;
|
|
107
106
|
["ArrowDown", "ArrowUp", "Enter"].includes(e.code) && e.preventDefault(), e.code === "Enter" && N(r[o].value);
|
|
@@ -111,13 +110,13 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
111
110
|
return (g = w.value) == null ? void 0 : g.scrollIntoActive();
|
|
112
111
|
});
|
|
113
112
|
};
|
|
114
|
-
he(
|
|
113
|
+
he(b), L(
|
|
115
114
|
() => n.modelValue,
|
|
116
115
|
() => ee(),
|
|
117
116
|
{ immediate: !0 }
|
|
118
117
|
);
|
|
119
118
|
const $ = u(() => {
|
|
120
|
-
if (!
|
|
119
|
+
if (!C.value) {
|
|
121
120
|
if (h.error)
|
|
122
121
|
return B(h.error);
|
|
123
122
|
if (_.error)
|
|
@@ -134,9 +133,9 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
134
133
|
}, [
|
|
135
134
|
d("div", {
|
|
136
135
|
ref_key: "rootRef",
|
|
137
|
-
ref:
|
|
136
|
+
ref: b,
|
|
138
137
|
tabindex: Z.value,
|
|
139
|
-
class: ue(["pl-autocomplete-multi", { open: l.open, error: !!$.value, disabled:
|
|
138
|
+
class: ue(["pl-autocomplete-multi", { open: l.open, error: !!$.value, disabled: S.value }]),
|
|
140
139
|
onKeydown: le,
|
|
141
140
|
onFocusout: P
|
|
142
141
|
}, [
|
|
@@ -144,11 +143,11 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
144
143
|
d("div", we, [
|
|
145
144
|
de(d("input", {
|
|
146
145
|
ref_key: "inputRef",
|
|
147
|
-
ref:
|
|
146
|
+
ref: k,
|
|
148
147
|
"onUpdate:modelValue": t[0] || (t[0] = (o) => l.search = o),
|
|
149
148
|
type: "text",
|
|
150
149
|
tabindex: "-1",
|
|
151
|
-
disabled:
|
|
150
|
+
disabled: S.value,
|
|
152
151
|
placeholder: X.value,
|
|
153
152
|
spellcheck: "false",
|
|
154
153
|
autocomplete: "chrome-off",
|
|
@@ -156,7 +155,7 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
156
155
|
}, null, 40, Re), [
|
|
157
156
|
[pe, l.search]
|
|
158
157
|
]),
|
|
159
|
-
l.open ? f("", !0) : (s(), c("div",
|
|
158
|
+
l.open ? f("", !0) : (s(), c("div", xe, [
|
|
160
159
|
(s(!0), c(E, null, F(R.value, (o, r) => (s(), m(i(j), {
|
|
161
160
|
key: r,
|
|
162
161
|
closeable: "",
|
|
@@ -170,8 +169,8 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
170
169
|
_: 2
|
|
171
170
|
}, 1032, ["onClose"]))), 128))
|
|
172
171
|
])),
|
|
173
|
-
d("div",
|
|
174
|
-
|
|
172
|
+
d("div", Ce, [
|
|
173
|
+
C.value ? (s(), m(i(ye), {
|
|
175
174
|
key: 0,
|
|
176
175
|
name: "loading"
|
|
177
176
|
})) : f("", !0),
|
|
@@ -184,10 +183,10 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
184
183
|
])])
|
|
185
184
|
])
|
|
186
185
|
]),
|
|
187
|
-
p.label ? (s(), c("label",
|
|
188
|
-
p.required ? (s(), m(i(
|
|
186
|
+
p.label ? (s(), c("label", Se, [
|
|
187
|
+
p.required ? (s(), m(i(ke), {
|
|
189
188
|
key: 0,
|
|
190
|
-
uri: i(
|
|
189
|
+
uri: i(be)
|
|
191
190
|
}, null, 8, ["uri"])) : f("", !0),
|
|
192
191
|
d("span", null, v(p.label), 1),
|
|
193
192
|
i(Q).tooltip ? (s(), m(i(ge), {
|
|
@@ -205,7 +204,7 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
205
204
|
key: 1,
|
|
206
205
|
ref_key: "overlay",
|
|
207
206
|
ref: w,
|
|
208
|
-
root:
|
|
207
|
+
root: b.value,
|
|
209
208
|
class: "pl-autocomplete-multi__options",
|
|
210
209
|
gap: 5,
|
|
211
210
|
tabindex: "-1",
|
|
@@ -225,7 +224,7 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
225
224
|
_: 2
|
|
226
225
|
}, 1032, ["onClose"]))), 128))
|
|
227
226
|
]),
|
|
228
|
-
(s(!0), c(E, null, F(
|
|
227
|
+
(s(!0), c(E, null, F(x.value, (o, r) => (s(), m(_e, {
|
|
229
228
|
key: r,
|
|
230
229
|
option: o,
|
|
231
230
|
"text-item": "text",
|
|
@@ -235,7 +234,7 @@ const Oe = ["tabindex"], Ve = { class: "pl-autocomplete-multi__container" }, we
|
|
|
235
234
|
"use-checkbox": "",
|
|
236
235
|
onClick: T((a) => N(o.value), ["stop"])
|
|
237
236
|
}, null, 8, ["option", "is-selected", "is-hovered", "onClick"]))), 128)),
|
|
238
|
-
!
|
|
237
|
+
!x.value.length && !C.value ? (s(), c("div", Ae, v(p.emptyOptionsText), 1)) : f("", !0)
|
|
239
238
|
]),
|
|
240
239
|
_: 1
|
|
241
240
|
}, 8, ["root"])) : f("", !0),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlAutocompleteMulti.vue.js","sources":["../../../src/components/PlAutocompleteMulti/PlAutocompleteMulti.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * A multi-select autocomplete component that allows users to search and select multiple values from a list of options.\n * Supports async data fetching, keyboard navigation, and displays selected items as removable chips.\n *\n * @example\n * Basic usage:\n * <PlAutocompleteMulti\n * v-model=\"selectedUsers\"\n * :options-search=\"searchUsers\"\n * :model-search=\"getUsersByIds\"\n * label=\"Select Users\"\n * placeholder=\"Search for users...\"\n * required\n * :debounce=\"300\"\n * helper=\"Choose one or more users from the list\"\n * />\n *\n * With async functions:\n * const selectedUsers = ref([])\n *\n * const searchUsers = async (searchTerm) => {\n * const response = await fetch('/api/users/search?q=' + searchTerm)\n * const users = await response.json()\n * return users.map(user => ({ value: user.id, label: user.name }))\n * }\n *\n * const getUsersByIds = async (userIds) => {\n * if (!userIds.length) return []\n * const response = await fetch('/api/users?ids=' + userIds.join(','))\n * const users = await response.json()\n * return users.map(user => ({ value: user.id, label: user.name }))\n * }\n */\nexport default {\n name: 'PlAutocompleteMulti',\n};\n</script>\n\n<script lang=\"ts\" setup generic=\"M extends string | number = string\">\nimport './pl-autocomplete-multi.scss';\n\nimport type { ListOptionBase } from '@platforma-sdk/model';\nimport canonicalize from 'canonicalize';\nimport { computed, reactive, ref, toRef, unref, useSlots, useTemplateRef, watch } from 'vue';\nimport { useWatchFetch } from '../../composition/useWatchFetch.ts';\nimport { getErrorMessage } from '../../helpers/error.ts';\nimport { deepEqual, deepIncludes } from '../../helpers/objects';\nimport DoubleContour from '../../utils/DoubleContour.vue';\nimport DropdownOverlay from '../../utils/DropdownOverlay/DropdownOverlay.vue';\nimport { useLabelNotch } from '../../utils/useLabelNotch';\nimport DropdownListItem from '../DropdownListItem.vue';\nimport { PlChip } from '../PlChip';\nimport { PlMaskIcon24 } from '../PlMaskIcon24';\nimport { PlTooltip } from '../PlTooltip';\n\nimport SvgRequired from '../../assets/images/required.svg?raw';\nimport { PlSvg } from '../PlSvg';\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', v: M[]): void;\n}>();\n\nconst emitModel = (v: M[]) => emit('update:modelValue', v);\n\nconst slots = useSlots();\n\nconst props = withDefaults(\n defineProps<{\n /**\n * The current selected values.\n */\n modelValue: M[];\n /**\n * Lambda for requesting of available options for the dropdown by search string.\n */\n optionsSearch: (s: string) => Promise<Readonly<ListOptionBase<M>[]>>;\n /**\n * Lambda for requesting options that correspond to the current model values.\n */\n modelSearch: (values: M[]) => Promise<Readonly<ListOptionBase<M>[]>>;\n /**\n * Unique identifier for the source of the options, changing it will invalidate the options cache.\n */\n sourceId?: string;\n /**\n * The label text for the dropdown field (optional)\n */\n label?: string;\n /**\n * A helper text displayed below the dropdown when there are no errors (optional).\n */\n helper?: string;\n /**\n * Error message displayed below the dropdown (optional)\n */\n error?: unknown;\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n /**\n * If `true`, the dropdown component is marked as required.\n */\n required?: boolean;\n /**\n * If `true`, the dropdown component is disabled and cannot be interacted with.\n */\n disabled?: boolean;\n /**\n * Debounce time in ms for the options search.\n */\n debounce?: number;\n /**\n * If `true`, the search input is reset and focus is set on it when the new option is selected.\n */\n resetSearchOnSelect?: boolean;\n /**\n * The text to display when no options are found.\n */\n emptyOptionsText?: string;\n /**\n * Makes some of corners not rounded\n * */\n groupPosition?: 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'middle';\n }>(),\n {\n modelValue: () => [],\n label: undefined,\n helper: undefined,\n error: undefined,\n placeholder: '...',\n required: false,\n disabled: false,\n debounce: 300,\n emptyOptionsText: 'Nothing found',\n sourceId: undefined,\n groupPosition: undefined,\n },\n);\n\nconst rootRef = ref<HTMLElement | undefined>();\nconst inputRef = ref<HTMLInputElement | undefined>();\n\nconst overlay = useTemplateRef('overlay');\n\nconst data = reactive({\n search: '',\n activeOption: -1,\n open: false,\n optionsHeight: 0,\n});\n\nwatch(() => data.open, (v) => {\n if (!v) {\n data.search = '';\n }\n}, { flush: 'sync' });\n\nconst selectedValuesRef = computed(() => (Array.isArray(props.modelValue) ? props.modelValue : []));\n\nconst placeholderRef = computed(() => {\n if (data.open && props.modelValue.length > 0) {\n return props.placeholder;\n }\n\n return props.modelValue.length > 0 ? '' : props.placeholder;\n});\n\nconst debounce = toRef(props, 'debounce');\n\nconst searchOptionsRef = useWatchFetch(() => [data.search, data.open, props.sourceId] as const, async ([search, _open]) => {\n return props.optionsSearch(search);\n}, {\n filterWatchResult: ([_search, open]) => open,\n debounce,\n});\n\nconst modelOptionsRef = useWatchFetch(() => [props.modelValue, props.sourceId] as const, async ([v]) => {\n return props.modelSearch(v);\n}, {\n debounce,\n});\n\nconst allOptionsRef = computed(() => {\n const modelOptions = modelOptionsRef.value ?? [];\n const searchOptions = searchOptionsRef.value ?? [];\n\n const seenValues = new Set<string | undefined>();\n const result = [] as ListOptionBase<M>[];\n\n const addOptions = (options: Readonly<ListOptionBase<M>[]>) => {\n for (const option of options) {\n const canonicalValue = canonicalize(option.value);\n if (!seenValues.has(canonicalValue)) {\n seenValues.add(canonicalValue);\n result.push(option);\n }\n }\n };\n\n addOptions(modelOptions);\n addOptions(searchOptions);\n\n return result;\n});\n\nconst selectedOptionsRef = computed(() => {\n return selectedValuesRef.value.map((v) =>\n allOptionsRef.value.find((opt) => deepEqual(opt.value, v))).filter((v) => v !== undefined,\n );\n});\n\nconst filteredOptionsRef = computed(() => {\n const selectedValues = unref(selectedValuesRef);\n\n const options = searchOptionsRef.value ?? [];\n\n return [...options].map((opt) => ({\n ...opt,\n selected: deepIncludes(selectedValues, opt.value),\n }));\n});\n\nconst isOptionsLoading = computed(() => searchOptionsRef.loading || modelOptionsRef.loading);\n\nconst isDisabled = computed(() => {\n if (modelOptionsRef.value === undefined) {\n return true;\n }\n\n return props.disabled;\n});\n\nconst tabindex = computed(() => (isDisabled.value ? undefined : '0'));\n\nconst updateActiveOption = () => {\n data.activeOption = 0;\n};\n\nconst selectOption = (v: M) => {\n const values = unref(selectedValuesRef);\n emitModel(deepIncludes(values, v) ? values.filter((it) => !deepEqual(it, v)) : [...values, v]);\n if (props.resetSearchOnSelect) {\n data.search = '';\n }\n inputRef.value?.focus();\n};\n\nconst unselectOption = (d: M) => emitModel(unref(selectedValuesRef).filter((v) => !deepEqual(v, d)));\n\nconst setFocusOnInput = () => inputRef.value?.focus();\n\nconst toggleOpen = () => data.open = !data.open;\n\nconst onFocusOut = (event: FocusEvent) => {\n const relatedTarget = event.relatedTarget as Node | null;\n\n if (!rootRef.value?.contains(relatedTarget) && !overlay.value?.listRef?.contains(relatedTarget)) {\n data.open = false;\n }\n};\n\nconst handleKeydown = (e: { code: string; preventDefault(): void }) => {\n const { open, activeOption } = data;\n\n if (!open) {\n if (e.code === 'Enter') {\n data.open = true;\n }\n return;\n }\n\n if (e.code === 'Escape') {\n data.open = false;\n inputRef.value?.focus();\n }\n\n const filteredOptions = unref(filteredOptionsRef);\n\n const { length } = filteredOptions;\n\n if (!length) {\n return;\n }\n\n if (['ArrowDown', 'ArrowUp', 'Enter'].includes(e.code)) {\n e.preventDefault();\n }\n\n if (e.code === 'Enter') {\n selectOption(filteredOptions[activeOption].value);\n }\n\n const d = e.code === 'ArrowDown' ? 1 : e.code === 'ArrowUp' ? -1 : 0;\n\n data.activeOption = Math.abs(activeOption + d + length) % length;\n\n requestAnimationFrame(() => overlay.value?.scrollIntoActive());\n};\n\nuseLabelNotch(rootRef);\n\nwatch(\n () => props.modelValue,\n () => updateActiveOption(),\n { immediate: true },\n);\n\nconst computedError = computed(() => {\n if (isOptionsLoading.value) {\n return undefined;\n }\n\n if (searchOptionsRef.error) {\n return getErrorMessage(searchOptionsRef.error);\n }\n\n if (modelOptionsRef.error) {\n return getErrorMessage(modelOptionsRef.error);\n }\n\n if (props.error) {\n return getErrorMessage(props.error);\n }\n\n if (props.modelValue.length && selectedOptionsRef.value.length !== props.modelValue.length) {\n return 'The selected values are not one of the options';\n }\n\n return undefined;\n});\n</script>\n\n<template>\n <div class=\"pl-autocomplete-multi__envelope\" @click=\"setFocusOnInput\">\n <div\n ref=\"rootRef\"\n :tabindex=\"tabindex\"\n class=\"pl-autocomplete-multi\"\n :class=\"{ open: data.open, error: Boolean(computedError), disabled: isDisabled }\"\n @keydown=\"handleKeydown\"\n @focusout=\"onFocusOut\"\n >\n <div class=\"pl-autocomplete-multi__container\">\n <div class=\"pl-autocomplete-multi__field\">\n <input\n ref=\"inputRef\"\n v-model=\"data.search\"\n type=\"text\"\n tabindex=\"-1\"\n :disabled=\"isDisabled\"\n :placeholder=\"placeholderRef\"\n spellcheck=\"false\"\n autocomplete=\"chrome-off\"\n @focus=\"data.open = true\"\n />\n <div v-if=\"!data.open\" class=\"chips-container\">\n <PlChip v-for=\"(opt, i) in selectedOptionsRef\" :key=\"i\" closeable small @click.stop=\"data.open = true\" @close=\"unselectOption(opt.value)\">\n {{ opt.label || opt.value }}\n </PlChip>\n </div>\n\n <div class=\"pl-autocomplete-multi__controls\">\n <PlMaskIcon24 v-if=\"isOptionsLoading\" name=\"loading\" />\n <slot name=\"append\" />\n <div class=\"pl-autocomplete-multi__arrow-wrapper\" @click.stop=\"toggleOpen\">\n <div class=\"arrow-icon arrow-icon-default\" />\n </div>\n </div>\n </div>\n <label v-if=\"label\">\n <PlSvg v-if=\"required\" :uri=\"SvgRequired\" />\n <span>{{ label }}</span>\n <PlTooltip v-if=\"slots.tooltip\" class=\"info\" position=\"top\">\n <template #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlTooltip>\n </label>\n <DropdownOverlay\n v-if=\"data.open\"\n ref=\"overlay\"\n :root=\"rootRef\"\n class=\"pl-autocomplete-multi__options\"\n :gap=\"5\"\n tabindex=\"-1\"\n @focusout=\"onFocusOut\"\n >\n <div class=\"pl-autocomplete-multi__open-chips-container\">\n <PlChip\n v-for=\"(opt, i) in selectedOptionsRef\"\n :key=\"i\"\n closeable\n small\n @close=\"unselectOption(opt.value)\"\n >\n {{ opt.label || opt.value }}\n </PlChip>\n </div>\n <DropdownListItem\n v-for=\"(item, index) in filteredOptionsRef\"\n :key=\"index\"\n :option=\"item\"\n :text-item=\"'text'\"\n :is-selected=\"item.selected\"\n :is-hovered=\"data.activeOption == index\"\n size=\"medium\"\n use-checkbox\n @click.stop=\"selectOption(item.value)\"\n />\n <div v-if=\"!filteredOptionsRef.length && !isOptionsLoading\" class=\"nothing-found\">{{ emptyOptionsText }}</div>\n </DropdownOverlay>\n <DoubleContour class=\"pl-autocomplete-multi__contour\" :group-position=\"groupPosition\" />\n </div>\n </div>\n <div v-if=\"computedError\" class=\"pl-autocomplete-multi__error\">{{ computedError }}</div>\n <div v-else-if=\"helper\" class=\"pl-autocomplete-multi__helper\">{{ helper }}</div>\n </div>\n</template>\n"],"names":["__default__","emit","__emit","emitModel","v","slots","useSlots","props","__props","rootRef","ref","inputRef","overlay","useTemplateRef","data","reactive","watch","selectedValuesRef","computed","placeholderRef","debounce","toRef","searchOptionsRef","useWatchFetch","search","_open","_search","open","modelOptionsRef","allOptionsRef","modelOptions","searchOptions","seenValues","result","addOptions","options","option","canonicalValue","canonicalize","selectedOptionsRef","opt","deepEqual","filteredOptionsRef","selectedValues","unref","deepIncludes","isOptionsLoading","isDisabled","tabindex","updateActiveOption","selectOption","values","it","_a","unselectOption","d","setFocusOnInput","toggleOpen","onFocusOut","event","relatedTarget","_c","_b","handleKeydown","activeOption","filteredOptions","length","useLabelNotch","computedError","getErrorMessage","_createElementBlock","_createElementVNode","_normalizeClass","_hoisted_2","_hoisted_3","_cache","$event","_vModelText","_openBlock","_hoisted_5","_Fragment","_renderList","i","_createBlock","_unref","PlChip","_withModifiers","_createTextVNode","_toDisplayString","_hoisted_6","PlMaskIcon24","_renderSlot","_ctx","_hoisted_7","PlSvg","SvgRequired","PlTooltip","DropdownOverlay","_hoisted_8","item","index","DropdownListItem","_hoisted_9","_createVNode","DoubleContour","_hoisted_10","_hoisted_11"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PlAutocompleteMulti.vue.js","sources":["../../../src/components/PlAutocompleteMulti/PlAutocompleteMulti.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * A multi-select autocomplete component that allows users to search and select multiple values from a list of options.\n * Supports async data fetching, keyboard navigation, and displays selected items as removable chips.\n *\n * @example\n * Basic usage:\n * <PlAutocompleteMulti\n * v-model=\"selectedUsers\"\n * :options-search=\"searchUsers\"\n * :model-search=\"getUsersByIds\"\n * label=\"Select Users\"\n * placeholder=\"Search for users...\"\n * required\n * :debounce=\"300\"\n * helper=\"Choose one or more users from the list\"\n * />\n *\n * With async functions:\n * const selectedUsers = ref([])\n *\n * const searchUsers = async (searchTerm) => {\n * const response = await fetch('/api/users/search?q=' + searchTerm)\n * const users = await response.json()\n * return users.map(user => ({ value: user.id, label: user.name }))\n * }\n *\n * const getUsersByIds = async (userIds) => {\n * if (!userIds.length) return []\n * const response = await fetch('/api/users?ids=' + userIds.join(','))\n * const users = await response.json()\n * return users.map(user => ({ value: user.id, label: user.name }))\n * }\n */\nexport default {\n name: 'PlAutocompleteMulti',\n};\n</script>\n\n<script lang=\"ts\" setup generic=\"M extends string | number = string\">\nimport './pl-autocomplete-multi.scss';\n\nimport type { ListOptionBase } from '@platforma-sdk/model';\nimport canonicalize from 'canonicalize';\nimport { computed, reactive, ref, toRef, unref, useSlots, useTemplateRef, watch } from 'vue';\nimport { useWatchFetch } from '../../composition/useWatchFetch.ts';\nimport { getErrorMessage } from '../../helpers/error.ts';\nimport { deepEqual, deepIncludes } from '../../helpers/objects';\nimport DoubleContour from '../../utils/DoubleContour.vue';\nimport DropdownOverlay from '../../utils/DropdownOverlay/DropdownOverlay.vue';\nimport { useLabelNotch } from '../../utils/useLabelNotch';\nimport DropdownListItem from '../DropdownListItem.vue';\nimport { PlChip } from '../PlChip';\nimport { PlMaskIcon24 } from '../PlMaskIcon24';\nimport { PlTooltip } from '../PlTooltip';\n\nimport SvgRequired from '../../assets/images/required.svg?raw';\nimport { PlSvg } from '../PlSvg';\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', v: M[]): void;\n}>();\n\nconst emitModel = (v: M[]) => emit('update:modelValue', v);\n\nconst slots = useSlots();\n\ninterface OptionsSearch {\n (s: string, type: 'label'): Promise<Readonly<ListOptionBase<M>[]>>;\n (s: M[], type: 'value'): Promise<Readonly<ListOptionBase<M>[]>>;\n}\n\nconst props = withDefaults(\n defineProps<{\n /**\n * The current selected values.\n */\n modelValue: M[];\n /**\n * Lambda for requesting of available options for the dropdown by search string.\n */\n optionsSearch: OptionsSearch;\n /**\n * Unique identifier for the source of the options, changing it will invalidate the options cache.\n */\n sourceId?: string;\n /**\n * The label text for the dropdown field (optional)\n */\n label?: string;\n /**\n * A helper text displayed below the dropdown when there are no errors (optional).\n */\n helper?: string;\n /**\n * Error message displayed below the dropdown (optional)\n */\n error?: unknown;\n /**\n * Placeholder text shown when no value is selected.\n */\n placeholder?: string;\n /**\n * If `true`, the dropdown component is marked as required.\n */\n required?: boolean;\n /**\n * If `true`, the dropdown component is disabled and cannot be interacted with.\n */\n disabled?: boolean;\n /**\n * Debounce time in ms for the options search.\n */\n debounce?: number;\n /**\n * If `true`, the search input is reset and focus is set on it when the new option is selected.\n */\n resetSearchOnSelect?: boolean;\n /**\n * The text to display when no options are found.\n */\n emptyOptionsText?: string;\n /**\n * Makes some of corners not rounded\n * */\n groupPosition?: 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'middle';\n }>(),\n {\n modelValue: () => [],\n label: undefined,\n helper: undefined,\n error: undefined,\n placeholder: '...',\n required: false,\n disabled: false,\n debounce: 300,\n emptyOptionsText: 'Nothing found',\n sourceId: undefined,\n groupPosition: undefined,\n },\n);\n\nconst rootRef = ref<HTMLElement | undefined>();\nconst inputRef = ref<HTMLInputElement | undefined>();\n\nconst overlay = useTemplateRef('overlay');\n\nconst data = reactive({\n search: '',\n activeOption: -1,\n open: false,\n optionsHeight: 0,\n});\n\nwatch(() => data.open, (v) => {\n if (!v) {\n data.search = '';\n }\n}, { flush: 'sync' });\n\nconst selectedValuesRef = computed(() => (Array.isArray(props.modelValue) ? props.modelValue : []));\n\nconst placeholderRef = computed(() => {\n if (data.open && props.modelValue.length > 0) {\n return props.placeholder;\n }\n\n return props.modelValue.length > 0 ? '' : props.placeholder;\n});\n\nconst debounce = toRef(props, 'debounce');\n\nconst searchOptionsRef = useWatchFetch(() => [data.search, data.open, props.sourceId] as const, async ([search, _open]) => {\n return props.optionsSearch(search, 'label');\n}, {\n filterWatchResult: ([_search, open]) => open,\n debounce,\n});\n\nconst modelOptionsRef = useWatchFetch(() => [props.modelValue, props.sourceId] as const, async ([v]) => {\n return props.optionsSearch(v, 'value');\n}, {\n debounce,\n});\n\nconst allOptionsRef = computed(() => {\n const modelOptions = modelOptionsRef.value ?? [];\n const searchOptions = searchOptionsRef.value ?? [];\n\n const seenValues = new Set<string | undefined>();\n const result = [] as ListOptionBase<M>[];\n\n const addOptions = (options: Readonly<ListOptionBase<M>[]>) => {\n for (const option of options) {\n const canonicalValue = canonicalize(option.value);\n if (!seenValues.has(canonicalValue)) {\n seenValues.add(canonicalValue);\n result.push(option);\n }\n }\n };\n\n addOptions(modelOptions);\n addOptions(searchOptions);\n\n return result;\n});\n\nconst selectedOptionsRef = computed(() => {\n return selectedValuesRef.value.map((v) =>\n allOptionsRef.value.find((opt) => deepEqual(opt.value, v))).filter((v) => v !== undefined,\n );\n});\n\nconst filteredOptionsRef = computed(() => {\n const selectedValues = unref(selectedValuesRef);\n\n const options = searchOptionsRef.value ?? [];\n\n return [...options].map((opt) => ({\n ...opt,\n selected: deepIncludes(selectedValues, opt.value),\n }));\n});\n\nconst isOptionsLoading = computed(() => searchOptionsRef.loading || modelOptionsRef.loading);\n\nconst isDisabled = computed(() => {\n if (modelOptionsRef.value === undefined) {\n return true;\n }\n\n return props.disabled;\n});\n\nconst tabindex = computed(() => (isDisabled.value ? undefined : '0'));\n\nconst updateActiveOption = () => {\n data.activeOption = 0;\n};\n\nconst selectOption = (v: M) => {\n const values = unref(selectedValuesRef);\n emitModel(deepIncludes(values, v) ? values.filter((it) => !deepEqual(it, v)) : [...values, v]);\n if (props.resetSearchOnSelect) {\n data.search = '';\n }\n inputRef.value?.focus();\n};\n\nconst unselectOption = (d: M) => emitModel(unref(selectedValuesRef).filter((v) => !deepEqual(v, d)));\n\nconst setFocusOnInput = () => inputRef.value?.focus();\n\nconst toggleOpen = () => data.open = !data.open;\n\nconst onFocusOut = (event: FocusEvent) => {\n const relatedTarget = event.relatedTarget as Node | null;\n\n if (!rootRef.value?.contains(relatedTarget) && !overlay.value?.listRef?.contains(relatedTarget)) {\n data.open = false;\n }\n};\n\nconst handleKeydown = (e: { code: string; preventDefault(): void }) => {\n const { open, activeOption } = data;\n\n if (!open) {\n if (e.code === 'Enter') {\n data.open = true;\n }\n return;\n }\n\n if (e.code === 'Escape') {\n data.open = false;\n inputRef.value?.focus();\n }\n\n const filteredOptions = unref(filteredOptionsRef);\n\n const { length } = filteredOptions;\n\n if (!length) {\n return;\n }\n\n if (['ArrowDown', 'ArrowUp', 'Enter'].includes(e.code)) {\n e.preventDefault();\n }\n\n if (e.code === 'Enter') {\n selectOption(filteredOptions[activeOption].value);\n }\n\n const d = e.code === 'ArrowDown' ? 1 : e.code === 'ArrowUp' ? -1 : 0;\n\n data.activeOption = Math.abs(activeOption + d + length) % length;\n\n requestAnimationFrame(() => overlay.value?.scrollIntoActive());\n};\n\nuseLabelNotch(rootRef);\n\nwatch(\n () => props.modelValue,\n () => updateActiveOption(),\n { immediate: true },\n);\n\nconst computedError = computed(() => {\n if (isOptionsLoading.value) {\n return undefined;\n }\n\n if (searchOptionsRef.error) {\n return getErrorMessage(searchOptionsRef.error);\n }\n\n if (modelOptionsRef.error) {\n return getErrorMessage(modelOptionsRef.error);\n }\n\n if (props.error) {\n return getErrorMessage(props.error);\n }\n\n if (props.modelValue.length && selectedOptionsRef.value.length !== props.modelValue.length) {\n return 'The selected values are not one of the options';\n }\n\n return undefined;\n});\n</script>\n\n<template>\n <div class=\"pl-autocomplete-multi__envelope\" @click=\"setFocusOnInput\">\n <div\n ref=\"rootRef\"\n :tabindex=\"tabindex\"\n class=\"pl-autocomplete-multi\"\n :class=\"{ open: data.open, error: Boolean(computedError), disabled: isDisabled }\"\n @keydown=\"handleKeydown\"\n @focusout=\"onFocusOut\"\n >\n <div class=\"pl-autocomplete-multi__container\">\n <div class=\"pl-autocomplete-multi__field\">\n <input\n ref=\"inputRef\"\n v-model=\"data.search\"\n type=\"text\"\n tabindex=\"-1\"\n :disabled=\"isDisabled\"\n :placeholder=\"placeholderRef\"\n spellcheck=\"false\"\n autocomplete=\"chrome-off\"\n @focus=\"data.open = true\"\n />\n <div v-if=\"!data.open\" class=\"chips-container\">\n <PlChip v-for=\"(opt, i) in selectedOptionsRef\" :key=\"i\" closeable small @click.stop=\"data.open = true\" @close=\"unselectOption(opt.value)\">\n {{ opt.label || opt.value }}\n </PlChip>\n </div>\n\n <div class=\"pl-autocomplete-multi__controls\">\n <PlMaskIcon24 v-if=\"isOptionsLoading\" name=\"loading\" />\n <slot name=\"append\" />\n <div class=\"pl-autocomplete-multi__arrow-wrapper\" @click.stop=\"toggleOpen\">\n <div class=\"arrow-icon arrow-icon-default\" />\n </div>\n </div>\n </div>\n <label v-if=\"label\">\n <PlSvg v-if=\"required\" :uri=\"SvgRequired\" />\n <span>{{ label }}</span>\n <PlTooltip v-if=\"slots.tooltip\" class=\"info\" position=\"top\">\n <template #tooltip>\n <slot name=\"tooltip\" />\n </template>\n </PlTooltip>\n </label>\n <DropdownOverlay\n v-if=\"data.open\"\n ref=\"overlay\"\n :root=\"rootRef\"\n class=\"pl-autocomplete-multi__options\"\n :gap=\"5\"\n tabindex=\"-1\"\n @focusout=\"onFocusOut\"\n >\n <div class=\"pl-autocomplete-multi__open-chips-container\">\n <PlChip\n v-for=\"(opt, i) in selectedOptionsRef\"\n :key=\"i\"\n closeable\n small\n @close=\"unselectOption(opt.value)\"\n >\n {{ opt.label || opt.value }}\n </PlChip>\n </div>\n <DropdownListItem\n v-for=\"(item, index) in filteredOptionsRef\"\n :key=\"index\"\n :option=\"item\"\n :text-item=\"'text'\"\n :is-selected=\"item.selected\"\n :is-hovered=\"data.activeOption == index\"\n size=\"medium\"\n use-checkbox\n @click.stop=\"selectOption(item.value)\"\n />\n <div v-if=\"!filteredOptionsRef.length && !isOptionsLoading\" class=\"nothing-found\">{{ emptyOptionsText }}</div>\n </DropdownOverlay>\n <DoubleContour class=\"pl-autocomplete-multi__contour\" :group-position=\"groupPosition\" />\n </div>\n </div>\n <div v-if=\"computedError\" class=\"pl-autocomplete-multi__error\">{{ computedError }}</div>\n <div v-else-if=\"helper\" class=\"pl-autocomplete-multi__helper\">{{ helper }}</div>\n </div>\n</template>\n"],"names":["__default__","emit","__emit","emitModel","v","slots","useSlots","props","__props","rootRef","ref","inputRef","overlay","useTemplateRef","data","reactive","watch","selectedValuesRef","computed","placeholderRef","debounce","toRef","searchOptionsRef","useWatchFetch","search","_open","_search","open","modelOptionsRef","allOptionsRef","modelOptions","searchOptions","seenValues","result","addOptions","options","option","canonicalValue","canonicalize","selectedOptionsRef","opt","deepEqual","filteredOptionsRef","selectedValues","unref","deepIncludes","isOptionsLoading","isDisabled","tabindex","updateActiveOption","selectOption","values","it","_a","unselectOption","d","setFocusOnInput","toggleOpen","onFocusOut","event","relatedTarget","_c","_b","handleKeydown","activeOption","filteredOptions","length","useLabelNotch","computedError","getErrorMessage","_createElementBlock","_createElementVNode","_normalizeClass","_hoisted_2","_hoisted_3","_cache","$event","_vModelText","_openBlock","_hoisted_5","_Fragment","_renderList","i","_createBlock","_unref","PlChip","_withModifiers","_createTextVNode","_toDisplayString","_hoisted_6","PlMaskIcon24","_renderSlot","_ctx","_hoisted_7","PlSvg","SvgRequired","PlTooltip","DropdownOverlay","_hoisted_8","item","index","DropdownListItem","_hoisted_9","_createVNode","DoubleContour","_hoisted_10","_hoisted_11"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/uikit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"resize-observer-polyfill": "^1.5.1",
|
|
33
33
|
"canonicalize": "~2.1.0",
|
|
34
34
|
"@milaboratories/helpers": "1.12.0",
|
|
35
|
-
"@platforma-sdk/model": "1.
|
|
35
|
+
"@platforma-sdk/model": "1.46.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"jsdom": "^25.0.1",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"svgo": "^3.3.2",
|
|
42
42
|
"typescript": "~5.6.3",
|
|
43
43
|
"@milaboratories/ts-configs": "1.0.6",
|
|
44
|
-
"@milaboratories/
|
|
44
|
+
"@milaboratories/eslint-config": "1.0.5",
|
|
45
45
|
"@milaboratories/ts-builder": "1.0.5",
|
|
46
|
-
"@milaboratories/
|
|
46
|
+
"@milaboratories/build-configs": "1.0.8"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"dev": "ts-builder serve --target browser-lib --build-config ./build.browser-lib.config.js",
|
|
@@ -39,11 +39,7 @@ const props = withDefaults(
|
|
|
39
39
|
/**
|
|
40
40
|
* Lambda for requesting of available options for the dropdown by search string.
|
|
41
41
|
*/
|
|
42
|
-
optionsSearch: (
|
|
43
|
-
/**
|
|
44
|
-
* Lambda for requesting of corresponding option for current model value. If empty, optionsSearch is used for this.
|
|
45
|
-
*/
|
|
46
|
-
modelSearch?: (v: NonNullable<M>) => Promise<ListOption<NonNullable<M>>>;
|
|
42
|
+
optionsSearch: (string: string, type: 'value' | 'label') => Promise<ListOption<M>[]>;
|
|
47
43
|
/**
|
|
48
44
|
* The label text for the dropdown field (optional)
|
|
49
45
|
*/
|
|
@@ -301,17 +297,14 @@ const searchDebounced = refDebounced(search, 300, { maxWait: 1000 });
|
|
|
301
297
|
|
|
302
298
|
const optionsRequest = useWatchFetch(() => searchDebounced.value, async (v) => {
|
|
303
299
|
if (v !== null) { // search is null when dropdown is closed;
|
|
304
|
-
return props.optionsSearch(v);
|
|
300
|
+
return props.optionsSearch(v, 'label');
|
|
305
301
|
}
|
|
306
302
|
return undefined;
|
|
307
303
|
});
|
|
308
304
|
|
|
309
305
|
const modelOptionRequest = useWatchFetch(() => model.value, async (v) => {
|
|
310
306
|
if (v != null && !deepEqual(modelOptionRef.value?.value, v)) { // load label for selected value if it was updated from outside the component
|
|
311
|
-
|
|
312
|
-
return props.modelSearch(v);
|
|
313
|
-
}
|
|
314
|
-
return (await props.optionsSearch(String(v)))?.[0];
|
|
307
|
+
return (await props.optionsSearch(String(v), 'value'))?.[0];
|
|
315
308
|
}
|
|
316
309
|
return modelOptionRef.value;
|
|
317
310
|
});
|
|
@@ -65,6 +65,11 @@ const emitModel = (v: M[]) => emit('update:modelValue', v);
|
|
|
65
65
|
|
|
66
66
|
const slots = useSlots();
|
|
67
67
|
|
|
68
|
+
interface OptionsSearch {
|
|
69
|
+
(s: string, type: 'label'): Promise<Readonly<ListOptionBase<M>[]>>;
|
|
70
|
+
(s: M[], type: 'value'): Promise<Readonly<ListOptionBase<M>[]>>;
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
const props = withDefaults(
|
|
69
74
|
defineProps<{
|
|
70
75
|
/**
|
|
@@ -74,11 +79,7 @@ const props = withDefaults(
|
|
|
74
79
|
/**
|
|
75
80
|
* Lambda for requesting of available options for the dropdown by search string.
|
|
76
81
|
*/
|
|
77
|
-
optionsSearch:
|
|
78
|
-
/**
|
|
79
|
-
* Lambda for requesting options that correspond to the current model values.
|
|
80
|
-
*/
|
|
81
|
-
modelSearch: (values: M[]) => Promise<Readonly<ListOptionBase<M>[]>>;
|
|
82
|
+
optionsSearch: OptionsSearch;
|
|
82
83
|
/**
|
|
83
84
|
* Unique identifier for the source of the options, changing it will invalidate the options cache.
|
|
84
85
|
*/
|
|
@@ -170,14 +171,14 @@ const placeholderRef = computed(() => {
|
|
|
170
171
|
const debounce = toRef(props, 'debounce');
|
|
171
172
|
|
|
172
173
|
const searchOptionsRef = useWatchFetch(() => [data.search, data.open, props.sourceId] as const, async ([search, _open]) => {
|
|
173
|
-
return props.optionsSearch(search);
|
|
174
|
+
return props.optionsSearch(search, 'label');
|
|
174
175
|
}, {
|
|
175
176
|
filterWatchResult: ([_search, open]) => open,
|
|
176
177
|
debounce,
|
|
177
178
|
});
|
|
178
179
|
|
|
179
180
|
const modelOptionsRef = useWatchFetch(() => [props.modelValue, props.sourceId] as const, async ([v]) => {
|
|
180
|
-
return props.
|
|
181
|
+
return props.optionsSearch(v, 'value');
|
|
181
182
|
}, {
|
|
182
183
|
debounce,
|
|
183
184
|
});
|
|
@@ -11,12 +11,6 @@ describe('PlAutocompleteMulti', () => {
|
|
|
11
11
|
'modelValue': [1],
|
|
12
12
|
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }),
|
|
13
13
|
'debounce': 0,
|
|
14
|
-
'modelSearch': async (values) => {
|
|
15
|
-
return [
|
|
16
|
-
{ label: 'Option 1', value: 1 },
|
|
17
|
-
{ label: 'Option 2', value: 2 },
|
|
18
|
-
].filter((v) => values.includes(v.value));
|
|
19
|
-
},
|
|
20
14
|
'optionsSearch': async () => {
|
|
21
15
|
return [
|
|
22
16
|
{ label: 'Option 1', value: 1 },
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { defineComponent as n, createBlock as r, openBlock as a, Transition as s, withCtx as p, renderSlot as c } from "vue";
|
|
2
|
-
const f = /* @__PURE__ */ n({
|
|
3
|
-
__name: "ExpandTransition",
|
|
4
|
-
setup(l) {
|
|
5
|
-
const t = (e) => {
|
|
6
|
-
e.classList.add("expand-collapse-fix"), e.style.setProperty("--component-height", e.scrollHeight + "px");
|
|
7
|
-
}, o = (e) => {
|
|
8
|
-
e.style.removeProperty("--component-height"), e.classList.remove("expand-collapse-fix");
|
|
9
|
-
};
|
|
10
|
-
return (e, i) => (a(), r(s, {
|
|
11
|
-
name: "expand-collapse",
|
|
12
|
-
onEnter: t,
|
|
13
|
-
onLeave: t,
|
|
14
|
-
onAfterEnter: o,
|
|
15
|
-
onAfterLeave: o
|
|
16
|
-
}, {
|
|
17
|
-
default: p(() => [
|
|
18
|
-
c(e.$slots, "default")
|
|
19
|
-
]),
|
|
20
|
-
_: 3
|
|
21
|
-
}));
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
export {
|
|
25
|
-
f as default
|
|
26
|
-
};
|
|
27
|
-
//# sourceMappingURL=ExpandTransition.vue.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ExpandTransition.vue.js","sources":["../../../src/components/PlAccordion/ExpandTransition.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nconst onStart = (el: Element) => {\n el.classList.add('expand-collapse-fix');\n (el as HTMLElement).style.setProperty('--component-height', el.scrollHeight + 'px');\n};\n\nconst onAfter = (el: Element) => {\n (el as HTMLElement).style.removeProperty('--component-height');\n el.classList.remove('expand-collapse-fix');\n};\n</script>\n\n<template>\n <Transition name=\"expand-collapse\" @enter=\"onStart\" @leave=\"onStart\" @after-enter=\"onAfter\" @after-leave=\"onAfter\">\n <slot/>\n </Transition>\n</template>\n\n<style>\n.expand-collapse-fix {\n overflow: hidden;\n}\n\n.expand-collapse-enter-active,\n.expand-collapse-leave-active {\n transition:\n height 0.2s ease-in-out,\n opacity 0.2s ease-in-out;\n height: var(--component-height);\n}\n\n.expand-collapse-enter-from,\n.expand-collapse-leave-to {\n opacity: 0.5;\n height: 0;\n}\n</style>\n"],"names":["onStart","el","onAfter","_createBlock","_Transition","_renderSlot","_ctx"],"mappings":";;;;AACA,UAAMA,IAAU,CAACC,MAAgB;AAC/B,MAAAA,EAAG,UAAU,IAAI,qBAAqB,GACrCA,EAAmB,MAAM,YAAY,sBAAsBA,EAAG,eAAe,IAAI;AAAA,IACpF,GAEMC,IAAU,CAACD,MAAgB;AAC9B,MAAAA,EAAmB,MAAM,eAAe,oBAAoB,GAC7DA,EAAG,UAAU,OAAO,qBAAqB;AAAA,IAC3C;2BAIEE,EAEaC,GAAA;AAAA,MAFD,MAAK;AAAA,MAAmB,SAAOJ;AAAA,MAAU,SAAOA;AAAA,MAAU,cAAaE;AAAA,MAAU,cAAaA;AAAA,IAAA;iBACxG,MAAO;AAAA,QAAPG,EAAOC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;;"}
|