import { defineComponent, h, nextTick, PropType, reactive, ref, Ref } from 'vue' import { TagReactData, VxeTagConstructor, VxeTagEmits, VxeTagMethods, VxeTagPropTypes } from '../../../types/tag' import XEUtils from 'xe-utils' import { getFuncText } from '../../tools/utils' export default defineComponent({ name: 'vxeTag', props: { content: [String, Number] as PropType, color: { type: String as PropType, default: 'default' }, size: { type: String as PropType, default: 'medium' }, closable: { type: Boolean as PropType, default: false }, editable: { type: Boolean as PropType, default: false }, round: Boolean as PropType, tagStyle: { type: String as PropType, default: 'default' }, icon: String as PropType, iconSet: { type: String as PropType, default: '' }, align: { type: String as PropType, default: 'middle' } }, emits: [ 'close', 'update:content', 'icon-click', 'edit' ] as VxeTagEmits, setup (props, context) { const { slots, emit } = context const xID = XEUtils.uniqueId() const tagStyleList = [ 'default', 'outline', 'flag', 'dashed', 'mark', 'arrow' ] const reactData = reactive({ inited: false, editing: false }) const refElem = ref() as Ref const refContent = ref() as Ref const refMaps = { refElem } const closeTag = (event: Event) => new Promise(() => { event.stopPropagation() emit('close', { $event: { tag: $vxtag } }) }) const startEditing = () => new Promise(resolve => { if (props.editable && !reactData.editing) { reactData.editing = true nextTick(() => { refContent.value.focus() const range = document.createRange() range.selectNodeContents(refContent.value) const selection = window.getSelection() selection?.removeAllRanges() selection?.addRange(range) resolve(true) }) } else { resolve(true) } }) const handleContentEdited = () => { if (props.editable) { if (reactData.editing) { emit('update:content', refContent.value.innerText) emit('edit', refContent.value.innerText) } reactData.editing = false } } const handleIconClick = () => { emit('icon-click', { $event: { tag: $vxtag } }) } let tagMethods = {} as VxeTagMethods const $vxtag = { xID, props, context, reactData, getRefMaps: () => refMaps } as unknown as VxeTagConstructor tagMethods = { dispatchEvent (type, params, event) { emit(type, Object.assign({ $tag: $vxtag, $event: event }, params)) }, close: closeTag, startEditing } Object.assign($vxtag, tagMethods) const renderContent = () => slots?.default?.() ?? getFuncText(props.content) const renderIcon = () => slots?.icon?.() ?? (props.icon ? h('i', { class: [ 'vxe-tag--icon', props.iconSet, props.icon ], onClick: handleIconClick }) : null) const renderVN = () => { const presetColors = [ 'default', 'info', 'primary', 'success', 'warning', 'danger', 'error', 'perfect' ] return h('span', { ref: refElem, class: [ 'vxe-tag', props.editable && reactData.editing ? 'is--editing' : '', props.closable ? 'vxe-tag--closable' : '', `size--${props.size}`, `vxe-tag-type--${tagStyleList.includes(props.tagStyle) ? props.tagStyle : 'default'}`, props.round ? 'is--round' : '', `vxe-tag-align--${props.align}`, props.color ? presetColors.includes(props.color) ? `vxe-tag-color--${props.color}` : '' : 'vxe-tag-color--default', { closable: props.closable } ], style: presetColors.includes(props.color) ? null : { '--tag-color': props.color } }, [ props.closable ? h('div', { class: 'vxe-tag-close-icon', onClick: (event: Event) => { // closeTag(event) tagMethods.dispatchEvent('close', {}, event) } }, [ 'x' ] ) : null, slots?.avatar?.() ?? null, h('span', { class: ['vxe-tag-content', { 'tag-select-none': props.editable }], ref: refContent, contentEditable: props.editable && reactData.editing, onClick: startEditing, onBlur: handleContentEdited, onKeydown: (event: KeyboardEvent) => { if (event.key === 'Enter') { handleContentEdited() } else if (event.key === 'Escape') { handleContentEdited() } else if (event.key === 'Tab') { handleContentEdited() } } }, renderContent()), renderIcon() ] ) } $vxtag.renderVN = renderVN return $vxtag }, render () { return this.renderVN() } })