refactor: popover

pull/4606/head
tangjinzhou 2021-08-31 14:44:45 +08:00
parent 783fb2c8a9
commit f71a72208b
9 changed files with 214 additions and 127 deletions

View File

@ -4,10 +4,8 @@ exports[`Popover should show overlay when trigger is clicked 1`] = `
<div class="ant-popover-content"> <div class="ant-popover-content">
<div class="ant-popover-arrow"><span class="ant-popover-arrow-content"></span></div> <div class="ant-popover-arrow"><span class="ant-popover-arrow-content"></span></div>
<div class="ant-popover-inner" role="tooltip"> <div class="ant-popover-inner" role="tooltip">
<div> <div class="ant-popover-title">code</div>
<div class="ant-popover-title">code</div> <div class="ant-popover-inner-content">console.log('hello world')</div>
<div class="ant-popover-inner-content">console.log('hello world')</div>
</div>
</div> </div>
</div> </div>
`; `;
@ -16,10 +14,8 @@ exports[`Popover should show overlay when trigger is clicked 2`] = `
<div class="ant-popover-content"> <div class="ant-popover-content">
<div class="ant-popover-arrow"><span class="ant-popover-arrow-content"></span></div> <div class="ant-popover-arrow"><span class="ant-popover-arrow-content"></span></div>
<div class="ant-popover-inner" role="tooltip"> <div class="ant-popover-inner" role="tooltip">
<div> <div class="ant-popover-title">code</div>
<div class="ant-popover-title">code</div> <div class="ant-popover-inner-content">console.log('hello world')</div>
<div class="ant-popover-inner-content">console.log('hello world')</div>
</div>
</div> </div>
</div> </div>
`; `;

View File

@ -1,54 +1,60 @@
import { defineComponent, inject } from 'vue'; import { computed, defineComponent, ExtractPropTypes, ref } from 'vue';
import Tooltip from '../tooltip'; import Tooltip from '../tooltip';
import abstractTooltipProps from '../tooltip/abstractTooltipProps'; import abstractTooltipProps from '../tooltip/abstractTooltipProps';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { getOptionProps, getComponent, getSlot } from '../_util/props-util'; import { initDefaultProps } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider';
import { withInstall } from '../_util/type'; import { withInstall } from '../_util/type';
import useConfigInject from '../_util/hooks/useConfigInject';
import omit from '../_util/omit';
import { getTransitionName } from '../_util/transition';
export const popoverProps = () => ({
...abstractTooltipProps(),
content: PropTypes.any,
title: PropTypes.any,
});
export type PopoverProps = Partial<ExtractPropTypes<ReturnType<typeof popoverProps>>>;
const props = abstractTooltipProps();
const Popover = defineComponent({ const Popover = defineComponent({
name: 'APopover', name: 'APopover',
props: { props: initDefaultProps(popoverProps(), {
...props, trigger: 'hover',
prefixCls: PropTypes.string, transitionName: 'zoom-big',
transitionName: PropTypes.string.def('zoom-big'), placement: 'top',
content: PropTypes.any, mouseEnterDelay: 0.1,
title: PropTypes.any, mouseLeaveDelay: 0.1,
}, }),
setup() { setup(props, { expose, slots }) {
return { const tooltipRef = ref();
configProvider: inject('configProvider', defaultConfigProvider),
};
},
methods: {
getPopupDomNode() {
return (this.$refs.tooltip as any).getPopupDomNode();
},
},
render() { expose({
const { title, prefixCls: customizePrefixCls, $slots } = this; getPopupDomNode: () => {
const { getPrefixCls } = this.configProvider; return tooltipRef.value?.getPopupDomNode?.();
const prefixCls = getPrefixCls('popover', customizePrefixCls); },
});
const props = getOptionProps(this); const { prefixCls, configProvider } = useConfigInject('popover', props);
delete props.title; const rootPrefixCls = computed(() => configProvider.getPrefixCls());
delete props.content; const getOverlay = () => {
const tooltipProps = { const { title = slots.title?.(), content = slots.content?.() } = props;
...props, return (
prefixCls, <>
ref: 'tooltip', {title && <div class={`${prefixCls.value}-title`}>{title}</div>}
title: ( <div class={`${prefixCls.value}-inner-content`}>{content}</div>
<div> </>
{(title || $slots.title) && ( );
<div class={`${prefixCls}-title`}>{getComponent(this, 'title')}</div> };
)} return () => {
<div class={`${prefixCls}-inner-content`}>{getComponent(this, 'content')}</div> return (
</div> <Tooltip
), {...omit(props, ['title', 'content'])}
prefixCls={prefixCls.value}
ref={tooltipRef}
v-slots={{ title: getOverlay, default: slots.default }}
transitionName={getTransitionName(rootPrefixCls.value, 'zoom-big', props.transitionName)}
/>
);
}; };
return <Tooltip {...tooltipProps}>{getSlot(this)}</Tooltip>;
}, },
}); });

View File

@ -0,0 +1,3 @@
@import './index.less';
.popover-customize-bg(@popover-prefix-cls, @popover-background);

View File

@ -3,6 +3,11 @@
@popover-prefix-cls: ~'@{ant-prefix}-popover'; @popover-prefix-cls: ~'@{ant-prefix}-popover';
@popover-arrow-rotate-width: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
@popover-arrow-offset-vertical: 12px;
@popover-arrow-offset-horizontal: 16px;
.@{popover-prefix-cls} { .@{popover-prefix-cls} {
.reset-component(); .reset-component();
@ -68,16 +73,16 @@
&-title { &-title {
min-width: @popover-min-width; min-width: @popover-min-width;
min-height: 32px; min-height: @popover-min-height;
margin: 0; // reset heading margin margin: 0; // reset heading margin
padding: 5px @padding-md 4px; padding: 5px @popover-padding-horizontal 4px;
color: @heading-color; color: @heading-color;
font-weight: 500; font-weight: 500;
border-bottom: 1px solid @border-color-split; border-bottom: 1px solid @border-color-split;
} }
&-inner-content { &-inner-content {
padding: 12px @padding-md; padding: @padding-sm @popover-padding-horizontal;
color: @popover-color; color: @popover-color;
} }
@ -88,7 +93,9 @@
font-size: @font-size-base; font-size: @font-size-base;
> .@{iconfont-css-prefix} { > .@{iconfont-css-prefix} {
position: absolute; position: absolute;
top: 8px; // 4px for padding-top, 4px for vertical middle; top: (
4px + ((@line-height-base * @font-size-base - @font-size-base) / 2)
); // 4px for padding-top, 4px for vertical middle
color: @warning-color; color: @warning-color;
font-size: @font-size-base; font-size: @font-size-base;
} }
@ -100,106 +107,146 @@
&-buttons { &-buttons {
margin-bottom: 4px; margin-bottom: 4px;
text-align: right; text-align: right;
button { button {
margin-left: 8px; margin-left: 8px;
} }
} }
// Arrows // Arrows
// .popover-arrow is outer, .popover-arrow:after is inner
&-arrow { &-arrow {
position: absolute; position: absolute;
display: block; display: block;
width: sqrt(@popover-arrow-width * @popover-arrow-width * 2); width: @popover-arrow-rotate-width;
height: sqrt(@popover-arrow-width * @popover-arrow-width * 2); height: @popover-arrow-rotate-width;
overflow: hidden;
background: transparent; background: transparent;
border-style: solid; pointer-events: none;
border-width: (sqrt(@popover-arrow-width * @popover-arrow-width * 2) / 2);
transform: rotate(45deg); &-content {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
width: @popover-arrow-width;
height: @popover-arrow-width;
margin: auto;
background-color: @popover-bg;
content: '';
pointer-events: auto;
}
} }
&-placement-top > &-content > &-arrow, &-placement-top &-arrow,
&-placement-topLeft > &-content > &-arrow, &-placement-topLeft &-arrow,
&-placement-topRight > &-content > &-arrow { &-placement-topRight &-arrow {
bottom: @popover-distance - @popover-arrow-width + 2.2px; bottom: @popover-distance - @popover-arrow-rotate-width;
border-top-color: transparent;
border-right-color: @popover-bg; &-content {
border-bottom-color: @popover-bg; box-shadow: 3px 3px 7px fade(@black, 7%);
border-left-color: transparent; transform: translateY((-@popover-arrow-rotate-width / 2)) rotate(45deg);
box-shadow: 3px 3px 7px fade(@black, 7%); }
} }
&-placement-top > &-content > &-arrow { &-placement-top &-arrow {
left: 50%; left: 50%;
transform: translateX(-50%) rotate(45deg); transform: translateX(-50%);
}
&-placement-topLeft > &-content > &-arrow {
left: 16px;
}
&-placement-topRight > &-content > &-arrow {
right: 16px;
} }
&-placement-right > &-content > &-arrow, &-placement-topLeft &-arrow {
&-placement-rightTop > &-content > &-arrow, left: @popover-arrow-offset-horizontal;
&-placement-rightBottom > &-content > &-arrow {
left: @popover-distance - @popover-arrow-width + 2px;
border-top-color: transparent;
border-right-color: transparent;
border-bottom-color: @popover-bg;
border-left-color: @popover-bg;
box-shadow: -3px 3px 7px fade(@black, 7%);
} }
&-placement-right > &-content > &-arrow {
&-placement-topRight &-arrow {
right: @popover-arrow-offset-horizontal;
}
&-placement-right &-arrow,
&-placement-rightTop &-arrow,
&-placement-rightBottom &-arrow {
left: @popover-distance - @popover-arrow-rotate-width;
&-content {
box-shadow: -3px 3px 7px fade(@black, 7%);
transform: translateX((@popover-arrow-rotate-width / 2)) rotate(45deg);
}
}
&-placement-right &-arrow {
top: 50%; top: 50%;
transform: translateY(-50%) rotate(45deg); transform: translateY(-50%);
} }
&-placement-rightTop > &-content > &-arrow { &-placement-rightTop &-arrow {
top: 12px; top: @popover-arrow-offset-vertical;
} }
&-placement-rightBottom > &-content > &-arrow { &-placement-rightBottom &-arrow {
bottom: 12px; bottom: @popover-arrow-offset-vertical;
} }
&-placement-bottom > &-content > &-arrow, &-placement-bottom &-arrow,
&-placement-bottomLeft > &-content > &-arrow, &-placement-bottomLeft &-arrow,
&-placement-bottomRight > &-content > &-arrow { &-placement-bottomRight &-arrow {
top: @popover-distance - @popover-arrow-width + 2px; top: @popover-distance - @popover-arrow-rotate-width;
border-top-color: @popover-bg;
border-right-color: transparent; &-content {
border-bottom-color: transparent; box-shadow: -2px -2px 5px fade(@black, 6%);
border-left-color: @popover-bg; transform: translateY((@popover-arrow-rotate-width / 2)) rotate(45deg);
box-shadow: -2px -2px 5px fade(@black, 6%); }
} }
&-placement-bottom > &-content > &-arrow {
&-placement-bottom &-arrow {
left: 50%; left: 50%;
transform: translateX(-50%) rotate(45deg); transform: translateX(-50%);
}
&-placement-bottomLeft > &-content > &-arrow {
left: 16px;
}
&-placement-bottomRight > &-content > &-arrow {
right: 16px;
} }
&-placement-left > &-content > &-arrow, &-placement-bottomLeft &-arrow {
&-placement-leftTop > &-content > &-arrow, left: @popover-arrow-offset-horizontal;
&-placement-leftBottom > &-content > &-arrow {
right: @popover-distance - @popover-arrow-width + 2px;
border-top-color: @popover-bg;
border-right-color: @popover-bg;
border-bottom-color: transparent;
border-left-color: transparent;
box-shadow: 3px -3px 7px fade(@black, 7%);
} }
&-placement-left > &-content > &-arrow {
&-placement-bottomRight &-arrow {
right: @popover-arrow-offset-horizontal;
}
&-placement-left &-arrow,
&-placement-leftTop &-arrow,
&-placement-leftBottom &-arrow {
right: @popover-distance - @popover-arrow-rotate-width;
&-content {
box-shadow: 3px -3px 7px fade(@black, 7%);
transform: translateX((-@popover-arrow-rotate-width / 2)) rotate(45deg);
}
}
&-placement-left &-arrow {
top: 50%; top: 50%;
transform: translateY(-50%) rotate(45deg); transform: translateY(-50%);
} }
&-placement-leftTop > &-content > &-arrow {
top: 12px; &-placement-leftTop &-arrow {
top: @popover-arrow-offset-vertical;
} }
&-placement-leftBottom > &-content > &-arrow {
bottom: 12px; &-placement-leftBottom &-arrow {
bottom: @popover-arrow-offset-vertical;
} }
} }
.generator-popover-preset-color(@i: length(@preset-colors)) when (@i > 0) {
.generator-popover-preset-color(@i - 1);
@color: extract(@preset-colors, @i);
@lightColor: '@{color}-6';
.@{popover-prefix-cls}-@{color} {
.@{popover-prefix-cls}-inner {
background-color: @@lightColor;
}
.@{popover-prefix-cls}-arrow {
&-content {
background-color: @@lightColor;
}
}
}
}
.generator-popover-preset-color();
@import './rtl';

View File

@ -0,0 +1,33 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@popover-prefix-cls: ~'@{ant-prefix}-popover';
.@{popover-prefix-cls} {
&-rtl {
direction: rtl;
text-align: right;
}
&-message {
&-title {
.@{popover-prefix-cls}-rtl & {
padding-right: @font-size-base + 8px;
padding-left: @padding-md;
}
}
}
&-buttons {
.@{popover-prefix-cls}-rtl & {
text-align: left;
}
button {
.@{popover-prefix-cls}-rtl & {
margin-right: 8px;
margin-left: 0;
}
}
}
}

View File

@ -472,6 +472,7 @@
@popover-color: @text-color; @popover-color: @text-color;
// Popover maximum width // Popover maximum width
@popover-min-width: 177px; @popover-min-width: 177px;
@popover-min-height: 32px;
// Popover arrow width // Popover arrow width
@popover-arrow-width: 6px; @popover-arrow-width: 6px;
// Popover arrow color // Popover arrow color
@ -481,6 +482,7 @@
@popover-arrow-outer-color: @popover-bg; @popover-arrow-outer-color: @popover-bg;
// Popover distance with trigger // Popover distance with trigger
@popover-distance: @popover-arrow-width + 4px; @popover-distance: @popover-arrow-width + 4px;
@popover-padding-horizontal: @padding-md;
// Modal // Modal
// -- // --

View File

@ -5,7 +5,7 @@
</template> </template>
<script> <script>
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import demo from '../v2-doc/src/docs/popconfirm/demo/index.vue'; import demo from '../v2-doc/src/docs/popover/demo/index.vue';
// import Affix from '../components/affix'; // import Affix from '../components/affix';
export default defineComponent({ export default defineComponent({
components: { components: {

2
v2-doc

@ -1 +1 @@
Subproject commit dcd8922ce91de1a48c515d3b06b898d27fcb30f4 Subproject commit 53e2e630b1d0c666252e9b747690f0e524e5a08b