EditorNavbarBottom.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import React, { useCallback, useState, useEffect } from 'react';
  2. import dynamic from 'next/dynamic';
  3. import { Collapse, Button } from 'reactstrap';
  4. import type { SavePageControlsProps } from '~/components/SavePageControls';
  5. import { useIsSlackConfigured } from '~/stores/context';
  6. import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
  7. import { useCurrentPagePath } from '~/stores/page';
  8. import {
  9. useEditorMode, useIsDeviceLargerThanLg, useIsDeviceLargerThanMd,
  10. } from '~/stores/ui';
  11. import styles from './EditorNavbarBottom.module.scss';
  12. const moduleClass = styles['grw-editor-navbar-bottom'];
  13. const SavePageControls = dynamic<SavePageControlsProps>(() => import('~/components/SavePageControls').then(mod => mod.SavePageControls), { ssr: false });
  14. const SlackLogo = dynamic(() => import('~/components/SlackLogo').then(mod => mod.SlackLogo), { ssr: false });
  15. const SlackNotification = dynamic(() => import('~/components/SlackNotification').then(mod => mod.SlackNotification), { ssr: false });
  16. const OptionsSelector = dynamic(() => import('~/components/PageEditor/OptionsSelector').then(mod => mod.OptionsSelector), { ssr: false });
  17. const EditorNavbarBottom = (): JSX.Element => {
  18. const [isExpanded, setExpanded] = useState(false);
  19. const [isSlackExpanded, setSlackExpanded] = useState(false);
  20. const { data: editorMode } = useEditorMode();
  21. const { data: isSlackConfigured } = useIsSlackConfigured();
  22. const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
  23. const { data: isDeviceLargerThanLg } = useIsDeviceLargerThanLg();
  24. const { data: currentPagePath } = useCurrentPagePath();
  25. const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
  26. const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
  27. const [slackChannelsStr, setSlackChannelsStr] = useState<string>('');
  28. // DO NOT dependent on slackChannelsData directly: https://github.com/weseek/growi/pull/7332
  29. const slackChannelsDataString = slackChannelsData?.toString();
  30. useEffect(() => {
  31. if (editorMode === 'editor') {
  32. setSlackChannelsStr(slackChannelsDataString ?? '');
  33. mutateIsSlackEnabled(false);
  34. }
  35. }, [editorMode, mutateIsSlackEnabled, slackChannelsDataString]);
  36. const isSlackEnabledToggleHandler = (bool: boolean) => {
  37. mutateIsSlackEnabled(bool, false);
  38. };
  39. const slackChannelsChangedHandler = useCallback((slackChannels: string) => {
  40. setSlackChannelsStr(slackChannels);
  41. }, []);
  42. const renderExpandButton = () => (
  43. <div className="d-md-none ms-2">
  44. <button
  45. type="button"
  46. className={`btn btn-outline-secondary btn-expand border-0 ${isExpanded ? 'expand' : ''}`}
  47. onClick={() => setExpanded(!isExpanded)}
  48. >
  49. <i className="icon-arrow-up"></i>
  50. </button>
  51. </div>
  52. );
  53. const isCollapsedOptionsSelectorEnabled = !isDeviceLargerThanLg;
  54. return (
  55. <div className={`${isCollapsedOptionsSelectorEnabled ? 'fixed-bottom' : ''} `} data-testid="grw-editor-navbar-bottom">
  56. {/* Collapsed SlackNotification */}
  57. {isSlackConfigured && (
  58. <Collapse isOpen={isSlackExpanded && !isDeviceLargerThanLg}>
  59. <nav className={`navbar navbar-expand-lg border-top ${moduleClass}`}>
  60. {isSlackEnabled != null
  61. && (
  62. <SlackNotification
  63. isSlackEnabled={isSlackEnabled}
  64. slackChannels={slackChannelsStr}
  65. onEnabledFlagChange={isSlackEnabledToggleHandler}
  66. onChannelChange={slackChannelsChangedHandler}
  67. id="idForEditorNavbarBottomForMobile"
  68. />
  69. )
  70. }
  71. </nav>
  72. </Collapse>
  73. )
  74. }
  75. <div className={`flex-expand-horiz align-items-center px-2 px-md-3 ${moduleClass}`}>
  76. <form>
  77. { isDeviceLargerThanMd && <OptionsSelector /> }
  78. </form>
  79. <form className="row row-cols-lg-auto g-3 align-items-center ms-auto">
  80. {/* Responsive Design for the SlackNotification */}
  81. {/* Button or the normal Slack banner */}
  82. {isSlackConfigured && (!isDeviceLargerThanMd ? (
  83. <Button
  84. className="grw-btn-slack border me-2"
  85. onClick={() => (setSlackExpanded(!isSlackExpanded))}
  86. >
  87. <div className="grw-slack-logo">
  88. <SlackLogo />
  89. <span className="grw-btn-slack-triangle fa fa-caret-up ms-2"></span>
  90. </div>
  91. </Button>
  92. ) : (
  93. <div className="me-2">
  94. {isSlackEnabled != null
  95. && (
  96. <SlackNotification
  97. isSlackEnabled={isSlackEnabled}
  98. slackChannels={slackChannelsStr}
  99. onEnabledFlagChange={isSlackEnabledToggleHandler}
  100. onChannelChange={slackChannelsChangedHandler}
  101. id="idForEditorNavbarBottom"
  102. />
  103. )}
  104. </div>
  105. ))}
  106. <SavePageControls slackChannels={slackChannelsStr} />
  107. { isCollapsedOptionsSelectorEnabled && renderExpandButton() }
  108. </form>
  109. </div>
  110. {/* Collapsed OptionsSelector */}
  111. { isCollapsedOptionsSelectorEnabled && (
  112. <Collapse isOpen={isExpanded}>
  113. <div className="px-2"> {/* set padding for border-top */}
  114. <div className={`navbar navbar-expand border-top px-0 ${moduleClass}`}>
  115. <form className="ms-auto">
  116. <OptionsSelector />
  117. </form>
  118. </div>
  119. </div>
  120. </Collapse>
  121. ) }
  122. </div>
  123. );
  124. };
  125. export default EditorNavbarBottom;