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-arrow"><span class="ant-popover-arrow-content"></span></div>
<div class="ant-popover-inner" role="tooltip">
<div>
<div class="ant-popover-title">code</div>
<div class="ant-popover-inner-content">console.log('hello world')</div>
</div>
<div class="ant-popover-title">code</div>
<div class="ant-popover-inner-content">console.log('hello world')</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-arrow"><span class="ant-popover-arrow-content"></span></div>
<div class="ant-popover-inner" role="tooltip">
<div>
<div class="ant-popover-title">code</div>
<div class="ant-popover-inner-content">console.log('hello world')</div>
</div>
<div class="ant-popover-title">code</div>
<div class="ant-popover-inner-content">console.log('hello world')</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 abstractTooltipProps from '../tooltip/abstractTooltipProps';
import PropTypes from '../_util/vue-types';
import { getOptionProps, getComponent, getSlot } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider';
import { initDefaultProps } from '../_util/props-util';
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({
name: 'APopover',
props: {
...props,
prefixCls: PropTypes.string,
transitionName: PropTypes.string.def('zoom-big'),
content: PropTypes.any,
title: PropTypes.any,
},
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
};
},
methods: {
getPopupDomNode() {
return (this.$refs.tooltip as any).getPopupDomNode();
},
},
props: initDefaultProps(popoverProps(), {
trigger: 'hover',
transitionName: 'zoom-big',
placement: 'top',
mouseEnterDelay: 0.1,
mouseLeaveDelay: 0.1,
}),
setup(props, { expose, slots }) {
const tooltipRef = ref();
render() {
const { title, prefixCls: customizePrefixCls, $slots } = this;
const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('popover', customizePrefixCls);
const props = getOptionProps(this);
delete props.title;
delete props.content;
const tooltipProps = {
...props,
prefixCls,
ref: 'tooltip',
title: (
<div>
{(title || $slots.title) && (
<div class={`${prefixCls}-title`}>{getComponent(this, 'title')}</div>
)}
<div class={`${prefixCls}-inner-content`}>{getComponent(this, 'content')}</div>
</div>
),
expose({
getPopupDomNode: () => {
return tooltipRef.value?.getPopupDomNode?.();
},
});
const { prefixCls, configProvider } = useConfigInject('popover', props);
const rootPrefixCls = computed(() => configProvider.getPrefixCls());
const getOverlay = () => {
const { title = slots.title?.(), content = slots.content?.() } = props;
return (
<>
{title && <div class={`${prefixCls.value}-title`}>{title}</div>}
<div class={`${prefixCls.value}-inner-content`}>{content}</div>
</>
);
};
return () => {
return (
<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-arrow-rotate-width: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
@popover-arrow-offset-vertical: 12px;
@popover-arrow-offset-horizontal: 16px;
.@{popover-prefix-cls} {
.reset-component();
@ -68,16 +73,16 @@
&-title {
min-width: @popover-min-width;
min-height: 32px;
min-height: @popover-min-height;
margin: 0; // reset heading margin
padding: 5px @padding-md 4px;
padding: 5px @popover-padding-horizontal 4px;
color: @heading-color;
font-weight: 500;
border-bottom: 1px solid @border-color-split;
}
&-inner-content {
padding: 12px @padding-md;
padding: @padding-sm @popover-padding-horizontal;
color: @popover-color;
}
@ -88,7 +93,9 @@
font-size: @font-size-base;
> .@{iconfont-css-prefix} {
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;
font-size: @font-size-base;
}
@ -100,106 +107,146 @@
&-buttons {
margin-bottom: 4px;
text-align: right;
button {
margin-left: 8px;
}
}
// Arrows
// .popover-arrow is outer, .popover-arrow:after is inner
&-arrow {
position: absolute;
display: block;
width: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
height: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
width: @popover-arrow-rotate-width;
height: @popover-arrow-rotate-width;
overflow: hidden;
background: transparent;
border-style: solid;
border-width: (sqrt(@popover-arrow-width * @popover-arrow-width * 2) / 2);
transform: rotate(45deg);
pointer-events: none;
&-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-topLeft > &-content > &-arrow,
&-placement-topRight > &-content > &-arrow {
bottom: @popover-distance - @popover-arrow-width + 2.2px;
border-top-color: transparent;
border-right-color: @popover-bg;
border-bottom-color: @popover-bg;
border-left-color: transparent;
box-shadow: 3px 3px 7px fade(@black, 7%);
&-placement-top &-arrow,
&-placement-topLeft &-arrow,
&-placement-topRight &-arrow {
bottom: @popover-distance - @popover-arrow-rotate-width;
&-content {
box-shadow: 3px 3px 7px fade(@black, 7%);
transform: translateY((-@popover-arrow-rotate-width / 2)) rotate(45deg);
}
}
&-placement-top > &-content > &-arrow {
&-placement-top &-arrow {
left: 50%;
transform: translateX(-50%) rotate(45deg);
}
&-placement-topLeft > &-content > &-arrow {
left: 16px;
}
&-placement-topRight > &-content > &-arrow {
right: 16px;
transform: translateX(-50%);
}
&-placement-right > &-content > &-arrow,
&-placement-rightTop > &-content > &-arrow,
&-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-topLeft &-arrow {
left: @popover-arrow-offset-horizontal;
}
&-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%;
transform: translateY(-50%) rotate(45deg);
transform: translateY(-50%);
}
&-placement-rightTop > &-content > &-arrow {
top: 12px;
&-placement-rightTop &-arrow {
top: @popover-arrow-offset-vertical;
}
&-placement-rightBottom > &-content > &-arrow {
bottom: 12px;
&-placement-rightBottom &-arrow {
bottom: @popover-arrow-offset-vertical;
}
&-placement-bottom > &-content > &-arrow,
&-placement-bottomLeft > &-content > &-arrow,
&-placement-bottomRight > &-content > &-arrow {
top: @popover-distance - @popover-arrow-width + 2px;
border-top-color: @popover-bg;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: @popover-bg;
box-shadow: -2px -2px 5px fade(@black, 6%);
&-placement-bottom &-arrow,
&-placement-bottomLeft &-arrow,
&-placement-bottomRight &-arrow {
top: @popover-distance - @popover-arrow-rotate-width;
&-content {
box-shadow: -2px -2px 5px fade(@black, 6%);
transform: translateY((@popover-arrow-rotate-width / 2)) rotate(45deg);
}
}
&-placement-bottom > &-content > &-arrow {
&-placement-bottom &-arrow {
left: 50%;
transform: translateX(-50%) rotate(45deg);
}
&-placement-bottomLeft > &-content > &-arrow {
left: 16px;
}
&-placement-bottomRight > &-content > &-arrow {
right: 16px;
transform: translateX(-50%);
}
&-placement-left > &-content > &-arrow,
&-placement-leftTop > &-content > &-arrow,
&-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-bottomLeft &-arrow {
left: @popover-arrow-offset-horizontal;
}
&-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%;
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 maximum width
@popover-min-width: 177px;
@popover-min-height: 32px;
// Popover arrow width
@popover-arrow-width: 6px;
// Popover arrow color
@ -481,6 +482,7 @@
@popover-arrow-outer-color: @popover-bg;
// Popover distance with trigger
@popover-distance: @popover-arrow-width + 4px;
@popover-padding-horizontal: @padding-md;
// Modal
// --

View File

@ -5,7 +5,7 @@
</template>
<script>
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';
export default defineComponent({
components: {

2
v2-doc

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