import { defineComponent , VNode , PropType , toRefs , computed , ref , Text , onUnmounted , VNodeTypes , watch , reactive , } from 'vue' ; import { getPrefixCls } from '../_utils/global-config' ; import { isObject , isString , isUndefined } from '../_utils/is' ; import { BaseProps , EllipsisConfig , EllipsisInternalConfig } from './interface' ; import EditContent from './edit-content.vue' ; import Operations from './operations.vue' ; import ResizeObserver from '../_components/resize-observer' ; import { omit } from '../_utils/omit' ; import useMergeState from '../_hooks/use-merge-state' ; import usePickSlots from '../_hooks/use-pick-slots' ; import measure from './utils/measure' ; import { clipboard } from '../_utils/clipboard' ; import getInnerText from './utils/getInnerText' ; import { caf , raf } from '../_utils/raf' ; import Tooltip from '../tooltip' ; import Popover from '../popover' ; interface BaseInternalProps extends BaseProps { component : keyof HTMLElementTagNameMap ; } function getClassNames ( prefixCls , props ) { const { type , disabled } = props ; const classNames = [ ] ; if ( type ) { classNames . push ( `${ prefixCls }-${ type }` ) ; } if ( disabled ) { classNames . push ( `${ prefixCls }-disabled` ) ; } return classNames ; } function getComponentTags < K extends keyof HTMLElementTagNameMap > ( props ) { const { bold , mark , underline , delete : propDelete , code } = props ; const componentTags = [ ] ; if ( bold ) { componentTags . push ( 'b' ) ; } if ( underline ) { componentTags . push ( 'u' ) ; } if ( propDelete ) { componentTags . push ( 'del' ) ; } if ( code ) { componentTags . push ( 'code' ) ; } if ( mark ) { componentTags . push ( 'mark' ) ; } return componentTags as K [ ] ; } function getEditText ( children ) { if ( ! children ) return '' ; const res = [ ] ; children . some ( ( child ) => { if ( child . type === Text && isString ( child . children ) ) { res . push ( String ( child . children ) ) ; return true ; } return false ; } ) ; return res . join ( '' ) ; } function Wrap ( props , children ) { const { mark } = props ; const componentTags = getComponentTags ( props ) ; let content = children ; componentTags . forEach ( ( Tag ) => { const attrs = isObject ( mark ) && mark . color ? { style : { backgroundColor : mark . color } } : { } ; content = < Tag { ... attrs } > { content } < /Tag>; } ) ; return content ; } function normalizeEllipsisConfig ( config ) { const showTooltip = ! ! config . showTooltip ; const TooltipComponent = isObject ( config . showTooltip ) && config . showTooltip . type === 'popover' ? Popover : Tooltip ; const tooltipProps = ( isObject ( config . showTooltip ) && config . showTooltip . props ) || { } ; return { rows : 1 , suffix : '' , ellipsisStr : '...' , expandable : false , ... omit ( config , [ 'showTooltip' ] ) , showTooltip , TooltipComponent , tooltipProps } ; } displayName noBrackets export default defineComponent ( { name : 'TypographyBase' , props : { component : { type : String as PropType < BaseInternalProps [ 'component' ] > , required : true } , zh en type : { type : String as PropType < 'primary' | 'secondary' | 'success' | 'danger' | 'warning' > } , zh en bold : { type : Boolean } , zh en mark : { type : [ Boolean , Object ] as PropType < boolean | { color : string ; } > , default : false } , zh en underline : { type : Boolean } , zh en delete : { type : Boolean } , zh en code : { type : Boolean } , zh en disabled : { type : Boolean } , zh en editable : { type : Boolean } , zh en vModel editing : { type : Boolean , default : undefined } , zh en defaultEditing : { type : Boolean } , zh en vModel editText : { type : String } , zh en copyable : { type : Boolean } , zh en copyText : { type : String } , zh en ellipsis : { type : [ Boolean , Object ] as PropType < boolean | EllipsisConfig > , default : false } } , emits : [ 'editStart' , 'change' , 'update:editText' , 'editEnd' , 'update:editing' , 'copy' , 'ellipsis' , 'expand' , ] , zh en slot binding zh en slot binding zh en slot binding setup ( props , { slots , emit } ) { const { editing : propEditing , defaultEditing , ellipsis , copyable , editable , copyText , } = toRefs ( props ) ; const prefixCls = getPrefixCls ( 'typography' ) ; const classNames = computed ( ( ) => [ prefixCls , ... getClassNames ( prefixCls , props ) , ] ) ; const wrapperRef = ref ( ) ; const defaultSlot = usePickSlots ( slots , 'default' ) ; const children = computed < VNode [ ] > ( ( ) => { return defaultSlot . value ?. ( ) || [ ] ; } ) ; const fullText = computed < string > ( ( ) => getInnerText ( children . value ) ) ; const [ editing , setEditing ] = useMergeState ( defaultEditing . value , reactive ( { value : propEditing } ) ) ; const mergeEditing = computed ( ( ) => editable . value && editing . value ) ; function onEditStart ( ) { emit ( 'update:editing' , true ) ; emit ( 'editStart' ) ; setEditing ( true ) ; } function onEditChange ( text ) { emit ( 'update:editText' , text ) ; emit ( 'change' , text ) ; } function onEditEnd ( ) { emit ( 'update:editing' , false ) ; emit ( 'editEnd' ) ; setEditing ( false ) ; } const isCopied = ref ( false ) ; const copyTimer = ref ( ) ; function onCopyClick ( ) { const text = ! isUndefined ( copyText ?. value ) ? copyText ?. value : fullText . value ; clipboard ( text || '' ) ; isCopied . value = true ; emit ( 'copy' , text ) ; copyTimer . value = setTimeout ( ( ) => { isCopied . value = false ; } , 3000 ) ; } onUnmounted ( ( ) => { copyTimer . value && clearTimeout ( copyTimer as any ) ; copyTimer . value = null ; } ) ; const isEllipsis = ref ( false ) ; const expanded = ref ( false ) ; const ellipsisText = ref ( '' ) ; const ellipsisConfig = computed < EllipsisInternalConfig > ( ( ) => normalizeEllipsisConfig ( ( isObject ( ellipsis ?. value ) && ellipsis ?. value ) || { } ) ) ; const rafId = ref ( ) ; function onExpandClick ( ) { const newVal = ! expanded . value ; expanded . value = newVal ; emit ( 'expand' , newVal ) ; } function renderOperations ( forceRenderExpand = false ) { return ( < Operations editable = { editable . value } copyable = { copyable . value } expandable = { ellipsisConfig . value . expandable } isCopied = { isCopied . value } isEllipsis = { isEllipsis . value } expanded = { expanded . value } forceRenderExpand = { forceRenderExpand } onEdit = { onEditStart } onCopy = { onCopyClick } onExpand = { onExpandClick } v - slots = { { 'copy-tooltip' : slots [ 'copy-tooltip' ] , 'copy-icon' : slots [ 'copy-icon' ] , 'expand-node' : slots [ 'expand-node' ] } } />); } function calEllipsis ( ) { if ( ! wrapperRef . value ) return ; const { ellipsis , text } = measure ( wrapperRef . value , ellipsisConfig . value , renderOperations ( ! ! ellipsisConfig . value . expandable ) , fullText . value ) ; if ( isEllipsis . value !== ellipsis ) { isEllipsis . value = ellipsis ; emit ( 'ellipsis' , ellipsis ) ; } if ( ellipsisText . value !== text ) { ellipsisText . value = text || '' ; } } function resizeOnNextFrame ( ) { const needCalEllipsis = ! ! ellipsis ?. value && ! expanded . value ; if ( ! needCalEllipsis ) return ; caf ( rafId . value ) ; rafId . value = raf ( ( ) => { calEllipsis ( ) ; } ) ; } onUnmounted ( ( ) => { caf ( rafId . value ) ; } ) ; const rows = computed ( ( ) => ellipsisConfig . value . rows ) ; watch ( [ rows , children ] , ( ) => { resizeOnNextFrame ( ) ; } ) ; watch ( ellipsis , ( newVal ) => { if ( newVal ) { resizeOnNextFrame ( ) ; } else { isEllipsis . value = false ; } } ) ; return { props , classNames , children , fullText , isEllipsis , expanded , ellipsisText , ellipsisConfig , mergeEditing , wrapperRef , renderOperations , onEditChange , onEditEnd , onResize ( ) { resizeOnNextFrame ( ) ; } } ; } , render ( ) { const { props , component : Component , classNames , isEllipsis , expanded , ellipsisText , ellipsisConfig , children , fullText , editText , mergeEditing , renderOperations , onResize , onEditChange , onEditEnd , } = this ; if ( mergeEditing ) { const _editText = ! isUndefined ( editText ) ? editText : getEditText ( children ) ; return ( < EditContent text = { _editText } onChange = { ( text ) => { if ( text !== _editText ) { onEditChange ( text ) ; } } } onEnd = { onEditEnd } / > ) ; } const { suffix , ellipsisStr , showTooltip , tooltipProps , TooltipComponent } = ellipsisConfig ; const showEllipsis = isEllipsis && ! expanded ; const Content = Wrap ( props , showEllipsis ? ellipsisText : children ) ; const titleAttrs = showEllipsis && ! showTooltip ? { title : fullText } : { } ; return ( < ResizeObserver onResize = { onResize } > < Component class = { classNames } ref = "wrapperRef" { ... titleAttrs } > { showEllipsis && showTooltip ? ( < TooltipComponent { ... tooltipProps } v - slots = { { content : ( ) => fullText , default : ( ) => [ < span > { Content } < /span>] } } />) : (Content)} { showEllipsis ? ellipsisStr : null } { suffix } { renderOperations ( ) } < /Component> < /ResizeObserver>); } } ) ;