AppearanceModeDropdown.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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,
  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 renderSidebarModeSwitch = useCallback((isEditMode: boolean) => {
  59. return (
  60. <>
  61. <h6 className="dropdown-header">{t(isEditMode ? 'personal_dropdown.sidebar_mode_editor' : 'personal_dropdown.sidebar_mode')}</h6>
  62. <form className="px-4">
  63. <div className="form-row justify-content-center">
  64. <div className="form-group col-auto mb-0 d-flex align-items-center">
  65. <IconWithTooltip id={isEditMode ? 'iwt-sidebar-editor-drawer' : 'iwt-sidebar-drawer'} label="Drawer" additionalClasses="grw-sidebar-mode-icon">
  66. <SidebarDrawerIcon />
  67. </IconWithTooltip>
  68. <div className="custom-control custom-switch custom-checkbox-secondary ml-2">
  69. <input
  70. id={isEditMode ? 'swSidebarModeOnEditor' : 'swSidebarMode'}
  71. className="custom-control-input"
  72. type="checkbox"
  73. checked={isEditMode ? !isPreferDrawerModeOnEdit : !isPreferDrawerMode}
  74. onChange={e => preferDrawerModeSwitchModifiedHandler(!e.target.checked, isEditMode)}
  75. />
  76. <label className="custom-control-label" htmlFor={isEditMode ? 'swSidebarModeOnEditor' : 'swSidebarMode'}></label>
  77. </div>
  78. <IconWithTooltip id={isEditMode ? 'iwt-sidebar-editor-dock' : 'iwt-sidebar-dock'} label="Dock" additionalClasses="grw-sidebar-mode-icon">
  79. <SidebarDockIcon />
  80. </IconWithTooltip>
  81. </div>
  82. </div>
  83. </form>
  84. </>
  85. );
  86. }, [isPreferDrawerMode, isPreferDrawerModeOnEdit, preferDrawerModeSwitchModifiedHandler, t]);
  87. return (
  88. <>
  89. {/* setting button */}
  90. {/* remove .dropdown-toggle for hide caret */}
  91. {/* See https://stackoverflow.com/a/44577512/13183572 */}
  92. <button className="bg-transparent border-0 nav-link" type="button" data-toggle="dropdown" ref={buttonRef} aria-haspopup="true">
  93. <i className="icon-settings"></i>
  94. </button>
  95. {/* dropdown */}
  96. <div className="dropdown-menu dropdown-menu-right">
  97. {/* sidebar mode */}
  98. {renderSidebarModeSwitch(false)}
  99. <div className="dropdown-divider"></div>
  100. {/* side bar mode on editor */}
  101. {isAuthenticated && renderSidebarModeSwitch(true)}
  102. <div className="dropdown-divider"></div>
  103. {/* color mode */}
  104. <h6 className="dropdown-header">{t('personal_dropdown.color_mode')}</h6>
  105. <form className="px-4">
  106. <div className="form-row justify-content-center">
  107. <div className="form-group col-auto d-flex align-items-center">
  108. <IconWithTooltip id="iwt-light" label="Light" additionalClasses={useOsSettings ? 'grw-color-mode-icon-muted' : 'grw-color-mode-icon'}>
  109. <SunIcon />
  110. </IconWithTooltip>
  111. <div className="custom-control custom-switch custom-checkbox-secondary ml-2">
  112. <input
  113. id="swUserPreference"
  114. className="custom-control-input"
  115. type="checkbox"
  116. checked={isDarkMode}
  117. disabled={useOsSettings}
  118. onChange={e => userPreferenceSwitchModifiedHandler(e.target.checked)}
  119. />
  120. <label className="custom-control-label" htmlFor="swUserPreference"></label>
  121. </div>
  122. <IconWithTooltip id="iwt-dark" label="Dark" additionalClasses={useOsSettings ? 'grw-color-mode-icon-muted' : 'grw-color-mode-icon'}>
  123. <MoonIcon />
  124. </IconWithTooltip>
  125. </div>
  126. </div>
  127. <div className="form-row">
  128. <div className="form-group col-auto">
  129. <div className="custom-control custom-checkbox">
  130. <input
  131. id="cbFollowOs"
  132. className="custom-control-input"
  133. type="checkbox"
  134. checked={useOsSettings}
  135. onChange={e => followOsCheckboxModifiedHandler(e.target.checked)}
  136. />
  137. <label className="custom-control-label text-nowrap" htmlFor="cbFollowOs">{t('personal_dropdown.use_os_settings')}</label>
  138. </div>
  139. </div>
  140. </div>
  141. </form>
  142. </div>
  143. </>
  144. );
  145. };