PageEditorModeManager.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import React, { type ReactNode, useCallback, useState } from 'react';
  2. import type { IGrantedGroup } from '@growi/core';
  3. import { useTranslation } from 'next-i18next';
  4. import { toastError } from '~/client/util/toastr';
  5. import { EditorMode, useEditorMode, useIsDeviceLargerThanMd } from '~/stores/ui';
  6. import { useCreatePageAndTransit } from './hooks';
  7. import styles from './PageEditorModeManager.module.scss';
  8. type PageEditorModeButtonProps = {
  9. currentEditorMode: EditorMode,
  10. editorMode: EditorMode,
  11. children?: ReactNode,
  12. isBtnDisabled?: boolean,
  13. onClick?: () => void,
  14. }
  15. const PageEditorModeButton = React.memo((props: PageEditorModeButtonProps) => {
  16. const {
  17. currentEditorMode, isBtnDisabled, editorMode, children, onClick,
  18. } = props;
  19. const classNames = ['btn py-1 px-2 d-flex align-items-center justify-content-center'];
  20. if (currentEditorMode === editorMode) {
  21. classNames.push('active');
  22. }
  23. if (isBtnDisabled) {
  24. classNames.push('disabled');
  25. }
  26. return (
  27. <button
  28. type="button"
  29. className={classNames.join(' ')}
  30. onClick={onClick}
  31. data-testid={`${editorMode}-button`}
  32. >
  33. {children}
  34. </button>
  35. );
  36. });
  37. type Props = {
  38. editorMode: EditorMode | undefined,
  39. isBtnDisabled: boolean,
  40. path?: string,
  41. grant?: number,
  42. // grantUserGroupId?: string
  43. grantUserGroupIds?: IGrantedGroup[]
  44. }
  45. export const PageEditorModeManager = (props: Props): JSX.Element => {
  46. const {
  47. editorMode = EditorMode.View,
  48. isBtnDisabled,
  49. path,
  50. // grant,
  51. // grantUserGroupId,
  52. } = props;
  53. const { t } = useTranslation('common');
  54. const [isCreating, setIsCreating] = useState(false);
  55. const { mutate: mutateEditorMode } = useEditorMode();
  56. const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
  57. const _isBtnDisabled = isCreating || isBtnDisabled;
  58. const createPageAndTransit = useCreatePageAndTransit();
  59. const editButtonClickedHandler = useCallback(() => {
  60. createPageAndTransit(
  61. path,
  62. {
  63. onCreationStart: () => { setIsCreating(true) },
  64. onAborted: () => { mutateEditorMode(EditorMode.Editor) },
  65. onError: () => { toastError(t('toaster.create_failed', { target: path })) },
  66. onTerminated: () => { setIsCreating(false) },
  67. },
  68. );
  69. }, [createPageAndTransit, path, mutateEditorMode, t]);
  70. return (
  71. <>
  72. <div
  73. className={`btn-group grw-page-editor-mode-manager ${styles['grw-page-editor-mode-manager']}`}
  74. role="group"
  75. aria-label="page-editor-mode-manager"
  76. id="grw-page-editor-mode-manager"
  77. >
  78. {(isDeviceLargerThanMd || editorMode !== EditorMode.View) && (
  79. <PageEditorModeButton
  80. currentEditorMode={editorMode}
  81. editorMode={EditorMode.View}
  82. isBtnDisabled={_isBtnDisabled}
  83. onClick={() => mutateEditorMode(EditorMode.View)}
  84. >
  85. <span className="material-symbols-outlined fs-4">play_arrow</span>{t('View')}
  86. </PageEditorModeButton>
  87. )}
  88. {(isDeviceLargerThanMd || editorMode === EditorMode.View) && (
  89. <PageEditorModeButton
  90. currentEditorMode={editorMode}
  91. editorMode={EditorMode.Editor}
  92. isBtnDisabled={_isBtnDisabled}
  93. onClick={editButtonClickedHandler}
  94. >
  95. <span className="material-symbols-outlined me-1 fs-5">edit_square</span>{t('Edit')}
  96. </PageEditorModeButton>
  97. )}
  98. </div>
  99. </>
  100. );
  101. };