PageEditorModeManager.tsx 3.7 KB

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