EditorNavbarBottom.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import React, { useCallback, useState } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { Collapse, Button } from 'reactstrap';
  4. import EditorContainer from '~/client/services/EditorContainer';
  5. import AppContainer from '~/client/services/AppContainer';
  6. import {
  7. EditorMode, useDrawerOpened, useEditorMode, useIsDeviceSmallerThanMd,
  8. } from '~/stores/ui';
  9. import { SlackNotification } from '../SlackNotification';
  10. import SlackLogo from '../SlackLogo';
  11. import { withUnstatedContainers } from '../UnstatedUtils';
  12. import SavePageControls from '../SavePageControls';
  13. import OptionsSelector from './OptionsSelector';
  14. import { useIsSlackEnabled } from '~/stores/editor';
  15. import { useSlackChannels } from '~/stores/context';
  16. const EditorNavbarBottom = (props) => {
  17. const { data: editorMode } = useEditorMode();
  18. const [isExpanded, setExpanded] = useState(false);
  19. const [isSlackExpanded, setSlackExpanded] = useState(false);
  20. const isSlackConfigured = props.appContainer.getConfig().isSlackConfigured;
  21. const { mutate: mutateDrawerOpened } = useDrawerOpened();
  22. const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
  23. const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
  24. const { data: slackChannels, mutate: mutateSlackChannels } = useSlackChannels();
  25. const additionalClasses = ['grw-editor-navbar-bottom'];
  26. const isSlackEnabledToggleHandler = useCallback(
  27. (bool: boolean) => mutateIsSlackEnabled(bool), [mutateIsSlackEnabled],
  28. );
  29. const slackChannelsChangedHandler = useCallback(
  30. (slackChannels: string) => mutateSlackChannels(slackChannels), [mutateSlackChannels],
  31. );
  32. const renderDrawerButton = () => (
  33. <button
  34. type="button"
  35. className="btn btn-outline-secondary border-0"
  36. onClick={() => mutateDrawerOpened(true)}
  37. >
  38. <i className="icon-menu"></i>
  39. </button>
  40. );
  41. const renderExpandButton = () => (
  42. <div className="d-md-none ml-2">
  43. <button
  44. type="button"
  45. className={`btn btn-outline-secondary btn-expand border-0 ${isExpanded ? 'expand' : ''}`}
  46. onClick={() => setExpanded(!isExpanded)}
  47. >
  48. <i className="icon-arrow-up"></i>
  49. </button>
  50. </div>
  51. );
  52. const isOptionsSelectorEnabled = editorMode !== EditorMode.HackMD;
  53. const isCollapsedOptionsSelectorEnabled = isOptionsSelectorEnabled && isDeviceSmallerThanMd;
  54. return (
  55. <div className={`${isCollapsedOptionsSelectorEnabled ? 'fixed-bottom' : ''} `}>
  56. {/* Collapsed SlackNotification */}
  57. {isSlackConfigured && (
  58. <Collapse isOpen={isSlackExpanded && isDeviceSmallerThanMd === true}>
  59. <nav className={`navbar navbar-expand-lg border-top ${additionalClasses.join(' ')}`}>
  60. <SlackNotification
  61. isSlackEnabled={isSlackEnabled ?? false}
  62. slackChannels={slackChannels}
  63. onEnabledFlagChange={isSlackEnabledToggleHandler}
  64. onChannelChange={slackChannelsChangedHandler}
  65. id="idForEditorNavbarBottomForMobile"
  66. />
  67. </nav>
  68. </Collapse>
  69. )
  70. }
  71. <div className={`navbar navbar-expand border-top px-2 px-md-3 ${additionalClasses.join(' ')}`}>
  72. <form className="form-inline">
  73. { isDeviceSmallerThanMd && renderDrawerButton() }
  74. { isOptionsSelectorEnabled && !isDeviceSmallerThanMd && <OptionsSelector /> }
  75. </form>
  76. <form className="form-inline flex-nowrap ml-auto">
  77. {/* Responsive Design for the SlackNotification */}
  78. {/* Button or the normal Slack banner */}
  79. {isSlackConfigured && (isDeviceSmallerThanMd ? (
  80. <Button
  81. className="grw-btn-slack border mr-2"
  82. onClick={() => (setSlackExpanded(!isSlackExpanded))}
  83. >
  84. <div className="grw-slack-logo">
  85. <SlackLogo />
  86. <span className="grw-btn-slack-triangle fa fa-caret-up ml-2"></span>
  87. </div>
  88. </Button>
  89. ) : (
  90. <div className="mr-2">
  91. <SlackNotification
  92. isSlackEnabled={isSlackEnabled ?? false}
  93. slackChannels={slackChannels}
  94. onEnabledFlagChange={isSlackEnabledToggleHandler}
  95. onChannelChange={slackChannelsChangedHandler}
  96. id="idForEditorNavbarBottom"
  97. />
  98. </div>
  99. ))}
  100. <SavePageControls />
  101. { isCollapsedOptionsSelectorEnabled && renderExpandButton() }
  102. </form>
  103. </div>
  104. {/* Collapsed OptionsSelector */}
  105. { isCollapsedOptionsSelectorEnabled && (
  106. <Collapse isOpen={isExpanded}>
  107. <div className="px-2"> {/* set padding for border-top */}
  108. <div className={`navbar navbar-expand border-top px-0 ${additionalClasses.join(' ')}`}>
  109. <form className="form-inline ml-auto">
  110. <OptionsSelector />
  111. </form>
  112. </div>
  113. </div>
  114. </Collapse>
  115. ) }
  116. </div>
  117. );
  118. };
  119. EditorNavbarBottom.propTypes = {
  120. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  121. editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
  122. };
  123. export default withUnstatedContainers(EditorNavbarBottom, [EditorContainer, AppContainer]);