index.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. "use client";
  2. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  3. import React, { useContext } from 'react';
  4. import { AppConfigContext } from '../app/context';
  5. import ConfigProvider, { ConfigContext, globalConfig, warnContext } from '../config-provider';
  6. import { unstableSetRender } from '../config-provider/UnstableContext';
  7. import PurePanel from './PurePanel';
  8. import useMessage, { useInternalMessage } from './useMessage';
  9. import { wrapPromiseFn } from './util';
  10. let message = null;
  11. let act = callback => callback();
  12. let taskQueue = [];
  13. let defaultGlobalConfig = {};
  14. function getGlobalContext() {
  15. const {
  16. getContainer,
  17. duration,
  18. rtl,
  19. maxCount,
  20. top
  21. } = defaultGlobalConfig;
  22. const mergedContainer = (getContainer === null || getContainer === void 0 ? void 0 : getContainer()) || document.body;
  23. return {
  24. getContainer: () => mergedContainer,
  25. duration,
  26. rtl,
  27. maxCount,
  28. top
  29. };
  30. }
  31. const GlobalHolder = /*#__PURE__*/React.forwardRef((props, ref) => {
  32. const {
  33. messageConfig,
  34. sync
  35. } = props;
  36. const {
  37. getPrefixCls
  38. } = useContext(ConfigContext);
  39. const prefixCls = defaultGlobalConfig.prefixCls || getPrefixCls('message');
  40. const appConfig = useContext(AppConfigContext);
  41. const [api, holder] = useInternalMessage(Object.assign(Object.assign(Object.assign({}, messageConfig), {
  42. prefixCls
  43. }), appConfig.message));
  44. React.useImperativeHandle(ref, () => {
  45. const instance = Object.assign({}, api);
  46. Object.keys(instance).forEach(method => {
  47. instance[method] = (...args) => {
  48. sync();
  49. return api[method].apply(api, args);
  50. };
  51. });
  52. return {
  53. instance,
  54. sync
  55. };
  56. });
  57. return holder;
  58. });
  59. const GlobalHolderWrapper = /*#__PURE__*/React.forwardRef((_, ref) => {
  60. const [messageConfig, setMessageConfig] = React.useState(getGlobalContext);
  61. const sync = () => {
  62. setMessageConfig(getGlobalContext);
  63. };
  64. React.useEffect(sync, []);
  65. const global = globalConfig();
  66. const rootPrefixCls = global.getRootPrefixCls();
  67. const rootIconPrefixCls = global.getIconPrefixCls();
  68. const theme = global.getTheme();
  69. const dom = /*#__PURE__*/React.createElement(GlobalHolder, {
  70. ref: ref,
  71. sync: sync,
  72. messageConfig: messageConfig
  73. });
  74. return /*#__PURE__*/React.createElement(ConfigProvider, {
  75. prefixCls: rootPrefixCls,
  76. iconPrefixCls: rootIconPrefixCls,
  77. theme: theme
  78. }, global.holderRender ? global.holderRender(dom) : dom);
  79. });
  80. const flushMessageQueue = () => {
  81. if (!message) {
  82. const holderFragment = document.createDocumentFragment();
  83. const newMessage = {
  84. fragment: holderFragment
  85. };
  86. message = newMessage;
  87. // Delay render to avoid sync issue
  88. act(() => {
  89. const reactRender = unstableSetRender();
  90. reactRender(/*#__PURE__*/React.createElement(GlobalHolderWrapper, {
  91. ref: node => {
  92. const {
  93. instance,
  94. sync
  95. } = node || {};
  96. // React 18 test env will throw if call immediately in ref
  97. Promise.resolve().then(() => {
  98. if (!newMessage.instance && instance) {
  99. newMessage.instance = instance;
  100. newMessage.sync = sync;
  101. flushMessageQueue();
  102. }
  103. });
  104. }
  105. }), holderFragment);
  106. });
  107. return;
  108. }
  109. // Notification not ready
  110. if (!message.instance) {
  111. return;
  112. }
  113. // >>> Execute task
  114. taskQueue.forEach(task => {
  115. const {
  116. type,
  117. skipped
  118. } = task;
  119. // Only `skipped` when user call notice but cancel it immediately
  120. // and instance not ready
  121. if (!skipped) {
  122. switch (type) {
  123. case 'open':
  124. {
  125. act(() => {
  126. const closeFn = message.instance.open(Object.assign(Object.assign({}, defaultGlobalConfig), task.config));
  127. closeFn === null || closeFn === void 0 ? void 0 : closeFn.then(task.resolve);
  128. task.setCloseFn(closeFn);
  129. });
  130. break;
  131. }
  132. case 'destroy':
  133. act(() => {
  134. message === null || message === void 0 ? void 0 : message.instance.destroy(task.key);
  135. });
  136. break;
  137. // Other type open
  138. default:
  139. {
  140. act(() => {
  141. var _message$instance;
  142. const closeFn = (_message$instance = message.instance)[type].apply(_message$instance, _toConsumableArray(task.args));
  143. closeFn === null || closeFn === void 0 ? void 0 : closeFn.then(task.resolve);
  144. task.setCloseFn(closeFn);
  145. });
  146. }
  147. }
  148. }
  149. });
  150. // Clean up
  151. taskQueue = [];
  152. };
  153. // ==============================================================================
  154. // == Export ==
  155. // ==============================================================================
  156. function setMessageGlobalConfig(config) {
  157. defaultGlobalConfig = Object.assign(Object.assign({}, defaultGlobalConfig), config);
  158. // Trigger sync for it
  159. act(() => {
  160. var _a;
  161. (_a = message === null || message === void 0 ? void 0 : message.sync) === null || _a === void 0 ? void 0 : _a.call(message);
  162. });
  163. }
  164. function open(config) {
  165. const result = wrapPromiseFn(resolve => {
  166. let closeFn;
  167. const task = {
  168. type: 'open',
  169. config,
  170. resolve,
  171. setCloseFn: fn => {
  172. closeFn = fn;
  173. }
  174. };
  175. taskQueue.push(task);
  176. return () => {
  177. if (closeFn) {
  178. act(() => {
  179. closeFn();
  180. });
  181. } else {
  182. task.skipped = true;
  183. }
  184. };
  185. });
  186. flushMessageQueue();
  187. return result;
  188. }
  189. function typeOpen(type, args) {
  190. const global = globalConfig();
  191. if (process.env.NODE_ENV !== 'production' && !global.holderRender) {
  192. warnContext('message');
  193. }
  194. const result = wrapPromiseFn(resolve => {
  195. let closeFn;
  196. const task = {
  197. type,
  198. args,
  199. resolve,
  200. setCloseFn: fn => {
  201. closeFn = fn;
  202. }
  203. };
  204. taskQueue.push(task);
  205. return () => {
  206. if (closeFn) {
  207. act(() => {
  208. closeFn();
  209. });
  210. } else {
  211. task.skipped = true;
  212. }
  213. };
  214. });
  215. flushMessageQueue();
  216. return result;
  217. }
  218. const destroy = key => {
  219. taskQueue.push({
  220. type: 'destroy',
  221. key
  222. });
  223. flushMessageQueue();
  224. };
  225. const methods = ['success', 'info', 'warning', 'error', 'loading'];
  226. const baseStaticMethods = {
  227. open,
  228. destroy,
  229. config: setMessageGlobalConfig,
  230. useMessage,
  231. _InternalPanelDoNotUseOrYouWillBeFired: PurePanel
  232. };
  233. const staticMethods = baseStaticMethods;
  234. methods.forEach(type => {
  235. staticMethods[type] = (...args) => typeOpen(type, args);
  236. });
  237. // ==============================================================================
  238. // == Test ==
  239. // ==============================================================================
  240. const noop = () => {};
  241. let _actWrapper = noop;
  242. if (process.env.NODE_ENV === 'test') {
  243. _actWrapper = wrapper => {
  244. act = wrapper;
  245. };
  246. }
  247. const actWrapper = _actWrapper;
  248. export { actWrapper };
  249. let _actDestroy = noop;
  250. if (process.env.NODE_ENV === 'test') {
  251. _actDestroy = () => {
  252. message = null;
  253. };
  254. }
  255. const actDestroy = _actDestroy;
  256. export { actDestroy };
  257. export default staticMethods;