diff --git a/src/popup/container.tsx b/src/popup/container.tsx index 80f2bb6d7..e2c01677d 100644 --- a/src/popup/container.tsx +++ b/src/popup/container.tsx @@ -2,7 +2,42 @@ import Vue from 'vue'; import { getAttach } from '../utils/dom'; import props from './props'; +function isContentRectChanged(rect1: DOMRectReadOnly, rect2: DOMRectReadOnly) { + if (!rect1 || !rect2) return; + if (['width', 'height', 'x', 'y'].some((k) => rect1[k] !== rect2[k])) { + return true; + } + return false; +} + const Ref = Vue.extend({ + data() { + return { + contentRect: null as DOMRectReadOnly, + }; + }, + mounted() { + if (window?.ResizeObserver && this.$el) { + const el = this.$el; + const vm = this as any; + const ro = new ResizeObserver((entries = []) => { + const { contentRect } = entries[0] || {}; + if (isContentRectChanged(contentRect, vm.contentRect)) { + vm.contentRect = contentRect; + vm.$emit('resize', { ...contentRect }); + return; + } + // omit initial change + if (!vm.contentRect) { + vm.contentRect = contentRect; + } + }); + ro.observe(el); + this.$on('hook:destroyed', () => { + ro.unobserve(el); + }); + } + }, render() { const children = this.$slots.default || []; if (children.length > 1 || !children[0]?.tag) { @@ -68,6 +103,6 @@ export default Vue.extend({ }, }, render() { - return {this.$slots.default}; + return this.$emit('refResize', ev)}>{this.$slots.default}; }, }); diff --git a/src/popup/popup.tsx b/src/popup/popup.tsx index 0467f7406..fcb46e067 100644 --- a/src/popup/popup.tsx +++ b/src/popup/popup.tsx @@ -53,6 +53,10 @@ export default Vue.extend({ /** if a trusted action (opening or closing) is prevented, increase this flag */ visibleState: 0, mouseInRange: false, + /** + * mark popup as clicked when mousedown + * consume this flag right after click event bubbles up to document + */ contentClicked: false, refClicked: false, }; @@ -328,17 +332,24 @@ export default Vue.extend({ directives: destroyOnClose ? undefined : [ - { - name: 'show', - rawName: 'v-show', - value: visible, - expression: 'visible', - } as VNodeDirective, + { + name: 'show', + rawName: 'v-show', + value: visible, + expression: 'visible', + } as VNodeDirective, ], on: { mousedown: () => { this.contentClicked = true; }, + mouseup: () => { + // make sure to execute after document click is triggered + setTimeout(() => { + // make sure flag is consumed + this.contentClicked = false; + }); + }, ...(hasTrigger.hover && { mouseenter: this.onMouseEnter, mouseleave: this.onMouseLeave, @@ -374,6 +385,11 @@ export default Vue.extend({ this.updateOverlayStyle(); } }} + onRefResize={() => { + if (visible) { + this.updatePopper(); + } + }} parent={this} visible={visible} attach={this.attach} diff --git a/src/select/select.tsx b/src/select/select.tsx index 9ee91fe4a..d6991d998 100644 --- a/src/select/select.tsx +++ b/src/select/select.tsx @@ -673,7 +673,7 @@ export default mixins(getConfigReceiverMixins('select')).exte {loading &&
{loadingTextSlot}
} - {!loading && !displayOptions.length && !showCreateOption &&
  • {emptySlot}
  • } + {!loading && !displayOptions.length && !showCreateOption &&
    {emptySlot}
    } {!this.hasOptions && displayOptions.length && !loading ? ( this.renderDataWithOptions() ) : ( @@ -685,37 +685,46 @@ export default mixins(getConfigReceiverMixins('select')).exte ); }, - }, + /** + * Parse options from slots before popup, execute only once + */ + initOptions() { + if (this.realOptions.length || this.isInited) return; - updated() { - if (this.realOptions.length || this.isInited) return; + const children = renderTNodeJSX(this, 'default'); + if (children) { + this.realOptions = parseOptions(children); + this.isInited = true; + } - // Parse options from slots before popup, execute only once - const children = renderTNodeJSX(this, 'default'); - if (children) { - this.realOptions = parseOptions(children); - this.isInited = true; - } + function parseOptions(vnodes: VNode[]): TdOptionProps[] { + if (!vnodes) return []; + return vnodes.reduce((options, vnode) => { + const { componentOptions } = vnode; + if (componentOptions?.tag === 't-option') { + const propsData = componentOptions.propsData as any; + return options.concat({ + label: propsData.label, + value: propsData.value, + disabled: propsData.disabled, + content: componentOptions.children ? () => componentOptions.children : propsData.content, + default: propsData.default, + }); + } + if (componentOptions?.tag === 't-option-group') { + return options.concat(parseOptions(componentOptions.children)); + } + return options; + }, []); + } + }, + }, - function parseOptions(vnodes: VNode[]): TdOptionProps[] { - if (!vnodes) return []; - return vnodes.reduce((options, vnode) => { - if (vnode.componentOptions.tag === 't-option') { - const propsData = vnode.componentOptions.propsData as any; - return options.concat({ - label: propsData.label, - value: propsData.value, - disabled: propsData.disabled, - content: propsData.content, - default: propsData.default, - }); - } - if (vnode.componentOptions.tag === 't-option-group') { - return options.concat(parseOptions(vnode.componentOptions.children)); - } - return options; - }, []); - } + mounted() { + this.initOptions(); + }, + updated() { + this.initOptions(); }, render(): VNode { diff --git a/test/ssr/__snapshots__/ssr.test.js.snap b/test/ssr/__snapshots__/ssr.test.js.snap index 66da774e1..673d19e96 100644 --- a/test/ssr/__snapshots__/ssr.test.js.snap +++ b/test/ssr/__snapshots__/ssr.test.js.snap @@ -10636,6 +10636,83 @@ exports[`ssr snapshot test renders ./examples/table/demos/custom-cell.vue correc `; +exports[`ssr snapshot test renders ./examples/table/demos/custom-col.vue correctly 1`] = ` +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    序号平台类型默认值是否必传详情信息
    0共有String-读取 0 个数据的嵌套信息值
    1私有Number0读取 1 个数据的嵌套信息值
    2共有Array[]读取 2 个数据的嵌套信息值
    3私有Object{}读取 3 个数据的嵌套信息值
    4共有String-读取 4 个数据的嵌套信息值
    +
    +
    +
    +
    +`; + exports[`ssr snapshot test renders ./examples/table/demos/custom-header.vue correctly 1`] = `