modal.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. import { useCallback, useMemo } from 'react';
  2. import type {
  3. IAttachmentHasId, IUserGroupHasId,
  4. } from '@growi/core';
  5. import { useSWRStatic } from '@growi/core/dist/swr';
  6. import { MarkdownTable } from '@growi/editor';
  7. import type { SWRResponse } from 'swr';
  8. import type { BookmarkFolderItems } from '~/interfaces/bookmark-info';
  9. import type {
  10. OnPutBackedFunction, onDeletedBookmarkFolderFunction, OnSelectedFunction,
  11. } from '~/interfaces/ui';
  12. import loggerFactory from '~/utils/logger';
  13. const logger = loggerFactory('growi:stores:modal');
  14. /*
  15. * GrantedGroupsInheritanceSelectModal
  16. */
  17. type GrantedGroupsInheritanceSelectModalStatus = {
  18. isOpened: boolean,
  19. onCreateBtnClick?: (onlyInheritUserRelatedGrantedGroups?: boolean) => Promise<void>,
  20. }
  21. type GrantedGroupsInheritanceSelectModalStatusUtils = {
  22. open(onCreateBtnClick?: (onlyInheritUserRelatedGrantedGroups?: boolean) => Promise<void>): Promise<GrantedGroupsInheritanceSelectModalStatus | undefined>
  23. close(): Promise<GrantedGroupsInheritanceSelectModalStatus | undefined>
  24. }
  25. export const useGrantedGroupsInheritanceSelectModal = (
  26. status?: GrantedGroupsInheritanceSelectModalStatus,
  27. ): SWRResponse<GrantedGroupsInheritanceSelectModalStatus, Error> & GrantedGroupsInheritanceSelectModalStatusUtils => {
  28. const initialData: GrantedGroupsInheritanceSelectModalStatus = { isOpened: false };
  29. const swrResponse = useSWRStatic<GrantedGroupsInheritanceSelectModalStatus, Error>(
  30. 'grantedGroupsInheritanceSelectModalStatus', status, { fallbackData: initialData },
  31. );
  32. const { mutate } = swrResponse;
  33. return {
  34. ...swrResponse,
  35. open: useCallback(
  36. (onCreateBtnClick?: (onlyInheritUserRelatedGrantedGroups?: boolean) => Promise<void>) => mutate({ isOpened: true, onCreateBtnClick }), [mutate],
  37. ),
  38. close: useCallback(() => mutate({ isOpened: false }), [mutate]),
  39. };
  40. };
  41. /*
  42. * PutBackPageModal
  43. */
  44. export type IPageForPagePutBackModal = {
  45. pageId: string,
  46. path: string
  47. }
  48. export type IPutBackPageModalOption = {
  49. onPutBacked?: OnPutBackedFunction,
  50. }
  51. type PutBackPageModalStatus = {
  52. isOpened: boolean,
  53. page?: IPageForPagePutBackModal,
  54. opts?: IPutBackPageModalOption,
  55. }
  56. type PutBackPageModalUtils = {
  57. open(
  58. page?: IPageForPagePutBackModal,
  59. opts?: IPutBackPageModalOption,
  60. ): Promise<PutBackPageModalStatus | undefined>
  61. close(): Promise<PutBackPageModalStatus | undefined>
  62. }
  63. export const usePutBackPageModal = (status?: PutBackPageModalStatus): SWRResponse<PutBackPageModalStatus, Error> & PutBackPageModalUtils => {
  64. const initialData: PutBackPageModalStatus = useMemo(() => ({
  65. isOpened: false,
  66. page: { pageId: '', path: '' },
  67. }), []);
  68. const swrResponse = useSWRStatic<PutBackPageModalStatus, Error>('putBackPageModalStatus', status, { fallbackData: initialData });
  69. return {
  70. ...swrResponse,
  71. open: (
  72. page: IPageForPagePutBackModal, opts?: IPutBackPageModalOption,
  73. ) => swrResponse.mutate({
  74. isOpened: true, page, opts,
  75. }),
  76. close: () => swrResponse.mutate({ isOpened: false, page: { pageId: '', path: '' } }),
  77. };
  78. };
  79. /*
  80. * PagePresentationModal
  81. */
  82. type PresentationModalStatus = {
  83. isOpened: boolean,
  84. }
  85. type PresentationModalStatusUtils = {
  86. open(): Promise<PresentationModalStatus | undefined>
  87. close(): Promise<PresentationModalStatus | undefined>
  88. }
  89. export const usePagePresentationModal = (
  90. status?: PresentationModalStatus,
  91. ): SWRResponse<PresentationModalStatus, Error> & PresentationModalStatusUtils => {
  92. const initialData: PresentationModalStatus = {
  93. isOpened: false,
  94. };
  95. const swrResponse = useSWRStatic<PresentationModalStatus, Error>('presentationModalStatus', status, { fallbackData: initialData });
  96. return {
  97. ...swrResponse,
  98. open: () => swrResponse.mutate({ isOpened: true }, { revalidate: true }),
  99. close: () => swrResponse.mutate({ isOpened: false }),
  100. };
  101. };
  102. /*
  103. * PrivateLegacyPagesMigrationModal
  104. */
  105. export type ILegacyPrivatePage = { pageId: string, path: string };
  106. export type PrivateLegacyPagesMigrationModalSubmitedHandler = (pages: ILegacyPrivatePage[], isRecursively?: boolean) => void;
  107. type PrivateLegacyPagesMigrationModalStatus = {
  108. isOpened: boolean,
  109. pages?: ILegacyPrivatePage[],
  110. onSubmited?: PrivateLegacyPagesMigrationModalSubmitedHandler,
  111. }
  112. type PrivateLegacyPagesMigrationModalStatusUtils = {
  113. open(pages: ILegacyPrivatePage[], onSubmited?: PrivateLegacyPagesMigrationModalSubmitedHandler): Promise<PrivateLegacyPagesMigrationModalStatus | undefined>,
  114. close(): Promise<PrivateLegacyPagesMigrationModalStatus | undefined>,
  115. }
  116. export const usePrivateLegacyPagesMigrationModal = (
  117. status?: PrivateLegacyPagesMigrationModalStatus,
  118. ): SWRResponse<PrivateLegacyPagesMigrationModalStatus, Error> & PrivateLegacyPagesMigrationModalStatusUtils => {
  119. const initialData: PrivateLegacyPagesMigrationModalStatus = {
  120. isOpened: false,
  121. pages: [],
  122. };
  123. const swrResponse = useSWRStatic<PrivateLegacyPagesMigrationModalStatus, Error>('privateLegacyPagesMigrationModal', status, { fallbackData: initialData });
  124. return {
  125. ...swrResponse,
  126. open: (pages, onSubmited?) => swrResponse.mutate({
  127. isOpened: true, pages, onSubmited,
  128. }),
  129. close: () => swrResponse.mutate({ isOpened: false, pages: [], onSubmited: undefined }),
  130. };
  131. };
  132. /*
  133. * DescendantsPageListModal
  134. */
  135. type DescendantsPageListModalStatus = {
  136. isOpened: boolean,
  137. path?: string,
  138. }
  139. type DescendantsPageListUtils = {
  140. open(path: string): Promise<DescendantsPageListModalStatus | undefined>
  141. close(): Promise<DescendantsPageListModalStatus | undefined>
  142. }
  143. export const useDescendantsPageListModal = (
  144. status?: DescendantsPageListModalStatus,
  145. ): SWRResponse<DescendantsPageListModalStatus, Error> & DescendantsPageListUtils => {
  146. const initialData: DescendantsPageListModalStatus = { isOpened: false };
  147. const swrResponse = useSWRStatic<DescendantsPageListModalStatus, Error>('descendantsPageListModalStatus', status, { fallbackData: initialData });
  148. return {
  149. ...swrResponse,
  150. open: (path: string) => swrResponse.mutate({ isOpened: true, path }),
  151. close: () => swrResponse.mutate({ isOpened: false }),
  152. };
  153. };
  154. /*
  155. * UpdateUserGroupConfirmModal
  156. */
  157. type UpdateUserGroupConfirmModalStatus = {
  158. isOpened: boolean,
  159. targetGroup?: IUserGroupHasId,
  160. updateData?: Partial<IUserGroupHasId>,
  161. onConfirm?: (targetGroup: IUserGroupHasId, updateData: Partial<IUserGroupHasId>, forceUpdateParents: boolean) => any,
  162. }
  163. type UpdateUserGroupConfirmModalUtils = {
  164. open(targetGroup: IUserGroupHasId, updateData: Partial<IUserGroupHasId>, onConfirm?: (...args: any[]) => any): Promise<void>,
  165. close(): Promise<void>,
  166. }
  167. export const useUpdateUserGroupConfirmModal = (): SWRResponse<UpdateUserGroupConfirmModalStatus, Error> & UpdateUserGroupConfirmModalUtils => {
  168. const initialStatus: UpdateUserGroupConfirmModalStatus = { isOpened: false };
  169. const swrResponse = useSWRStatic<UpdateUserGroupConfirmModalStatus, Error>('updateParentConfirmModal', undefined, { fallbackData: initialStatus });
  170. return {
  171. ...swrResponse,
  172. async open(targetGroup: IUserGroupHasId, updateData: Partial<IUserGroupHasId>, onConfirm?: (...args: any[]) => any) {
  173. await swrResponse.mutate({
  174. isOpened: true, targetGroup, updateData, onConfirm,
  175. });
  176. },
  177. async close() {
  178. await swrResponse.mutate({ isOpened: false });
  179. },
  180. };
  181. };
  182. /*
  183. * DrawioModal
  184. */
  185. type DrawioModalSaveHandler = (drawioMxFile: string) => void;
  186. type DrawioModalStatus = {
  187. isOpened: boolean,
  188. drawioMxFile: string,
  189. onSave?: DrawioModalSaveHandler,
  190. }
  191. type DrawioModalStatusUtils = {
  192. open(
  193. drawioMxFile: string,
  194. onSave?: DrawioModalSaveHandler,
  195. ): void,
  196. close(): void,
  197. }
  198. export const useDrawioModal = (status?: DrawioModalStatus): SWRResponse<DrawioModalStatus, Error> & DrawioModalStatusUtils => {
  199. const initialData: DrawioModalStatus = {
  200. isOpened: false,
  201. drawioMxFile: '',
  202. };
  203. const swrResponse = useSWRStatic<DrawioModalStatus, Error>('drawioModalStatus', status, { fallbackData: initialData });
  204. const { mutate } = swrResponse;
  205. const open = useCallback((drawioMxFile: string, onSave?: DrawioModalSaveHandler): void => {
  206. mutate({ isOpened: true, drawioMxFile, onSave });
  207. }, [mutate]);
  208. const close = useCallback((): void => {
  209. mutate({ isOpened: false, drawioMxFile: '', onSave: undefined });
  210. }, [mutate]);
  211. return {
  212. ...swrResponse,
  213. open,
  214. close,
  215. };
  216. };
  217. /*
  218. * HandsonTableModal
  219. */
  220. type HandsonTableModalSaveHandler = (table: MarkdownTable) => void;
  221. type HandsontableModalStatus = {
  222. isOpened: boolean,
  223. table: MarkdownTable,
  224. autoFormatMarkdownTable?: boolean,
  225. // onSave is passed only when editing table directly from the page.
  226. onSave?: HandsonTableModalSaveHandler
  227. }
  228. type HandsontableModalStatusUtils = {
  229. open(
  230. table: MarkdownTable,
  231. autoFormatMarkdownTable?: boolean,
  232. onSave?: HandsonTableModalSaveHandler
  233. ): void
  234. close(): void
  235. }
  236. const defaultMarkdownTable = () => {
  237. return new MarkdownTable(
  238. [
  239. ['col1', 'col2', 'col3'],
  240. ['', '', ''],
  241. ['', '', ''],
  242. ],
  243. {
  244. align: ['', '', ''],
  245. },
  246. );
  247. };
  248. export const useHandsontableModal = (status?: HandsontableModalStatus): SWRResponse<HandsontableModalStatus, Error> & HandsontableModalStatusUtils => {
  249. const initialData: HandsontableModalStatus = {
  250. isOpened: false,
  251. table: defaultMarkdownTable(),
  252. autoFormatMarkdownTable: false,
  253. };
  254. const swrResponse = useSWRStatic<HandsontableModalStatus, Error>('handsontableModalStatus', status, { fallbackData: initialData });
  255. const { mutate } = swrResponse;
  256. const open = useCallback((table: MarkdownTable, autoFormatMarkdownTable?: boolean, onSave?: HandsonTableModalSaveHandler): void => {
  257. mutate({
  258. isOpened: true, table, autoFormatMarkdownTable, onSave,
  259. });
  260. }, [mutate]);
  261. const close = useCallback((): void => {
  262. mutate({
  263. isOpened: false, table: defaultMarkdownTable(), autoFormatMarkdownTable: false, onSave: undefined,
  264. });
  265. }, [mutate]);
  266. return {
  267. ...swrResponse,
  268. open,
  269. close,
  270. };
  271. };
  272. /*
  273. * ConflictDiffModal
  274. */
  275. type ResolveConflictHandler = (newMarkdown: string) => Promise<void> | void;
  276. type ConflictDiffModalStatus = {
  277. isOpened: boolean,
  278. requestRevisionBody?: string,
  279. onResolve?: ResolveConflictHandler
  280. }
  281. type ConflictDiffModalUtils = {
  282. open(requestRevisionBody: string, onResolveConflict: ResolveConflictHandler): void,
  283. close(): void,
  284. }
  285. export const useConflictDiffModal = (): SWRResponse<ConflictDiffModalStatus, Error> & ConflictDiffModalUtils => {
  286. const initialStatus: ConflictDiffModalStatus = { isOpened: false };
  287. const swrResponse = useSWRStatic<ConflictDiffModalStatus, Error>('conflictDiffModal', undefined, { fallbackData: initialStatus });
  288. const { mutate } = swrResponse;
  289. const open = useCallback((requestRevisionBody: string, onResolve: ResolveConflictHandler) => {
  290. mutate({ isOpened: true, requestRevisionBody, onResolve });
  291. }, [mutate]);
  292. const close = useCallback(() => {
  293. mutate({ isOpened: false });
  294. }, [mutate]);
  295. return {
  296. ...swrResponse,
  297. open,
  298. close,
  299. };
  300. };
  301. /*
  302. * BookmarkFolderDeleteModal
  303. */
  304. export type IDeleteBookmarkFolderModalOption = {
  305. onDeleted?: onDeletedBookmarkFolderFunction,
  306. }
  307. type DeleteBookmarkFolderModalStatus = {
  308. isOpened: boolean,
  309. bookmarkFolder?: BookmarkFolderItems,
  310. opts?: IDeleteBookmarkFolderModalOption,
  311. }
  312. type DeleteModalBookmarkFolderStatusUtils = {
  313. open(
  314. bookmarkFolder?: BookmarkFolderItems,
  315. opts?: IDeleteBookmarkFolderModalOption,
  316. ): Promise<DeleteBookmarkFolderModalStatus | undefined>,
  317. close(): Promise<DeleteBookmarkFolderModalStatus | undefined>,
  318. }
  319. export const useBookmarkFolderDeleteModal = (status?: DeleteBookmarkFolderModalStatus):
  320. SWRResponse<DeleteBookmarkFolderModalStatus, Error> & DeleteModalBookmarkFolderStatusUtils => {
  321. const initialData: DeleteBookmarkFolderModalStatus = {
  322. isOpened: false,
  323. };
  324. const swrResponse = useSWRStatic<DeleteBookmarkFolderModalStatus, Error>('deleteBookmarkFolderModalStatus', status, { fallbackData: initialData });
  325. return {
  326. ...swrResponse,
  327. open: (
  328. bookmarkFolder?: BookmarkFolderItems,
  329. opts?: IDeleteBookmarkFolderModalOption,
  330. ) => swrResponse.mutate({
  331. isOpened: true, bookmarkFolder, opts,
  332. }),
  333. close: () => swrResponse.mutate({ isOpened: false }),
  334. };
  335. };
  336. /**
  337. * DeleteAttachmentModal
  338. */
  339. type Remove =
  340. (body: {
  341. attachment_id: string;
  342. }) => Promise<void>
  343. type DeleteAttachmentModalStatus = {
  344. isOpened: boolean,
  345. attachment?: IAttachmentHasId,
  346. remove?: Remove,
  347. }
  348. type DeleteAttachmentModalUtils = {
  349. open(
  350. attachment: IAttachmentHasId,
  351. remove: Remove,
  352. ): void,
  353. close(): void,
  354. }
  355. export const useDeleteAttachmentModal = (): SWRResponse<DeleteAttachmentModalStatus, Error> & DeleteAttachmentModalUtils => {
  356. const initialStatus: DeleteAttachmentModalStatus = {
  357. isOpened: false,
  358. attachment: undefined,
  359. remove: undefined,
  360. };
  361. const swrResponse = useSWRStatic<DeleteAttachmentModalStatus, Error>('deleteAttachmentModal', undefined, { fallbackData: initialStatus });
  362. const { mutate } = swrResponse;
  363. const open = useCallback((attachment: IAttachmentHasId, remove: Remove) => {
  364. mutate({ isOpened: true, attachment, remove });
  365. }, [mutate]);
  366. const close = useCallback(() => {
  367. mutate({ isOpened: false });
  368. }, [mutate]);
  369. return {
  370. ...swrResponse,
  371. open,
  372. close,
  373. };
  374. };
  375. /*
  376. * PageSelectModal
  377. */
  378. type IPageSelectModalOption = {
  379. isHierarchicalSelectionMode?: boolean,
  380. onSelected?: OnSelectedFunction,
  381. }
  382. type PageSelectModalStatus = {
  383. isOpened: boolean
  384. opts?: IPageSelectModalOption
  385. }
  386. type PageSelectModalStatusUtils = {
  387. open(opts?: IPageSelectModalOption): void
  388. close(): void
  389. }
  390. export const usePageSelectModal = (
  391. status?: PageSelectModalStatus,
  392. ): SWRResponse<PageSelectModalStatus, Error> & PageSelectModalStatusUtils => {
  393. const initialStatus = { isOpened: false };
  394. const swrResponse = useSWRStatic<PageSelectModalStatus, Error>('PageSelectModal', status, { fallbackData: initialStatus });
  395. return {
  396. ...swrResponse,
  397. open: (
  398. opts?: IPageSelectModalOption,
  399. ) => swrResponse.mutate({
  400. isOpened: true, opts,
  401. }),
  402. close: () => swrResponse.mutate({ isOpened: false }),
  403. };
  404. };
  405. /*
  406. * OverwriteMergeModeModal
  407. */