PageEditorModeManager.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import React, { type JSX, type ReactNode, useCallback, useMemo } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import { useCreatePage } from '~/client/services/create-page';
  4. import { useStartEditing } from '~/client/services/use-start-editing';
  5. import { toastError } from '~/client/util/toastr';
  6. import { useCurrentPageYjsData } from '~/features/collaborative-editor/states';
  7. import { useDeviceLargerThanMd } from '~/states/ui/device';
  8. import { EditorMode, useEditorMode } from '~/states/ui/editor';
  9. import styles from './PageEditorModeManager.module.scss';
  10. type PageEditorModeButtonProps = {
  11. currentEditorMode: EditorMode;
  12. editorMode: EditorMode;
  13. children?: ReactNode;
  14. isBtnDisabled?: boolean;
  15. onClick?: () => void;
  16. };
  17. const PageEditorModeButton = React.memo((props: PageEditorModeButtonProps) => {
  18. const { currentEditorMode, isBtnDisabled, editorMode, children, onClick } =
  19. props;
  20. const classNames = [
  21. 'btn py-1 px-2 d-flex align-items-center justify-content-center',
  22. ];
  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 { editorMode = EditorMode.View, isBtnDisabled, path } = props;
  47. const { t } = useTranslation('commons');
  48. const { setEditorMode } = useEditorMode();
  49. const [isDeviceLargerThanMd] = useDeviceLargerThanMd();
  50. const currentPageYjsData = useCurrentPageYjsData();
  51. const startEditing = useStartEditing();
  52. const { isCreating } = useCreatePage();
  53. const editButtonClickedHandler = useCallback(async () => {
  54. try {
  55. await startEditing(path);
  56. } catch (err) {
  57. toastError(t('toaster.create_failed', { target: path }));
  58. }
  59. }, [startEditing, path, t]);
  60. const _isBtnDisabled = isCreating || isBtnDisabled;
  61. const circleColor = useMemo(() => {
  62. if ((currentPageYjsData?.awarenessStateSize ?? 0) > 0) {
  63. return 'bg-primary';
  64. }
  65. if (currentPageYjsData?.hasYdocsNewerThanLatestRevision ?? false) {
  66. return 'bg-secondary';
  67. }
  68. }, [currentPageYjsData]);
  69. return (
  70. <>
  71. <fieldset
  72. className={`btn-group grw-page-editor-mode-manager ${styles['grw-page-editor-mode-manager']}`}
  73. aria-label="page-editor-mode-manager"
  74. id="grw-page-editor-mode-manager"
  75. data-testid="grw-page-editor-mode-manager"
  76. >
  77. {(isDeviceLargerThanMd || editorMode !== EditorMode.View) && (
  78. <PageEditorModeButton
  79. currentEditorMode={editorMode}
  80. editorMode={EditorMode.View}
  81. isBtnDisabled={_isBtnDisabled}
  82. onClick={() => setEditorMode(EditorMode.View)}
  83. >
  84. <span className="material-symbols-outlined fs-4">play_arrow</span>
  85. {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">
  96. edit_square
  97. </span>
  98. {t('Edit')}
  99. {circleColor != null && (
  100. <span
  101. className={`position-absolute top-0 start-100 translate-middle p-1 rounded-circle ${circleColor}`}
  102. />
  103. )}
  104. </PageEditorModeButton>
  105. )}
  106. </fieldset>
  107. </>
  108. );
  109. };