Notification.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode } from "vue";
  4. var __rest = this && this.__rest || function (s, e) {
  5. var t = {};
  6. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
  7. if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  8. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
  9. }
  10. return t;
  11. };
  12. import { getTransitionGroupProps } from '../_util/transition';
  13. import { shallowRef, createVNode, computed, defineComponent, ref, TransitionGroup, onMounted, render as vueRender } from 'vue';
  14. import Notice from './Notice';
  15. import ConfigProvider, { globalConfigForApi } from '../config-provider';
  16. import classNames from '../_util/classNames';
  17. let seed = 0;
  18. const now = Date.now();
  19. function getUuid() {
  20. const id = seed;
  21. seed += 1;
  22. return `rcNotification_${now}_${id}`;
  23. }
  24. const Notification = defineComponent({
  25. name: 'Notification',
  26. inheritAttrs: false,
  27. props: ['prefixCls', 'transitionName', 'animation', 'maxCount', 'closeIcon', 'hashId'],
  28. setup(props, _ref) {
  29. let {
  30. attrs,
  31. expose,
  32. slots
  33. } = _ref;
  34. const hookRefs = new Map();
  35. const notices = ref([]);
  36. const transitionProps = computed(() => {
  37. const {
  38. prefixCls,
  39. animation = 'fade'
  40. } = props;
  41. let name = props.transitionName;
  42. if (!name && animation) {
  43. name = `${prefixCls}-${animation}`;
  44. }
  45. return getTransitionGroupProps(name);
  46. });
  47. const add = (originNotice, holderCallback) => {
  48. const key = originNotice.key || getUuid();
  49. const notice = _extends(_extends({}, originNotice), {
  50. key
  51. });
  52. const {
  53. maxCount
  54. } = props;
  55. const noticeIndex = notices.value.map(v => v.notice.key).indexOf(key);
  56. const updatedNotices = notices.value.concat();
  57. if (noticeIndex !== -1) {
  58. updatedNotices.splice(noticeIndex, 1, {
  59. notice,
  60. holderCallback
  61. });
  62. } else {
  63. if (maxCount && notices.value.length >= maxCount) {
  64. // XXX, use key of first item to update new added (let React to move exsiting
  65. // instead of remove and mount). Same key was used before for both a) external
  66. // manual control and b) internal react 'key' prop , which is not that good.
  67. // eslint-disable-next-line no-param-reassign
  68. // zombieJ: Not know why use `updateKey`. This makes Notice infinite loop in jest.
  69. // Change to `updateMark` for compare instead.
  70. // https://github.com/react-component/notification/commit/32299e6be396f94040bfa82517eea940db947ece
  71. notice.key = updatedNotices[0].notice.key;
  72. notice.updateMark = getUuid();
  73. // zombieJ: That's why. User may close by key directly.
  74. // We need record this but not re-render to avoid upper issue
  75. // https://github.com/react-component/notification/issues/129
  76. notice.userPassKey = key;
  77. updatedNotices.shift();
  78. }
  79. updatedNotices.push({
  80. notice,
  81. holderCallback
  82. });
  83. }
  84. notices.value = updatedNotices;
  85. };
  86. const remove = removeKey => {
  87. notices.value = notices.value.filter(_ref2 => {
  88. let {
  89. notice: {
  90. key,
  91. userPassKey
  92. }
  93. } = _ref2;
  94. const mergedKey = userPassKey || key;
  95. return mergedKey !== removeKey;
  96. });
  97. };
  98. expose({
  99. add,
  100. remove,
  101. notices
  102. });
  103. return () => {
  104. var _a;
  105. const {
  106. prefixCls,
  107. closeIcon = (_a = slots.closeIcon) === null || _a === void 0 ? void 0 : _a.call(slots, {
  108. prefixCls
  109. })
  110. } = props;
  111. const noticeNodes = notices.value.map((_ref3, index) => {
  112. let {
  113. notice,
  114. holderCallback
  115. } = _ref3;
  116. const updateMark = index === notices.value.length - 1 ? notice.updateMark : undefined;
  117. const {
  118. key,
  119. userPassKey
  120. } = notice;
  121. const {
  122. content
  123. } = notice;
  124. const noticeProps = _extends(_extends(_extends({
  125. prefixCls,
  126. closeIcon: typeof closeIcon === 'function' ? closeIcon({
  127. prefixCls
  128. }) : closeIcon
  129. }, notice), notice.props), {
  130. key,
  131. noticeKey: userPassKey || key,
  132. updateMark,
  133. onClose: noticeKey => {
  134. var _a;
  135. remove(noticeKey);
  136. (_a = notice.onClose) === null || _a === void 0 ? void 0 : _a.call(notice);
  137. },
  138. onClick: notice.onClick
  139. });
  140. if (holderCallback) {
  141. return _createVNode("div", {
  142. "key": key,
  143. "class": `${prefixCls}-hook-holder`,
  144. "ref": div => {
  145. if (typeof key === 'undefined') {
  146. return;
  147. }
  148. if (div) {
  149. hookRefs.set(key, div);
  150. holderCallback(div, noticeProps);
  151. } else {
  152. hookRefs.delete(key);
  153. }
  154. }
  155. }, null);
  156. }
  157. return _createVNode(Notice, _objectSpread(_objectSpread({}, noticeProps), {}, {
  158. "class": classNames(noticeProps.class, props.hashId)
  159. }), {
  160. default: () => [typeof content === 'function' ? content({
  161. prefixCls
  162. }) : content]
  163. });
  164. });
  165. const className = {
  166. [prefixCls]: 1,
  167. [attrs.class]: !!attrs.class,
  168. [props.hashId]: true
  169. };
  170. return _createVNode("div", {
  171. "class": className,
  172. "style": attrs.style || {
  173. top: '65px',
  174. left: '50%'
  175. }
  176. }, [_createVNode(TransitionGroup, _objectSpread({
  177. "tag": "div"
  178. }, transitionProps.value), {
  179. default: () => [noticeNodes]
  180. })]);
  181. };
  182. }
  183. });
  184. Notification.newInstance = function newNotificationInstance(properties, callback) {
  185. const _a = properties || {},
  186. {
  187. name = 'notification',
  188. getContainer,
  189. appContext,
  190. prefixCls: customizePrefixCls,
  191. rootPrefixCls: customRootPrefixCls,
  192. transitionName: customTransitionName,
  193. hasTransitionName,
  194. useStyle
  195. } = _a,
  196. props = __rest(_a, ["name", "getContainer", "appContext", "prefixCls", "rootPrefixCls", "transitionName", "hasTransitionName", "useStyle"]);
  197. const div = document.createElement('div');
  198. if (getContainer) {
  199. const root = getContainer();
  200. root.appendChild(div);
  201. } else {
  202. document.body.appendChild(div);
  203. }
  204. const Wrapper = defineComponent({
  205. compatConfig: {
  206. MODE: 3
  207. },
  208. name: 'NotificationWrapper',
  209. setup(_props, _ref4) {
  210. let {
  211. attrs
  212. } = _ref4;
  213. const notiRef = shallowRef();
  214. const prefixCls = computed(() => globalConfigForApi.getPrefixCls(name, customizePrefixCls));
  215. const [, hashId] = useStyle(prefixCls);
  216. onMounted(() => {
  217. callback({
  218. notice(noticeProps) {
  219. var _a;
  220. (_a = notiRef.value) === null || _a === void 0 ? void 0 : _a.add(noticeProps);
  221. },
  222. removeNotice(key) {
  223. var _a;
  224. (_a = notiRef.value) === null || _a === void 0 ? void 0 : _a.remove(key);
  225. },
  226. destroy() {
  227. vueRender(null, div);
  228. if (div.parentNode) {
  229. div.parentNode.removeChild(div);
  230. }
  231. },
  232. component: notiRef
  233. });
  234. });
  235. return () => {
  236. const global = globalConfigForApi;
  237. const rootPrefixCls = global.getRootPrefixCls(customRootPrefixCls, prefixCls.value);
  238. const transitionName = hasTransitionName ? customTransitionName : `${prefixCls.value}-${customTransitionName}`;
  239. return _createVNode(ConfigProvider, _objectSpread(_objectSpread({}, global), {}, {
  240. "prefixCls": rootPrefixCls
  241. }), {
  242. default: () => [_createVNode(Notification, _objectSpread(_objectSpread({
  243. "ref": notiRef
  244. }, attrs), {}, {
  245. "prefixCls": prefixCls.value,
  246. "transitionName": transitionName,
  247. "hashId": hashId.value
  248. }), null)]
  249. });
  250. };
  251. }
  252. });
  253. const vm = createVNode(Wrapper, props);
  254. vm.appContext = appContext || vm.appContext;
  255. vueRender(vm, div);
  256. };
  257. export default Notification;