AppearanceModeDropdown.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import React, {
  2. FC, useCallback, useRef,
  3. } from 'react';
  4. import { useTranslation } from 'next-i18next';
  5. import { useRipple } from 'react-use-ripple';
  6. import { UncontrolledTooltip } from 'reactstrap';
  7. import { useUserUISettings } from '~/client/services/user-ui-settings';
  8. import { usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser } from '~/stores/ui';
  9. import { Themes, useNextThemes } from '~/stores/use-next-themes';
  10. import MoonIcon from '../Icons/MoonIcon';
  11. import SidebarDockIcon from '../Icons/SidebarDockIcon';
  12. import SidebarDrawerIcon from '../Icons/SidebarDrawerIcon';
  13. import SunIcon from '../Icons/SunIcon';
  14. type AppearanceModeDropdownProps = {
  15. isAuthenticated: boolean,
  16. }
  17. export const AppearanceModeDropdown:FC<AppearanceModeDropdownProps> = (props: AppearanceModeDropdownProps) => {
  18. const { t } = useTranslation('commons');
  19. const { isAuthenticated } = props;
  20. const {
  21. setTheme, resolvedTheme, useOsSettings, isDarkMode, isForcedByGrowiTheme,
  22. } = useNextThemes();
  23. const { data: isPreferDrawerMode, update: updatePreferDrawerMode } = usePreferDrawerModeByUser();
  24. const { data: isPreferDrawerModeOnEdit, mutate: mutatePreferDrawerModeOnEdit } = usePreferDrawerModeOnEditByUser();
  25. const { scheduleToPut } = useUserUISettings();
  26. // ripple
  27. const buttonRef = useRef(null);
  28. useRipple(buttonRef, { rippleColor: 'rgba(255, 255, 255, 0.3)' });
  29. const preferDrawerModeSwitchModifiedHandler = useCallback((preferDrawerMode: boolean, isEditMode: boolean) => {
  30. if (isEditMode) {
  31. mutatePreferDrawerModeOnEdit(preferDrawerMode);
  32. scheduleToPut({ preferDrawerModeOnEditByUser: preferDrawerMode });
  33. }
  34. else {
  35. updatePreferDrawerMode(preferDrawerMode);
  36. }
  37. }, [updatePreferDrawerMode, mutatePreferDrawerModeOnEdit, scheduleToPut]);
  38. const followOsCheckboxModifiedHandler = useCallback((isChecked: boolean) => {
  39. if (isChecked) {
  40. setTheme(Themes.SYSTEM);
  41. }
  42. else {
  43. setTheme(resolvedTheme ?? Themes.LIGHT);
  44. }
  45. }, [resolvedTheme, setTheme]);
  46. const userPreferenceSwitchModifiedHandler = useCallback((isDarkMode: boolean) => {
  47. setTheme(isDarkMode ? Themes.DARK : Themes.LIGHT);
  48. }, [setTheme]);
  49. /* eslint-disable react/prop-types */
  50. const IconWithTooltip = ({
  51. id, label, children, additionalClasses,
  52. }) => (
  53. <>
  54. <div id={id} className={`px-2 grw-icon-container ${additionalClasses != null ? additionalClasses : ''}`}>{children}</div>
  55. <UncontrolledTooltip placement="bottom" fade={false} target={id}>{label}</UncontrolledTooltip>
  56. </>
  57. );
  58. const dropdownDivider = <div className="dropdown-divider"></div>;
  59. const renderSidebarModeSwitch = useCallback((isEditMode: boolean) => {
  60. return (
  61. <>
  62. <h6 className="dropdown-header">{t(isEditMode ? 'personal_dropdown.sidebar_mode_editor' : 'personal_dropdown.sidebar_mode')}</h6>
  63. <form className="px-4">
  64. <div className="form-row justify-content-center">
  65. <div className="form-group col-auto mb-0 d-flex align-items-center">
  66. <IconWithTooltip id={isEditMode ? 'iwt-sidebar-editor-drawer' : 'iwt-sidebar-drawer'} label="Drawer" additionalClasses="grw-sidebar-mode-icon">
  67. <SidebarDrawerIcon />
  68. </IconWithTooltip>
  69. <div className="form-check form-switch form-check-secondary ml-2">
  70. <input
  71. id={isEditMode ? 'swSidebarModeOnEditor' : 'swSidebarMode'}
  72. className="form-check-input"
  73. type="checkbox"
  74. checked={isEditMode ? !isPreferDrawerModeOnEdit : !isPreferDrawerMode}
  75. onChange={e => preferDrawerModeSwitchModifiedHandler(!e.target.checked, isEditMode)}
  76. />
  77. <label className="form-check-label" htmlFor={isEditMode ? 'swSidebarModeOnEditor' : 'swSidebarMode'}></label>
  78. </div>
  79. <IconWithTooltip id={isEditMode ? 'iwt-sidebar-editor-dock' : 'iwt-sidebar-dock'} label="Dock" additionalClasses="grw-sidebar-mode-icon">
  80. <SidebarDockIcon />
  81. </IconWithTooltip>
  82. </div>
  83. </div>
  84. </form>
  85. </>
  86. );
  87. }, [isPreferDrawerMode, isPreferDrawerModeOnEdit, preferDrawerModeSwitchModifiedHandler, t]);
  88. return (
  89. <div className="dropend">
  90. {/* setting button */}
  91. {/* remove .dropdown-toggle for hide caret */}
  92. {/* See https://stackoverflow.com/a/44577512/13183572 */}
  93. <button className="btn btn-primary" type="button" data-bs-toggle="dropdown" ref={buttonRef} aria-haspopup="true">
  94. <i className="material-icons">settings</i>
  95. </button>
  96. {/* dropdown */}
  97. <div className="dropdown-menu">
  98. {/* sidebar mode */}
  99. {renderSidebarModeSwitch(false)}
  100. {/* side bar mode on editor */}
  101. {isAuthenticated && (
  102. <>
  103. {dropdownDivider}
  104. {renderSidebarModeSwitch(true)}
  105. </>
  106. )}
  107. {/* color mode */}
  108. { !isForcedByGrowiTheme && (
  109. <>
  110. {dropdownDivider}
  111. <h6 className="dropdown-header">{t('personal_dropdown.color_mode')}</h6>
  112. <form className="px-4">
  113. <div className="form-row justify-content-center">
  114. <div className="form-group col-auto d-flex align-items-center">
  115. <IconWithTooltip id="iwt-light" label="Light" additionalClasses={useOsSettings ? 'grw-color-mode-icon-muted' : 'grw-color-mode-icon'}>
  116. <SunIcon />
  117. </IconWithTooltip>
  118. <div className="form-check form-switch form-check-secondary ml-2">
  119. <input
  120. id="swUserPreference"
  121. className="form-check-input"
  122. type="checkbox"
  123. checked={isDarkMode}
  124. disabled={useOsSettings}
  125. onChange={e => userPreferenceSwitchModifiedHandler(e.target.checked)}
  126. />
  127. <label className="form-check-label" htmlFor="swUserPreference"></label>
  128. </div>
  129. <IconWithTooltip id="iwt-dark" label="Dark" additionalClasses={useOsSettings ? 'grw-color-mode-icon-muted' : 'grw-color-mode-icon'}>
  130. <MoonIcon />
  131. </IconWithTooltip>
  132. </div>
  133. </div>
  134. <div className="form-row">
  135. <div className="form-group col-auto">
  136. <div className="form-check">
  137. <input
  138. id="cbFollowOs"
  139. className="form-check-input"
  140. type="checkbox"
  141. checked={useOsSettings}
  142. onChange={e => followOsCheckboxModifiedHandler(e.target.checked)}
  143. />
  144. <label className="form-check-label text-nowrap" htmlFor="cbFollowOs">{t('personal_dropdown.use_os_settings')}</label>
  145. </div>
  146. </div>
  147. </div>
  148. </form>
  149. </>
  150. ) }
  151. </div>
  152. </div>
  153. );
  154. };