{"version":3,"file":"taginput.mjs","sources":["../../src/components/taginput/Taginput.vue","../../src/components/taginput/index.ts"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T = string\">\nimport {\n    computed,\n    useAttrs,\n    useTemplateRef,\n    useId,\n    watchEffect,\n    ref,\n    type Component,\n} from \"vue\";\n\nimport OAutocomplete from \"../autocomplete/Autocomplete.vue\";\nimport OTag from \"../tag/Tag.vue\";\n\nimport { getDefault } from \"@/utils/config\";\nimport {\n    defineClasses,\n    getActiveClasses,\n    normalizeOptions,\n    findOption,\n    useInputHandler,\n    useSequentialId,\n    toOptionsGroup,\n    type OptionsGroupItem,\n} from \"@/composables\";\n\nimport type { TaginputProps } from \"./props\";\n\n/**\n * A simple tag input field that can have autocomplete functionality.\n * @displayName Taginput\n * @style _taginput.scss\n */\ndefineOptions({\n    isOruga: true,\n    name: \"OTaginput\",\n    configField: \"taginput\",\n    inheritAttrs: false,\n});\n\ntype ModelValue = TaginputProps<T>[\"modelValue\"];\n\nconst props = withDefaults(defineProps<TaginputProps<T>>(), {\n    override: undefined,\n    modelValue: undefined,\n    input: \"\",\n    options: undefined,\n    filter: undefined,\n    size: () => getDefault(\"taginput.size\"),\n    variant: () => getDefault(\"taginput.variant\"),\n    maxitems: undefined,\n    maxlength: undefined,\n    counter: () => getDefault(\"taginput.counter\", true),\n    openOnFocus: () => getDefault(\"taginput.openOnFocus\", true),\n    keepOpen: () => getDefault(\"taginput.keepOpen\", false),\n    placeholder: undefined,\n    expanded: false,\n    disabled: false,\n    keepFirst: () => getDefault(\"taginput.keepFirst\", false),\n    allowNew: () => getDefault(\"taginput.allowNew\", false),\n    allowDuplicates: () => getDefault(\"taginput.allowDuplicates\", false),\n    validateItem: () => true,\n    createItem: (item: T | string) => item as T,\n    checkScroll: () => getDefault(\"taginput.checkScroll\", false),\n    closeable: () => getDefault(\"taginput.closeable\", true),\n    iconPack: () => getDefault(\"taginput.iconPack\"),\n    icon: () => getDefault(\"taginput.icon\"),\n    closeIcon: () => getDefault(\"taginput.closeIcon\", \"close\"),\n    ariaCloseLabel: () => getDefault(\"taginput.ariaCloseLabel\", \"Remove\"),\n    autocomplete: () => getDefault(\"taginput.autocomplete\", \"off\"),\n    useHtml5Validation: () => getDefault(\"useHtml5Validation\", true),\n    customValidity: undefined,\n    teleport: () => getDefault(\"taginput.teleport\", false),\n    autocompleteClasses: () => getDefault(\"taginput.autocompleteClasses\", {}),\n});\n\nconst emits = defineEmits<{\n    /**\n     * modelValue prop two-way binding\n     * @param value {string[] | number[] | object[]} updated modelValue prop\n     */\n    \"update:model-value\": [value: ModelValue[]];\n    /**\n     * input prop two-way binding\n     * @param value {string} updated input prop\n     */\n    \"update:input\": [value: string];\n    /**\n     * on input change event\n     * @param value {string} input value\n     * @param event {Event} native event\n     */\n    input: [value: string, event: Event];\n    /**\n     * new item got added\n     * @param value {string | number | object} added item\n     */\n    add: [value: T];\n    /**\n     * item got removed\n     * @param value {string | number | object} removed item\n     */\n    remove: [value: T];\n    /**\n     * on input focus event\n     * @param event {Event} native event\n     */\n    focus: [event: Event];\n    /**\n     * on input blur event\n     * @param event {Event} native event\n     */\n    blur: [event: Event];\n    /**\n     * on input invalid event\n     * @param event {Event} native event\n     */\n    invalid: [event: Event];\n    /**\n     * on icon click event\n     * @param event {Event} native event\n     */\n    \"icon-click\": [event: Event];\n    /**\n     * on icon right click event\n     * @param event {Event} native event\n     */\n    \"icon-right-click\": [event: Event];\n    /** the list inside the dropdown reached the start */\n    \"scroll-start\": [];\n    /** the list inside the dropdown reached it's end */\n    \"scroll-end\": [];\n}>();\n\n// define as Component to prevent docs memmory overload\nconst autocompleteRef = useTemplateRef<Component>(\"autocompleteComponent\");\n\n// use form input functionalities\nconst { checkHtml5Validity, setFocus, onFocus, onBlur, onInvalid } =\n    useInputHandler(autocompleteRef, emits, props);\n\nconst isDropdownActive = ref(false);\n\n// the selected items, use v-model to make it two-way binding\nconst selectedItems = defineModel<ModelValue>({ default: undefined });\n\n// the value of the inner input, use v-model:input to make it two-way binding\nconst inputValue = defineModel<string>(\"input\", { default: \"\" });\n\nconst inputLength = computed(() => inputValue.value.trim().length);\nconst itemsLength = computed(() => selectedItems.value?.length || 0);\n\n// create a unique id sequence\nconst { nextSequence } = useSequentialId();\n\n/** normalized programamtic options */\nconst groupedOptions = computed<OptionsGroupItem<T>[]>(() => {\n    const normalizedOptions = normalizeOptions<T>(props.options, nextSequence);\n    const groupedOptions = toOptionsGroup<T>(normalizedOptions, nextSequence());\n    return groupedOptions;\n});\n\n/** map the selected items into option items */\nconst selectedOptions = computed(() => {\n    if (!selectedItems.value) return [];\n    return selectedItems.value.map((value) => {\n        const option = findOption<T>(groupedOptions, value);\n        // return the found option or create a new option object\n        if (option) return option;\n        else return { label: String(value), value, key: useId() };\n    });\n});\n\n/** show the input field if a maxitems hasn't been set or reached */\nconst hasInput = computed(\n    () => props.maxitems == null || itemsLength.value < Number(props.maxitems),\n);\n\nwatchEffect(() => {\n    // blur if input is empty\n    if (!hasInput.value) onBlur(new Event(\"blur\"));\n});\n\nfunction addItem(item?: T | string): void {\n    item = item || inputValue.value.trim();\n\n    if (item) {\n        const itemToAdd = props.createItem(item);\n\n        if (!selectedItems.value?.length) {\n            // Add the item input if not items are set yet\n            if (props.validateItem(item)) {\n                selectedItems.value = [itemToAdd];\n                emits(\"add\", itemToAdd);\n            }\n        } else {\n            // Add the item input if it is not blank\n            // or previously added (if not allowDuplicates).\n            const add = !props.allowDuplicates\n                ? !selectedItems.value.includes(itemToAdd)\n                : true;\n\n            if (add && props.validateItem(item)) {\n                selectedItems.value = [...selectedItems.value, itemToAdd];\n                emits(\"add\", itemToAdd);\n            }\n        }\n    }\n\n    // after autocomplete events\n    requestAnimationFrame(() => {\n        inputValue.value = \"\";\n        emits(\"input\", \"\", new Event(\"input\"));\n    });\n}\n\nfunction removeItem(index: number, event?: Event): void {\n    if (!selectedItems.value?.length) return;\n    const item = selectedItems.value.at(index);\n    if (!item) return;\n    selectedItems.value = selectedItems.value.toSpliced(index, 1);\n    emits(\"remove\", item);\n    if (event) event.stopPropagation();\n    if (props.openOnFocus && autocompleteRef.value) setFocus();\n}\n\n// --- Event Handler ---\n\nfunction onSelect(option: T | undefined): void {\n    if (!option) return;\n    addItem(option);\n}\n\nfunction onInput(value: string, event: Event): void {\n    emits(\"input\", value?.trim(), event);\n}\n\nfunction onBackspace(): void {\n    if (!inputValue.value?.length && itemsLength.value > 0)\n        // remove last item\n        removeItem(itemsLength.value - 1);\n}\n\nfunction onEnter(): void {\n    // Add item if not select only and dropdown selection is closed\n    if (props.allowNew && !isDropdownActive.value) addItem();\n}\n\n// --- Computed Component Classes ---\n\nconst rootClasses = defineClasses(\n    [\"rootClass\", \"o-taginput\"],\n    [\n        \"sizeClass\",\n        \"o-taginput--\",\n        computed(() => props.size),\n        computed(() => !!props.size),\n    ],\n    [\n        \"variantClass\",\n        \"o-taginput--\",\n        computed(() => props.variant),\n        computed(() => !!props.variant),\n    ],\n    [\n        \"expandedClass\",\n        \"o-taginput--expanded\",\n        null,\n        computed(() => props.expanded),\n    ],\n    [\n        \"disabledClass\",\n        \"o-taginput--disabled\",\n        null,\n        computed(() => props.disabled),\n    ],\n);\n\nconst containerClasses = defineClasses([\n    \"containerClass\",\n    \"o-taginput__container\",\n]);\n\nconst itemClasses = defineClasses(\n    [\"itemClass\", \"o-taginput__item\"],\n    [\n        \"variantClass\",\n        \"o-taginput__item--\",\n        computed(() => props.variant),\n        computed(() => !!props.variant),\n    ],\n);\n\nconst counterClasses = defineClasses([\"counterClass\", \"o-taginput__counter\"]);\n\nconst autocompleteRootClasses = defineClasses([\n    \"autocompleteClasses.rootClass\",\n    \"o-taginput__autocomplete\",\n]);\n\nconst autocompleteInputClasses = defineClasses([\n    \"autocompleteClasses.inputClasses.inputClass\",\n    \"o-taginput__input\",\n]);\n\nconst attrs = useAttrs();\n\nconst autocompleteBind = computed(() => ({\n    ...attrs,\n    \"root-class\": getActiveClasses(autocompleteRootClasses),\n    \"input-classes\": {\n        \"input-class\": getActiveClasses(autocompleteInputClasses),\n    },\n    ...props.autocompleteClasses,\n}));\n\n// --- Expose Public Functionalities ---\n\n/** expose functionalities for programmatic usage */\ndefineExpose({ checkHtml5Validity, focus: setFocus, value: selectedItems });\n</script>\n\n<template>\n    <div data-oruga=\"taginput\" :class=\"rootClasses\">\n        <div :class=\"containerClasses\" @focus=\"onFocus\" @blur=\"onBlur\">\n            <!--\n                @slot Override selected items\n                @binding {(string, object)[]} items - selected items\n                @binding {object[]} options - selected options\n                @binding {(index, event): void} removeItem - remove item function\n            -->\n            <slot\n                name=\"selected\"\n                :items=\"selectedItems\"\n                :options=\"selectedOptions\"\n                :remove-item=\"removeItem\">\n                <o-tag\n                    v-for=\"(option, index) in selectedOptions\"\n                    :key=\"option.key\"\n                    :label=\"option.label\"\n                    :class=\"itemClasses\"\n                    :closeable=\"closeable && !disabled\"\n                    :close-icon=\"closeIcon\"\n                    :close-icon-pack=\"iconPack\"\n                    :aria-close-label=\"ariaCloseLabel\"\n                    @close=\"removeItem(index, $event)\" />\n            </slot>\n\n            <o-autocomplete\n                v-show=\"hasInput\"\n                ref=\"autocompleteComponent\"\n                v-model:active=\"isDropdownActive\"\n                v-model:input=\"inputValue\"\n                v-bind=\"autocompleteBind\"\n                :options=\"options\"\n                :filter=\"filter\"\n                :placeholder=\"placeholder\"\n                :icon=\"icon\"\n                :icon-pack=\"iconPack\"\n                :maxlength=\"maxlength\"\n                :size=\"size\"\n                :disabled=\"disabled\"\n                :autocomplete=\"autocomplete\"\n                :open-on-focus=\"openOnFocus\"\n                :keep-first=\"keepFirst\"\n                :keep-open=\"keepOpen\"\n                :check-scroll=\"checkScroll\"\n                :teleport=\"teleport\"\n                :has-counter=\"false\"\n                :use-html5-validation=\"false\"\n                expanded\n                @input=\"onInput\"\n                @focus=\"onFocus\"\n                @blur=\"onBlur\"\n                @invalid=\"onInvalid\"\n                @keydown.enter=\"onEnter\"\n                @keydown.tab=\"onEnter\"\n                @keydown.backspace=\"onBackspace\"\n                @select=\"onSelect\"\n                @scroll-start=\"$emit('scroll-start')\"\n                @scroll-end=\"$emit('scroll-end')\"\n                @icon-click=\"$emit('icon-click', $event)\"\n                @icon-right-click=\"$emit('icon-right-click', $event)\">\n                <template v-if=\"$slots.header\" #header>\n                    <!--\n                        @slot Define an additional header\n                    -->\n                    <slot name=\"header\" />\n                </template>\n\n                <template\n                    v-if=\"$slots.default\"\n                    #default=\"{ option, index, value }\">\n                    <!--\n                        @slot Override the select option\n                        @binding {object} option - option object\n                        @binding {number} index - option index\n                        @binding {unknown} value - option value\n                    -->\n                    <slot :option=\"option\" :index=\"index\" :value=\"value\" />\n                </template>\n\n                <template v-if=\"$slots.empty\" #empty>\n                    <!--\n                        @slot Define content for empty state \n                    -->\n                    <slot name=\"empty\" />\n                </template>\n\n                <template v-if=\"$slots.footer\" #footer>\n                    <!--\n                        @slot Define an additional footer\n                    -->\n                    <slot name=\"footer\" />\n                </template>\n            </o-autocomplete>\n        </div>\n\n        <small\n            v-if=\"counter && (maxitems || maxlength)\"\n            :class=\"counterClasses\">\n            <template v-if=\"maxlength && inputLength > 0\">\n                <!--\n                    @slot Override the counter\n                    @binding {number} items - items count\n                    @binding {number} total - total count\n                -->\n                <slot name=\"counter\" :items=\"inputLength\" :total=\"maxlength\">\n                    {{ inputLength }} / {{ maxlength }}\n                </slot>\n            </template>\n\n            <template v-else-if=\"maxitems\">\n                <!--\n                    @slot Override the counter\n                    @binding {number} items - items count\n                    @binding {number} total - total count\n                -->\n                <slot name=\"counter\" :items=\"itemsLength\" :total=\"maxitems\">\n                    {{ itemsLength }} / {{ maxitems }}\n                </slot>\n            </template>\n        </small>\n    </div>\n</template>\n","import type { App, Plugin } from \"vue\";\n\nimport Taginput from \"./Taginput.vue\";\n\nimport { registerComponent } from \"@/utils/plugins\";\n\n/** export taginput plugin */\nexport default {\n    install(Vue: App) {\n        registerComponent(Vue, Taginput);\n    },\n} as Plugin;\n\n/** export taginput components */\nexport { Taginput as OTaginput };\n"],"names":["_useModel","groupedOptions","index","Taginput"],"mappings":"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"}