PageEditorModeManager.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import React, { type ReactNode, useCallback, useMemo } from 'react';
  2. import { Origin } from '@growi/core';
  3. import { useTranslation } from 'next-i18next';
  4. import { useCreatePageAndTransit } from '~/client/services/create-page';
  5. import { toastError } from '~/client/util/toastr';
  6. import { useIsNotFound } from '~/stores/page';
  7. import { EditorMode, useEditorMode, useIsDeviceLargerThanMd } from '~/stores/ui';
  8. import { useCurrentPageYjsData } from '~/stores/yjs';
  9. import { shouldCreateWipPage } from '../../utils/should-create-wip-page';
  10. import styles from './PageEditorModeManager.module.scss';
  11. type PageEditorModeButtonProps = {
  12. currentEditorMode: EditorMode,
  13. editorMode: EditorMode,
  14. children?: ReactNode,
  15. isBtnDisabled?: boolean,
  16. onClick?: () => void,
  17. }
  18. const PageEditorModeButton = React.memo((props: PageEditorModeButtonProps) => {
  19. const {
  20. currentEditorMode, isBtnDisabled, editorMode, children, onClick,
  21. } = props;
  22. const classNames = ['btn py-1 px-2 d-flex align-items-center justify-content-center'];
  23. if (currentEditorMode === editorMode) {
  24. classNames.push('active');
  25. }
  26. if (isBtnDisabled) {
  27. classNames.push('disabled');
  28. }
  29. return (
  30. <button
  31. type="button"
  32. className={classNames.join(' ')}
  33. onClick={onClick}
  34. data-testid={`${editorMode}-button`}
  35. >
  36. {children}
  37. </button>
  38. );
  39. });
  40. type Props = {
  41. editorMode: EditorMode | undefined,
  42. isBtnDisabled: boolean,
  43. path?: string,
  44. }
  45. export const PageEditorModeManager = (props: Props): JSX.Element => {
  46. const {
  47. editorMode = EditorMode.View,
  48. isBtnDisabled,
  49. path,
  50. } = props;
  51. const { t } = useTranslation('commons');
  52. const { data: isNotFound } = useIsNotFound();
  53. const { mutate: mutateEditorMode } = useEditorMode();
  54. const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
  55. const { data: currentPageYjsData } = useCurrentPageYjsData();
  56. const { isCreating, createAndTransit } = useCreatePageAndTransit();
  57. const editButtonClickedHandler = useCallback(async() => {
  58. if (isNotFound == null || isNotFound === false) {
  59. mutateEditorMode(EditorMode.Editor);
  60. return;
  61. }
  62. try {
  63. await createAndTransit(
  64. { path, wip: shouldCreateWipPage(path), origin: Origin.View },
  65. { shouldCheckPageExists: true },
  66. );
  67. }
  68. catch (err) {
  69. toastError(t('toaster.create_failed', { target: path }));
  70. }
  71. }, [createAndTransit, isNotFound, mutateEditorMode, path, t]);
  72. const _isBtnDisabled = isCreating || isBtnDisabled;
  73. const circleColor = useMemo(() => {
  74. if (currentPageYjsData?.awarenessStateSize != null && currentPageYjsData.awarenessStateSize > 0) {
  75. return 'bg-primary';
  76. }
  77. // TODO: https://redmine.weseek.co.jp/issues/145652
  78. }, [currentPageYjsData]);
  79. return (
  80. <>
  81. <div
  82. className={`btn-group grw-page-editor-mode-manager ${styles['grw-page-editor-mode-manager']}`}
  83. role="group"
  84. aria-label="page-editor-mode-manager"
  85. id="grw-page-editor-mode-manager"
  86. >
  87. {(isDeviceLargerThanMd || editorMode !== EditorMode.View) && (
  88. <PageEditorModeButton
  89. currentEditorMode={editorMode}
  90. editorMode={EditorMode.View}
  91. isBtnDisabled={_isBtnDisabled}
  92. onClick={() => mutateEditorMode(EditorMode.View)}
  93. >
  94. <span className="material-symbols-outlined fs-4">play_arrow</span>{t('View')}
  95. </PageEditorModeButton>
  96. )}
  97. {(isDeviceLargerThanMd || editorMode === EditorMode.View) && (
  98. <PageEditorModeButton
  99. currentEditorMode={editorMode}
  100. editorMode={EditorMode.Editor}
  101. isBtnDisabled={_isBtnDisabled}
  102. onClick={editButtonClickedHandler}
  103. >
  104. <span className="material-symbols-outlined me-1 fs-5">edit_square</span>{t('Edit')}
  105. { circleColor != null && <span className={`position-absolute top-0 start-100 translate-middle p-1 rounded-circle ${circleColor}`} />}
  106. </PageEditorModeButton>
  107. )}
  108. </div>
  109. </>
  110. );
  111. };