index.tsx 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import React, { ReactElement, ReactNode, useState } from 'react';
  2. import cls from 'classnames';
  3. import { cssClasses } from '@douyinfe/semi-foundation/notification/constants';
  4. import HookNotice from './HookNotice';
  5. import '@douyinfe/semi-foundation/notification/notification.scss';
  6. import getUuid from '@douyinfe/semi-foundation/utils/uuid';
  7. import { NoticeInstance, NoticePosition, NoticeProps } from '@douyinfe/semi-foundation/notification/notificationFoundation';
  8. import { NoticesInPosition } from '@douyinfe/semi-ui/notification/index';
  9. // TODO: Automatic folding + unfolding function when there are more than N
  10. const defaultConfig = {
  11. duration: 3,
  12. position: 'topRight' as NoticePosition,
  13. motion: true,
  14. content: '',
  15. title: '',
  16. zIndex: 1010,
  17. };
  18. function usePatchElement(): [ReactNode[], typeof patchElement] {
  19. const [elements, setElements] = useState([]);
  20. function patchElement(element: ReactElement, config: NoticeInstance) {
  21. setElements(originElements => [{ element, config }, ...originElements]);
  22. return (id: string) => {
  23. setElements(originElements => originElements.filter(({ config: configOfCurrentElement }) => configOfCurrentElement.id !== id));
  24. };
  25. }
  26. function renderList() {
  27. const noticesInPosition: NoticesInPosition = {
  28. top: [],
  29. topLeft: [],
  30. topRight: [],
  31. bottom: [],
  32. bottomLeft: [],
  33. bottomRight: [],
  34. };
  35. elements.forEach(({ element, config }) => {
  36. const { position } = config;
  37. noticesInPosition[position].push(element);
  38. });
  39. return Object.entries(noticesInPosition).map(obj => {
  40. const pos = obj[0];
  41. const notices = obj[1];
  42. // @ts-ignore
  43. return Array.isArray(notices) && notices.length ? <div key={pos} className={cls(cssClasses.LIST)} placement={pos}>{notices}</div> : null;
  44. });
  45. }
  46. return [
  47. renderList(),
  48. patchElement,
  49. ];
  50. }
  51. export default function useNotification() {
  52. const [elements, patchElement] = usePatchElement();
  53. const noticeRef = new Map<string, { close: () => void } & ReactElement>();
  54. const addNotice = (config: NoticeProps) => {
  55. const id = getUuid('semi_notice_');
  56. const mergeConfig = {
  57. ...config,
  58. id,
  59. };
  60. // eslint-disable-next-line prefer-const
  61. let closeFunc: ReturnType<typeof patchElement>;
  62. const ref = (ele: { close: () => void } & ReactElement) => {
  63. noticeRef.set(id, ele);
  64. };
  65. const notice = <HookNotice key={id} {...mergeConfig} afterClose={(instanceID: string) => closeFunc(instanceID)} ref={ref} />;
  66. closeFunc = patchElement(notice, { ...mergeConfig });
  67. return id;
  68. };
  69. const removeElement = (instanceID: string) => {
  70. const ele = noticeRef.get(instanceID);
  71. ele && ele.close();
  72. };
  73. return [
  74. {
  75. success: (config: NoticeProps) => addNotice({ ...defaultConfig, ...config, type: 'success' }),
  76. info: (config: NoticeProps) => addNotice({ ...defaultConfig, ...config, type: 'info' }),
  77. error: (config: NoticeProps) => addNotice({ ...defaultConfig, ...config, type: 'error' }),
  78. warning: (config: NoticeProps) => addNotice({ ...defaultConfig, ...config, type: 'warning' }),
  79. open: (config: NoticeProps) => addNotice({ ...defaultConfig, ...config, type: 'default' }),
  80. close: removeElement,
  81. },
  82. <>{elements}</>,
  83. ];
  84. }