From f6760bff270db75bc428281a20afe9d8ca2c7809 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 29 Dec 2021 21:23:50 +0800 Subject: [PATCH 1/4] refactor: notification --- components/notification/demo/custom-style.vue | 10 +- components/notification/style/index.less | 48 +++- .../style/{index.ts => index.tsx} | 0 components/notification/style/rtl.less | 53 ++++ components/vc-notification/Notice.jsx | 88 ------ components/vc-notification/Notice.tsx | 138 ++++++++++ components/vc-notification/Notification.jsx | 177 ------------ components/vc-notification/Notification.tsx | 251 ++++++++++++++++++ components/vc-notification/assets/index.less | 95 ------- .../vc-notification/{index.js => index.ts} | 3 +- 10 files changed, 488 insertions(+), 375 deletions(-) rename components/notification/style/{index.ts => index.tsx} (100%) create mode 100644 components/notification/style/rtl.less delete mode 100644 components/vc-notification/Notice.jsx create mode 100644 components/vc-notification/Notice.tsx delete mode 100644 components/vc-notification/Notification.jsx create mode 100644 components/vc-notification/Notification.tsx delete mode 100644 components/vc-notification/assets/index.less rename components/vc-notification/{index.js => index.ts} (67%) diff --git a/components/notification/demo/custom-style.vue b/components/notification/demo/custom-style.vue index 9fcac538b3..a053f7d75c 100644 --- a/components/notification/demo/custom-style.vue +++ b/components/notification/demo/custom-style.vue @@ -8,11 +8,11 @@ title: ## zh-CN -使用 style 和 className 来定义样式。 +使用 `style` 和 `class` 来定义样式。 ## en-US -The style and className are available to customize Notification. +The `style` and `class` are available to customize Notification. @@ -33,6 +33,7 @@ export default defineComponent({ width: '600px', marginLeft: `${335 - 600}px`, }, + class: 'notification-custom-class', }); }; return { @@ -41,3 +42,8 @@ export default defineComponent({ }, }); + diff --git a/components/notification/style/index.less b/components/notification/style/index.less index 04d7f24a4d..b85b73f55f 100644 --- a/components/notification/style/index.less +++ b/components/notification/style/index.less @@ -1,26 +1,25 @@ @import '../../style/themes/index'; @import '../../style/mixins/index'; +.popover-customize-bg(@notification-prefix-cls, @popover-background); + @notification-prefix-cls: ~'@{ant-prefix}-notification'; @notification-width: 384px; -@notification-padding-vertical: 16px; -@notification-padding-horizontal: 24px; @notification-padding: @notification-padding-vertical @notification-padding-horizontal; @notification-margin-bottom: 16px; +@notification-margin-edge: 24px; .@{notification-prefix-cls} { .reset-component(); position: fixed; z-index: @zindex-notification; - width: @notification-width; - max-width: ~'calc(100vw - 32px)'; - margin-right: 24px; + margin-right: @notification-margin-edge; &-topLeft, &-bottomLeft { margin-right: 0; - margin-left: 24px; + margin-left: @notification-margin-edge; .@{notification-prefix-cls}-fade-enter.@{notification-prefix-cls}-fade-enter-active, .@{notification-prefix-cls}-fade-appear.@{notification-prefix-cls}-fade-appear-active { @@ -33,18 +32,31 @@ cursor: pointer; } + &-hook-holder { + position: relative; + } + &-notice { position: relative; + width: @notification-width; + max-width: ~'calc(100vw - @{notification-margin-edge} * 2)'; margin-bottom: @notification-margin-bottom; + margin-left: auto; padding: @notification-padding; overflow: hidden; - line-height: 1.5; + line-height: @line-height-base; + word-wrap: break-word; background: @notification-bg; border-radius: @border-radius-base; box-shadow: @shadow-2; + .@{notification-prefix-cls}-topLeft &, + .@{notification-prefix-cls}-bottomLeft & { + margin-right: auto; + margin-left: 0; + } + &-message { - display: inline-block; margin-bottom: 8px; color: @heading-color; font-size: @font-size-lg; @@ -57,6 +69,7 @@ max-width: 4px; background-color: transparent; pointer-events: none; + &::before { display: block; content: ''; @@ -97,12 +110,15 @@ &-success { color: @success-color; } + &-info { color: @info-color; } + &-warning { color: @warning-color; } + &-error { color: @error-color; } @@ -116,7 +132,12 @@ outline: none; &:hover { - color: shade(@text-color-secondary, 40%); + & when (@theme = dark) { + color: fade(@white, 85%); + } + & when not (@theme = dark) { + color: shade(@text-color-secondary, 40%); + } } } @@ -134,9 +155,9 @@ &-fade-enter, &-fade-appear { - opacity: 0; .notification-fade-effect(); + opacity: 0; animation-play-state: paused; } @@ -164,6 +185,7 @@ left: @notification-width; opacity: 0; } + 100% { left: 0; opacity: 1; @@ -175,6 +197,7 @@ right: @notification-width; opacity: 0; } + 100% { right: 0; opacity: 1; @@ -185,10 +208,9 @@ 0% { max-height: 150px; margin-bottom: @notification-margin-bottom; - padding-top: @notification-padding; - padding-bottom: @notification-padding; opacity: 1; } + 100% { max-height: 0; margin-bottom: 0; @@ -197,3 +219,5 @@ opacity: 0; } } + +@import './rtl'; diff --git a/components/notification/style/index.ts b/components/notification/style/index.tsx similarity index 100% rename from components/notification/style/index.ts rename to components/notification/style/index.tsx diff --git a/components/notification/style/rtl.less b/components/notification/style/rtl.less new file mode 100644 index 0000000000..9285a25cc5 --- /dev/null +++ b/components/notification/style/rtl.less @@ -0,0 +1,53 @@ +@import '../../style/themes/index'; +@import '../../style/mixins/index'; + +@notification-prefix-cls: ~'@{ant-prefix}-notification'; + +.@{notification-prefix-cls} { + &-rtl { + direction: rtl; + } + + &-notice { + &-closable &-message { + .@{notification-prefix-cls}-rtl & { + padding-right: 0; + padding-left: 24px; + } + } + + &-with-icon &-message { + .@{notification-prefix-cls}-rtl & { + margin-right: 48px; + margin-left: 0; + } + } + + &-with-icon &-description { + .@{notification-prefix-cls}-rtl & { + margin-right: 48px; + margin-left: 0; + } + } + + &-icon { + .@{notification-prefix-cls}-rtl & { + margin-right: 4px; + margin-left: 0; + } + } + + &-close { + .@{notification-prefix-cls}-rtl & { + right: auto; + left: 22px; + } + } + + &-btn { + .@{notification-prefix-cls}-rtl & { + float: left; + } + } + } +} diff --git a/components/vc-notification/Notice.jsx b/components/vc-notification/Notice.jsx deleted file mode 100644 index 0a6f2346c8..0000000000 --- a/components/vc-notification/Notice.jsx +++ /dev/null @@ -1,88 +0,0 @@ -import PropTypes from '../_util/vue-types'; -import { getComponent, getSlot } from '../_util/props-util'; -import BaseMixin from '../_util/BaseMixin'; - -export default { - mixins: [BaseMixin], - props: { - duration: PropTypes.number.def(1.5), - closable: PropTypes.looseBool, - prefixCls: PropTypes.string, - update: PropTypes.looseBool, - closeIcon: PropTypes.any, - onClose: PropTypes.func, - }, - watch: { - duration() { - this.restartCloseTimer(); - }, - }, - - mounted() { - this.startCloseTimer(); - }, - updated() { - if (this.update) { - this.restartCloseTimer(); - } - }, - - beforeUnmount() { - this.clearCloseTimer(); - this.willDestroy = true; // beforeUnmount调用后依然会触发onMouseleave事件 - }, - methods: { - close(e) { - if (e) { - e.stopPropagation(); - } - this.clearCloseTimer(); - this.__emit('close'); - }, - - startCloseTimer() { - this.clearCloseTimer(); - if (!this.willDestroy && this.duration) { - this.closeTimer = setTimeout(() => { - this.close(); - }, this.duration * 1000); - } - }, - - clearCloseTimer() { - if (this.closeTimer) { - clearTimeout(this.closeTimer); - this.closeTimer = null; - } - }, - restartCloseTimer() { - this.clearCloseTimer(); - this.startCloseTimer(); - }, - }, - - render() { - const { prefixCls, closable, clearCloseTimer, startCloseTimer, close, $attrs } = this; - const componentClass = `${prefixCls}-notice`; - const className = { - [`${componentClass}`]: 1, - [`${componentClass}-closable`]: closable, - }; - const closeIcon = getComponent(this, 'closeIcon'); - return ( -
-
{getSlot(this)}
- {closable ? ( - - {closeIcon || } - - ) : null} -
- ); - }, -}; diff --git a/components/vc-notification/Notice.tsx b/components/vc-notification/Notice.tsx new file mode 100644 index 0000000000..e29039a88b --- /dev/null +++ b/components/vc-notification/Notice.tsx @@ -0,0 +1,138 @@ +import type { Key } from '../_util/type'; +import { Teleport, computed, defineComponent, onMounted, watch, onUnmounted } from 'vue'; +import type { HTMLAttributes } from 'vue'; +import type { MouseEventHandler } from '../_util/EventInterface'; +import classNames from '../_util/classNames'; + +interface DivProps extends HTMLAttributes { + // Ideally we would allow all data-* props but this would depend on https://github.com/microsoft/TypeScript/issues/28960 + 'data-testid'?: string; +} + +export interface NoticeProps { + prefixCls: string; + duration?: number | null; + updateMark?: string; + /** Mark as final key since set maxCount may keep the key but user pass key is different */ + noticeKey: Key; + closeIcon?: any; + closable?: boolean; + props?: DivProps; + onClick?: MouseEventHandler; + onClose?: (key: Key) => void; + + /** @private Only for internal usage. We don't promise that we will refactor this */ + holder?: HTMLDivElement; + + /** @private Provided by CSSMotionList */ + visible?: boolean; +} + +export default defineComponent({ + name: 'Notice', + inheritAttrs: false, + props: [ + 'prefixCls', + 'duration', + 'updateMark', + 'noticeKey', + 'closeIcon', + 'closable', + 'props', + 'onClick', + 'onClose', + 'holder', + 'visible', + ] as any, + setup(props, { attrs, slots }) { + let closeTimer: any; + const duration = computed(() => (props.duration === undefined ? 1.5 : props.duration)); + const startCloseTimer = () => { + if (duration.value) { + closeTimer = setTimeout(() => { + close(); + }, duration.value * 1000); + } + }; + + const clearCloseTimer = () => { + if (closeTimer) { + clearTimeout(closeTimer); + closeTimer = null; + } + }; + const close = (e?: MouseEvent) => { + if (e) { + e.stopPropagation(); + } + clearCloseTimer(); + const { onClose, noticeKey } = props; + if (onClose) { + onClose(noticeKey); + } + }; + const restartCloseTimer = () => { + clearCloseTimer(); + startCloseTimer(); + }; + onMounted(() => { + startCloseTimer(); + }); + onUnmounted(() => { + clearCloseTimer(); + }); + + watch( + [duration, () => props.updateMark, () => props.visible], + ([preDuration, preUpdateMark, preVisible], [newDuration, newUpdateMark, newVisible]) => { + if ( + preDuration !== newDuration || + preUpdateMark !== newUpdateMark || + (preVisible !== newVisible && newVisible) + ) { + restartCloseTimer(); + } + }, + { flush: 'post' }, + ); + return () => { + const { prefixCls, closable, closeIcon = slots.closeIcon?.(), onClick, holder } = props; + const { class: className, style } = attrs; + const componentClass = `${prefixCls}-notice`; + const dataOrAriaAttributeProps = Object.keys(attrs).reduce( + (acc: Record, key: string) => { + if (key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-' || key === 'role') { + acc[key] = (attrs as any)[key]; + } + return acc; + }, + {}, + ); + const node = ( +
+
{slots.default?.()}
+ {closable ? ( + + {closeIcon || } + + ) : null} +
+ ); + + if (holder) { + return node }}>; + } + + return node; + }; + }, +}); diff --git a/components/vc-notification/Notification.jsx b/components/vc-notification/Notification.jsx deleted file mode 100644 index 374ec9cc71..0000000000 --- a/components/vc-notification/Notification.jsx +++ /dev/null @@ -1,177 +0,0 @@ -import { defineComponent, createVNode, render as vueRender, onMounted, ref } from 'vue'; -import PropTypes from '../_util/vue-types'; -import { getComponent } from '../_util/props-util'; -import BaseMixin from '../_util/BaseMixin'; -import createChainedFunction from '../_util/createChainedFunction'; -import Notice from './Notice'; -import { getTransitionGroupProps, TransitionGroup } from '../_util/transition'; -import ConfigProvider, { globalConfigForApi } from '../config-provider'; - -function noop() {} - -let seed = 0; -const now = Date.now(); - -function getUuid() { - return `rcNotification_${now}_${seed++}`; -} - -const Notification = defineComponent({ - mixins: [BaseMixin], - props: { - prefixCls: PropTypes.string.def('rc-notification'), - transitionName: PropTypes.string, - animation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).def('fade'), - maxCount: PropTypes.number, - closeIcon: PropTypes.any, - }, - data() { - return { - notices: [], - }; - }, - methods: { - getTransitionName() { - const props = this.$props; - let transitionName = props.transitionName; - if (!transitionName && props.animation) { - transitionName = `${props.prefixCls}-${props.animation}`; - } - return transitionName; - }, - - add(notice) { - const key = (notice.key = notice.key || getUuid()); - const { maxCount } = this.$props; - this.setState(previousState => { - const notices = previousState.notices; - const noticeIndex = notices.map(v => v.key).indexOf(key); - const updatedNotices = notices.concat(); - if (noticeIndex !== -1) { - updatedNotices.splice(noticeIndex, 1, notice); - } else { - if (maxCount && notices.length >= maxCount) { - // XXX, use key of first item to update new added (let React to move exsiting - // instead of remove and mount). Same key was used before for both a) external - // manual control and b) internal react 'key' prop , which is not that good. - notice.updateKey = updatedNotices[0].updateKey || updatedNotices[0].key; - updatedNotices.shift(); - } - updatedNotices.push(notice); - } - return { - notices: updatedNotices, - }; - }); - }, - - remove(key) { - this.setState(previousState => { - return { - notices: previousState.notices.filter(notice => notice.key !== key), - }; - }); - }, - }, - - render() { - const { prefixCls, notices, remove, getTransitionName, $attrs } = this; - const transitionProps = getTransitionGroupProps(getTransitionName()); - const noticeNodes = notices.map((notice, index) => { - const update = Boolean(index === notices.length - 1 && notice.updateKey); - const key = notice.updateKey ? notice.updateKey : notice.key; - - const { content, duration, closable, onClose, style, class: className } = notice; - const close = createChainedFunction(remove.bind(this, notice.key), onClose); - const noticeProps = { - prefixCls, - duration, - closable, - update, - closeIcon: getComponent(this, 'closeIcon', { prefixCls }), - onClose: close, - onClick: notice.onClick || noop, - style, - class: className, - key, - }; - return ( - - {typeof content === 'function' ? content({ prefixCls }) : content} - - ); - }); - const className = { - [prefixCls]: 1, - }; - return ( -
- - {noticeNodes} - -
- ); - }, -}); - -Notification.newInstance = function newNotificationInstance(properties, callback) { - const { - name = 'notification', - getContainer, - appContext, - prefixCls: customizePrefixCls, - rootPrefixCls: customRootPrefixCls, - ...props - } = properties || {}; - const div = document.createElement('div'); - if (getContainer) { - const root = getContainer(); - root.appendChild(div); - } else { - document.body.appendChild(div); - } - const Wrapper = defineComponent({ - setup(_props, { attrs }) { - const notiRef = ref(); - onMounted(() => { - callback({ - notice(noticeProps) { - notiRef.value?.add(noticeProps); - }, - removeNotice(key) { - notiRef.value?.remove(key); - }, - destroy() { - vueRender(null, div); - if (div.parentNode) { - div.parentNode.removeChild(div); - } - }, - }); - }); - return () => { - const global = globalConfigForApi; - const prefixCls = global.getPrefixCls(name, customizePrefixCls); - const rootPrefixCls = global.getRootPrefixCls(customRootPrefixCls, prefixCls); - return ( - - - - ); - }; - }, - }); - - const vm = createVNode(Wrapper, props); - vm.appContext = appContext || vm.appContext; - vueRender(vm, div); -}; -export default Notification; diff --git a/components/vc-notification/Notification.tsx b/components/vc-notification/Notification.tsx new file mode 100644 index 0000000000..77f06022a3 --- /dev/null +++ b/components/vc-notification/Notification.tsx @@ -0,0 +1,251 @@ +import { getTransitionGroupProps } from 'ant-design-vue/es/_util/transition'; +import type { Key } from 'ant-design-vue/es/_util/type'; +import type { CSSProperties } from 'vue'; +import { + createVNode, + computed, + defineComponent, + ref, + TransitionGroup, + onMounted, + render as vueRender, +} from 'vue'; +import type { NoticeProps } from './Notice'; +import Notice from './Notice'; +import ConfigProvider, { globalConfigForApi } from '../config-provider'; + +let seed = 0; +const now = Date.now(); + +function getUuid() { + const id = seed; + seed += 1; + return `rcNotification_${now}_${id}`; +} + +export interface NoticeContent extends Omit { + prefixCls?: string; + key?: Key; + updateMark?: string; + content?: any; + onClose?: () => void; + style?: CSSProperties; + class?: String; +} + +export type NoticeFunc = (noticeProps: NoticeContent) => void; +export type HolderReadyCallback = ( + div: HTMLDivElement, + noticeProps: NoticeProps & { key: Key }, +) => void; + +export interface NotificationInstance { + notice: NoticeFunc; + removeNotice: (key: Key) => void; + destroy: () => void; + component: Notification; + + useNotification: () => [NoticeFunc, any]; +} + +export interface NotificationProps { + prefixCls?: string; + transitionName?: string; + animation?: string | object; + maxCount?: number; + closeIcon?: any; +} + +type NotificationState = { + notice: NoticeContent & { + userPassKey?: Key; + }; + holderCallback?: HolderReadyCallback; +}[]; + +const Notification = defineComponent({ + name: 'Notification', + props: ['prefixCls', 'transitionName', 'animation', 'maxCount', 'closeIcon'] as any, + setup(props, { attrs, expose, slots }) { + const hookRefs = new Map(); + const notices = ref([]); + const transitionProps = computed(() => { + const { prefixCls, animation = 'fade' } = props; + let name = props.transitionName; + if (!name && animation) { + name = `${prefixCls}-${animation}`; + } + return getTransitionGroupProps(name); + }); + + const add = (originNotice: NoticeContent, holderCallback?: HolderReadyCallback) => { + const key = originNotice.key || getUuid(); + const notice: NoticeContent & { key: Key; userPassKey?: Key } = { + ...originNotice, + key, + }; + const { maxCount } = props; + const noticeIndex = notices.value.map(v => v.notice.key).indexOf(key); + const updatedNotices = notices.value.concat(); + if (noticeIndex !== -1) { + updatedNotices.splice(noticeIndex, 1, { notice, holderCallback } as any); + } else { + if (maxCount && notices.value.length >= maxCount) { + // XXX, use key of first item to update new added (let React to move exsiting + // instead of remove and mount). Same key was used before for both a) external + // manual control and b) internal react 'key' prop , which is not that good. + // eslint-disable-next-line no-param-reassign + + // zombieJ: Not know why use `updateKey`. This makes Notice infinite loop in jest. + // Change to `updateMark` for compare instead. + // https://github.com/react-component/notification/commit/32299e6be396f94040bfa82517eea940db947ece + notice.key = updatedNotices[0].notice.key as Key; + notice.updateMark = getUuid(); + + // zombieJ: That's why. User may close by key directly. + // We need record this but not re-render to avoid upper issue + // https://github.com/react-component/notification/issues/129 + notice.userPassKey = key; + + updatedNotices.shift(); + } + updatedNotices.push({ notice, holderCallback } as any); + } + notices.value = updatedNotices; + }; + + const remove = (removeKey: Key) => { + notices.value = notices.value.filter(({ notice: { key, userPassKey } }) => { + const mergedKey = userPassKey || key; + return mergedKey !== removeKey; + }); + }; + expose({ + add, + remove, + }); + return () => { + const { prefixCls, closeIcon = slots.closeIcon?.() } = props; + const noticeNodes = notices.value.map(({ notice, holderCallback }, index) => { + const updateMark = index === notices.value.length - 1 ? notice.updateMark : undefined; + const { key, userPassKey } = notice; + + const { content } = notice; + const noticeProps = { + prefixCls, + closeIcon: typeof closeIcon === 'function' ? closeIcon({ prefixCls }) : closeIcon, + ...(notice as any), + ...notice.props, + key, + noticeKey: userPassKey || key, + updateMark, + onClose: (noticeKey: Key) => { + remove(noticeKey); + notice.onClose?.(); + }, + onClick: notice.onClick, + }; + if (holderCallback) { + return ( +
{ + if (typeof key === 'undefined') { + return; + } + + if (div) { + hookRefs.set(key, div); + holderCallback(div, noticeProps); + } else { + hookRefs.delete(key); + } + }} + /> + ); + } + return ( + + {typeof content === 'function' ? content({ prefixCls }) : content} + + ); + }); + const className = { + [prefixCls]: 1, + [attrs.class as string]: !!attrs.class, + }; + return ( +
+ + {noticeNodes} + +
+ ); + }; + }, +}); + +Notification.newInstance = function newNotificationInstance(properties, callback) { + const { + name = 'notification', + getContainer, + appContext, + prefixCls: customizePrefixCls, + rootPrefixCls: customRootPrefixCls, + ...props + } = properties || {}; + const div = document.createElement('div'); + if (getContainer) { + const root = getContainer(); + root.appendChild(div); + } else { + document.body.appendChild(div); + } + const Wrapper = defineComponent({ + name: 'NotificationWrapper', + setup(_props, { attrs }) { + const notiRef = ref(); + onMounted(() => { + callback({ + notice(noticeProps: NoticeContent) { + notiRef.value?.add(noticeProps); + }, + removeNotice(key: Key) { + notiRef.value?.remove(key); + }, + destroy() { + vueRender(null, div); + if (div.parentNode) { + div.parentNode.removeChild(div); + } + }, + }); + }); + return () => { + const global = globalConfigForApi; + const prefixCls = global.getPrefixCls(name, customizePrefixCls); + const rootPrefixCls = global.getRootPrefixCls(customRootPrefixCls, prefixCls); + return ( + + + + ); + }; + }, + }); + + const vm = createVNode(Wrapper, props); + vm.appContext = appContext || vm.appContext; + vueRender(vm, div); +}; + +export default Notification; diff --git a/components/vc-notification/assets/index.less b/components/vc-notification/assets/index.less deleted file mode 100644 index c020b681c3..0000000000 --- a/components/vc-notification/assets/index.less +++ /dev/null @@ -1,95 +0,0 @@ -@notificationPrefixCls: rc-notification; - -.@{notificationPrefixCls} { - position: fixed; - z-index: 1000; - - &-notice { - padding: 7px 20px 7px 10px; - border-radius: 3px 3px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); - border: 0px solid rgba(0, 0, 0, 0); - background: #fff; - display: block; - width: auto; - line-height: 1.5; - vertical-align: middle; - position: relative; - margin: 10px 0; - - &-closable { - padding-right: 20px; - } - - &-close { - position: absolute; - right: 5px; - top: 3px; - color: #000; - cursor: pointer; - outline: none; - font-size: 16px; - font-weight: 700; - line-height: 1; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: 0.2; - text-decoration: none; - - &-x:after { - content: '×'; - } - - &:hover { - opacity: 1; - filter: alpha(opacity=100); - text-decoration: none; - } - } - } - - .fade-effect() { - animation-duration: 0.3s; - animation-fill-mode: both; - animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2); - } - - &-fade-enter { - opacity: 0; - .fade-effect(); - animation-play-state: paused; - } - - &-fade-leave { - .fade-effect(); - animation-play-state: paused; - } - - &-fade-enter&-fade-enter-active { - animation-name: rcNotificationFadeIn; - animation-play-state: running; - } - - &-fade-leave&-fade-leave-active { - animation-name: rcDialogFadeOut; - animation-play-state: running; - } - - @keyframes rcNotificationFadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - - @keyframes rcDialogFadeOut { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } - } -} diff --git a/components/vc-notification/index.js b/components/vc-notification/index.ts similarity index 67% rename from components/vc-notification/index.js rename to components/vc-notification/index.ts index fdaa1bbe8e..759e48825f 100644 --- a/components/vc-notification/index.js +++ b/components/vc-notification/index.ts @@ -1,3 +1,4 @@ -// based on rc-notification 3.3.1 +// based on rc-notification 4.5.7 import Notification from './Notification'; + export default Notification; From f92e75a1d935ee74643194ff5864841b9b8828f1 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 30 Dec 2021 10:57:15 +0800 Subject: [PATCH 2/4] refactor: message --- components/message/demo/custom-style.vue | 46 +++++ components/message/demo/index.vue | 3 + components/message/demo/update.vue | 22 ++- components/message/index.en-US.md | 26 ++- components/message/index.tsx | 159 +++++++++++------- components/message/index.zh-CN.md | 46 +++-- components/message/style/index.less | 11 +- .../message/style/{index.ts => index.tsx} | 0 components/message/style/rtl.less | 17 ++ components/vc-notification/Notification.tsx | 18 +- 10 files changed, 248 insertions(+), 100 deletions(-) create mode 100644 components/message/demo/custom-style.vue rename components/message/style/{index.ts => index.tsx} (100%) create mode 100644 components/message/style/rtl.less diff --git a/components/message/demo/custom-style.vue b/components/message/demo/custom-style.vue new file mode 100644 index 0000000000..3c70c80d34 --- /dev/null +++ b/components/message/demo/custom-style.vue @@ -0,0 +1,46 @@ + +--- +order: 6 +title: + zh-CN: 自定义样式 + en-US: Customized style +--- + +## zh-CN + +使用 `style` 和 `class` 来定义样式。 + +## en-US + +The `style` and `class` are available to customize Message. + + + + + + diff --git a/components/message/demo/index.vue b/components/message/demo/index.vue index 8cc38ab40d..6ad3af47cf 100644 --- a/components/message/demo/index.vue +++ b/components/message/demo/index.vue @@ -6,6 +6,7 @@ +