UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

259 lines 8.54 kB
import React from 'react'; import ReactDOM from 'react-dom'; import cls from 'classnames'; import PropTypes from 'prop-types'; import ConfigContext from '../configProvider/context'; import NotificationListFoundation from '@douyinfe/semi-foundation/lib/es/notification/notificationListFoundation'; import { cssClasses, strings } from '@douyinfe/semi-foundation/lib/es/notification/constants'; import Notice from './notice'; import BaseComponent from '../_base/baseComponent'; import '@douyinfe/semi-foundation/lib/es/notification/notification.css'; import getUuid from '@douyinfe/semi-foundation/lib/es/utils/uuid'; import useNotification from './useNotification'; import CSSAnimation from "../_cssAnimation"; let ref = null; const defaultConfig = { duration: 3, position: 'topRight', motion: true, content: '', title: '', zIndex: 1010 }; class NotificationList extends BaseComponent { constructor(props) { var _this; super(props); _this = this; this.add = noticeOpts => this.foundation.addNotice(noticeOpts); this.has = id => this.foundation.has(id); this.remove = id => { this.foundation.removeNotice(String(id)); }; this.update = (id, opts) => { return this.foundation.update(id, opts); }; this.destroyAll = () => this.foundation.destroyAll(); this.renderNoticeInPosition = function (notices, position) { let removedItems = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; let updatedItems = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; const className = cls(cssClasses.LIST); // TODO notifyOnClose if (notices.length) { const style = _this.setPosInStyle(notices[0]); return ( /*#__PURE__*/ // @ts-ignore React.createElement("div", { placement: position, key: position, className: className, style: style }, notices.map((notice, index) => { const isRemoved = removedItems.find(removedItem => removedItem.id === notice.id) !== undefined; return /*#__PURE__*/React.createElement(CSSAnimation, { key: notice.id, animationState: isRemoved ? "leave" : "enter", startClassName: `${cssClasses.NOTICE}-animation-${isRemoved ? "hide" : "show"}_${position}` }, _ref => { let { animationClassName, animationEventsNeedBind, isAnimating } = _ref; return isRemoved && !isAnimating ? null : /*#__PURE__*/React.createElement(Notice, Object.assign({}, notice, { ref: notice => { if (notice && updatedItems.some(item => item.id === notice.props.id)) { notice.foundation.restartCloseTimer(); } }, className: cls({ [notice.className]: Boolean(notice.className), [animationClassName]: true }) }, animationEventsNeedBind, { style: Object.assign({}, notice.style), close: _this.remove })); }); })) ); } return null; }; this.state = { notices: [], removedItems: [], updatedItems: [] }; this.noticeStorage = []; this.removeItemStorage = []; this.foundation = new NotificationListFoundation(this.adapter); } get adapter() { var _this2 = this; return Object.assign(Object.assign({}, super.adapter), { updateNotices: function (notices) { let removedItems = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; let updatedItems = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; _this2.noticeStorage = [...notices]; _this2.removeItemStorage = [...removedItems]; // setState is async sometimes and react often merges state, so use "this" , make sure other code always get right data. _this2.setState({ notices, removedItems, updatedItems }); }, getNotices: () => this.noticeStorage }); } static addNotice(notice) { var _a; notice = Object.assign(Object.assign({}, defaultConfig), notice); const id = (_a = notice.id) !== null && _a !== void 0 ? _a : getUuid('notification'); if (!ref) { const { getPopupContainer } = notice; const div = document.createElement('div'); if (!this.wrapperId) { this.wrapperId = getUuid('notification-wrapper').slice(0, 32); } div.className = cssClasses.WRAPPER; div.id = this.wrapperId; div.style.zIndex = String(typeof notice.zIndex === 'number' ? notice.zIndex : defaultConfig.zIndex); if (getPopupContainer) { const container = getPopupContainer(); container.appendChild(div); } else { document.body.appendChild(div); } ReactDOM.render(/*#__PURE__*/React.createElement(NotificationList, { ref: instance => ref = instance }), div, () => { ref.add(Object.assign(Object.assign({}, notice), { id })); }); } else { if (ref.has(`${id}`)) { ref.update(id, notice); } else { ref.add(Object.assign(Object.assign({}, notice), { id })); } } return id; } static removeNotice(id) { if (ref) { ref.remove(id); } return id; } static info(opts) { return this.addNotice(Object.assign(Object.assign({}, opts), { type: 'info' })); } static success(opts) { return this.addNotice(Object.assign(Object.assign({}, opts), { type: 'success' })); } static error(opts) { return this.addNotice(Object.assign(Object.assign({}, opts), { type: 'error' })); } static warning(opts) { return this.addNotice(Object.assign(Object.assign({}, opts), { type: 'warning' })); } static open(opts) { return this.addNotice(Object.assign(Object.assign({}, opts), { type: 'default' })); } static close(id) { return this.removeNotice(id); } static destroyAll() { if (ref) { ref.destroyAll(); const wrapper = document.querySelector(`#${this.wrapperId}`); ReactDOM.unmountComponentAtNode(wrapper); wrapper && wrapper.parentNode.removeChild(wrapper); ref = null; this.wrapperId = null; } } static config(opts) { ['top', 'left', 'bottom', 'right'].map(pos => { if (pos in opts) { defaultConfig[pos] = opts[pos]; } }); if (typeof opts.zIndex === 'number') { defaultConfig.zIndex = opts.zIndex; } if (typeof opts.duration === 'number') { defaultConfig.duration = opts.duration; } if (typeof opts.position === 'string') { defaultConfig.position = opts.position; } } setPosInStyle(noticeInstance) { const style = {}; ['top', 'left', 'bottom', 'right'].forEach(pos => { if (pos in noticeInstance) { const val = noticeInstance[pos]; style[pos] = typeof val === 'number' ? `${val}px` : val; } }); return style; } render() { let { notices } = this.state; const { removedItems, updatedItems } = this.state; notices = Array.from(new Set([...notices, ...removedItems])); const noticesInPosition = { top: [], topLeft: [], topRight: [], bottom: [], bottomLeft: [], bottomRight: [] }; notices.forEach(notice => { const direction = notice.direction || this.context.direction; const defaultPosition = direction === 'rtl' ? 'topLeft' : 'topRight'; const position = notice.position || defaultPosition; noticesInPosition[position].push(notice); }); const noticesList = Object.entries(noticesInPosition).map(obj => { const pos = obj[0]; const noticesInPos = obj[1]; return this.renderNoticeInPosition(noticesInPos, pos, removedItems, updatedItems); }); return /*#__PURE__*/React.createElement(React.Fragment, null, noticesList); } } NotificationList.contextType = ConfigContext; NotificationList.propTypes = { style: PropTypes.object, className: PropTypes.string, direction: PropTypes.oneOf(strings.directions) }; NotificationList.defaultProps = {}; NotificationList.useNotification = useNotification; export default NotificationList;