|
|
@@ -1,283 +1,186 @@
|
|
|
-import React from 'react';
|
|
|
+import React, {
|
|
|
+ memo, useCallback, useMemo, useState,
|
|
|
+} from 'react';
|
|
|
|
|
|
-import PropTypes from 'prop-types';
|
|
|
-import { withTranslation } from 'react-i18next';
|
|
|
+import { useTranslation } from 'react-i18next';
|
|
|
import {
|
|
|
Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
|
|
|
} from 'reactstrap';
|
|
|
|
|
|
import AppContainer from '~/client/services/AppContainer';
|
|
|
import EditorContainer from '~/client/services/EditorContainer';
|
|
|
-import { toastError } from '~/client/util/apiNotification';
|
|
|
-import { apiv3Put } from '~/client/util/apiv3-client';
|
|
|
+import { useEditorSettings, useIsTextlintEnabled } from '~/stores/editor';
|
|
|
|
|
|
+import { KeyMapMode } from '../../interfaces/editor-settings';
|
|
|
import { withUnstatedContainers } from '../UnstatedUtils';
|
|
|
|
|
|
import { DownloadDictModal } from './DownloadDictModal';
|
|
|
|
|
|
|
|
|
-export const defaultEditorOptions = {
|
|
|
- theme: 'elegant',
|
|
|
- keymapMode: 'default',
|
|
|
- styleActiveLine: false,
|
|
|
-};
|
|
|
-
|
|
|
-export const defaultPreviewOptions = {
|
|
|
- renderMathJaxInRealtime: false,
|
|
|
- renderDrawioInRealtime: true,
|
|
|
-};
|
|
|
-
|
|
|
-class OptionsSelector extends React.Component {
|
|
|
-
|
|
|
- constructor(props) {
|
|
|
- super(props);
|
|
|
-
|
|
|
- const config = this.props.appContainer.getConfig();
|
|
|
- const isMathJaxEnabled = !!config.env.MATHJAX;
|
|
|
-
|
|
|
- this.state = {
|
|
|
- isCddMenuOpened: false,
|
|
|
- isMathJaxEnabled,
|
|
|
- isDownloadDictModalShown: false,
|
|
|
- isSkipAskingAgainChecked: false,
|
|
|
- };
|
|
|
-
|
|
|
- this.availableThemes = [
|
|
|
- 'eclipse', 'elegant', 'neo', 'mdn-like', 'material', 'dracula', 'monokai', 'twilight',
|
|
|
- ];
|
|
|
- this.keymapModes = {
|
|
|
- default: 'Default',
|
|
|
- vim: 'Vim',
|
|
|
- emacs: 'Emacs',
|
|
|
- sublime: 'Sublime Text',
|
|
|
- };
|
|
|
- this.typicalIndentSizes = [2, 4];
|
|
|
-
|
|
|
- this.onChangeTheme = this.onChangeTheme.bind(this);
|
|
|
- this.onChangeKeymapMode = this.onChangeKeymapMode.bind(this);
|
|
|
- this.onClickStyleActiveLine = this.onClickStyleActiveLine.bind(this);
|
|
|
- this.onClickRenderMathJaxInRealtime = this.onClickRenderMathJaxInRealtime.bind(this);
|
|
|
- this.onClickRenderDrawioInRealtime = this.onClickRenderDrawioInRealtime.bind(this);
|
|
|
- this.onClickMarkdownTableAutoFormatting = this.onClickMarkdownTableAutoFormatting.bind(this);
|
|
|
- this.switchTextlintEnabledHandler = this.switchTextlintEnabledHandler.bind(this);
|
|
|
- this.confirmEnableTextlintHandler = this.confirmEnableTextlintHandler.bind(this);
|
|
|
- this.toggleTextlint = this.toggleTextlint.bind(this);
|
|
|
- this.updateIsTextlintEnabledToDB = this.updateIsTextlintEnabledToDB.bind(this);
|
|
|
- this.onToggleConfigurationDropdown = this.onToggleConfigurationDropdown.bind(this);
|
|
|
- this.onChangeIndentSize = this.onChangeIndentSize.bind(this);
|
|
|
- }
|
|
|
-
|
|
|
- onChangeTheme(newValue) {
|
|
|
- const { editorContainer } = this.props;
|
|
|
-
|
|
|
- const newOpts = Object.assign(editorContainer.state.editorOptions, { theme: newValue });
|
|
|
- editorContainer.setState({ editorOptions: newOpts });
|
|
|
-
|
|
|
- // save to localStorage
|
|
|
- editorContainer.saveOptsToLocalStorage();
|
|
|
- }
|
|
|
-
|
|
|
- onChangeKeymapMode(newValue) {
|
|
|
- const { editorContainer } = this.props;
|
|
|
-
|
|
|
- const newOpts = Object.assign(editorContainer.state.editorOptions, { keymapMode: newValue });
|
|
|
- editorContainer.setState({ editorOptions: newOpts });
|
|
|
-
|
|
|
- // save to localStorage
|
|
|
- editorContainer.saveOptsToLocalStorage();
|
|
|
- }
|
|
|
-
|
|
|
- onClickStyleActiveLine(event) {
|
|
|
- const { editorContainer } = this.props;
|
|
|
-
|
|
|
- // keep dropdown opened
|
|
|
- this._cddForceOpen = true;
|
|
|
-
|
|
|
- const newValue = !editorContainer.state.editorOptions.styleActiveLine;
|
|
|
- const newOpts = Object.assign(editorContainer.state.editorOptions, { styleActiveLine: newValue });
|
|
|
- editorContainer.setState({ editorOptions: newOpts });
|
|
|
-
|
|
|
- // save to localStorage
|
|
|
- editorContainer.saveOptsToLocalStorage();
|
|
|
- }
|
|
|
-
|
|
|
- onClickRenderMathJaxInRealtime(event) {
|
|
|
- const { editorContainer } = this.props;
|
|
|
-
|
|
|
- const newValue = !editorContainer.state.previewOptions.renderMathJaxInRealtime;
|
|
|
- const newOpts = Object.assign(editorContainer.state.previewOptions, { renderMathJaxInRealtime: newValue });
|
|
|
- editorContainer.setState({ previewOptions: newOpts });
|
|
|
-
|
|
|
- // save to localStorage
|
|
|
- editorContainer.saveOptsToLocalStorage();
|
|
|
- }
|
|
|
+const AVAILABLE_THEMES = [
|
|
|
+ 'eclipse', 'elegant', 'neo', 'mdn-like', 'material', 'dracula', 'monokai', 'twilight',
|
|
|
+];
|
|
|
|
|
|
- onClickRenderDrawioInRealtime(event) {
|
|
|
- const { editorContainer } = this.props;
|
|
|
+const TYPICAL_INDENT_SIZE = [2, 4];
|
|
|
|
|
|
- const newValue = !editorContainer.state.previewOptions.renderDrawioInRealtime;
|
|
|
- const newOpts = Object.assign(editorContainer.state.previewOptions, { renderDrawioInRealtime: newValue });
|
|
|
- editorContainer.setState({ previewOptions: newOpts });
|
|
|
|
|
|
- // save to localStorage
|
|
|
- editorContainer.saveOptsToLocalStorage();
|
|
|
- }
|
|
|
+const ThemeSelector = (): JSX.Element => {
|
|
|
|
|
|
- onClickMarkdownTableAutoFormatting(event) {
|
|
|
- const { editorContainer } = this.props;
|
|
|
+ const { data: editorSettings, update } = useEditorSettings();
|
|
|
|
|
|
- const newValue = !editorContainer.state.editorOptions.ignoreMarkdownTableAutoFormatting;
|
|
|
- const newOpts = Object.assign(editorContainer.state.editorOptions, { ignoreMarkdownTableAutoFormatting: newValue });
|
|
|
- editorContainer.setState({ editorOptions: newOpts });
|
|
|
+ const menuItems = useMemo(() => (
|
|
|
+ <>
|
|
|
+ { AVAILABLE_THEMES.map((theme) => {
|
|
|
+ return <button key={theme} className="dropdown-item" type="button" onClick={() => update({ theme })}>{theme}</button>;
|
|
|
+ }) }
|
|
|
+ </>
|
|
|
+ ), [update]);
|
|
|
|
|
|
- // save to localStorage
|
|
|
- editorContainer.saveOptsToLocalStorage();
|
|
|
- }
|
|
|
+ const selectedTheme = editorSettings?.theme ?? 'elegant';
|
|
|
|
|
|
- async updateIsTextlintEnabledToDB(newVal) {
|
|
|
- try {
|
|
|
- await apiv3Put('/personal-setting/editor-settings', { textlintSettings: { isTextlintEnabled: newVal } });
|
|
|
- }
|
|
|
- catch (err) {
|
|
|
- toastError(err);
|
|
|
- }
|
|
|
- }
|
|
|
+ return (
|
|
|
+ <div className="input-group flex-nowrap">
|
|
|
+ <div className="input-group-prepend">
|
|
|
+ <span className="input-group-text" id="igt-theme">Theme</span>
|
|
|
+ </div>
|
|
|
+ <div className="input-group-append dropup">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ className="btn btn-outline-secondary dropdown-toggle"
|
|
|
+ data-toggle="dropdown"
|
|
|
+ aria-haspopup="true"
|
|
|
+ aria-expanded="false"
|
|
|
+ aria-describedby="igt-theme"
|
|
|
+ >
|
|
|
+ {selectedTheme}
|
|
|
+ </button>
|
|
|
+ <div className="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
|
|
+ {menuItems}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|
|
|
|
|
|
- toggleTextlint() {
|
|
|
- const { editorContainer } = this.props;
|
|
|
- const newVal = !editorContainer.state.isTextlintEnabled;
|
|
|
- editorContainer.setState({ isTextlintEnabled: newVal });
|
|
|
- if (this.state.isSkipAskingAgainChecked) {
|
|
|
- this.updateIsTextlintEnabledToDB(newVal);
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- switchTextlintEnabledHandler() {
|
|
|
- const { editorContainer } = this.props;
|
|
|
- if (editorContainer.state.isTextlintEnabled === null) {
|
|
|
- this.setState({ isDownloadDictModalShown: true });
|
|
|
- return;
|
|
|
- }
|
|
|
- this.toggleTextlint();
|
|
|
- }
|
|
|
+type KeyMapModeToLabel = {
|
|
|
+ [key in KeyMapMode]: string;
|
|
|
+}
|
|
|
|
|
|
- confirmEnableTextlintHandler(isSkipAskingAgainChecked) {
|
|
|
- this.setState(
|
|
|
- { isSkipAskingAgainChecked, isDownloadDictModalShown: false },
|
|
|
- () => this.toggleTextlint(),
|
|
|
- );
|
|
|
- }
|
|
|
+const KEYMAP_LABEL_MAP: KeyMapModeToLabel = {
|
|
|
+ default: 'Default',
|
|
|
+ vim: 'Vim',
|
|
|
+ emacs: 'Emacs',
|
|
|
+ sublime: 'Sublime Text',
|
|
|
+};
|
|
|
|
|
|
- onToggleConfigurationDropdown(newValue) {
|
|
|
- this.setState({ isCddMenuOpened: !this.state.isCddMenuOpened });
|
|
|
- }
|
|
|
+const KeymapSelector = memo((): JSX.Element => {
|
|
|
+
|
|
|
+ const { data: editorSettings, update } = useEditorSettings();
|
|
|
+
|
|
|
+ Object.keys(KEYMAP_LABEL_MAP);
|
|
|
+ const menuItems = useMemo(() => (
|
|
|
+ <>
|
|
|
+ { (Object.keys(KEYMAP_LABEL_MAP) as KeyMapMode[]).map((keymapMode) => {
|
|
|
+ const keymapLabel = KEYMAP_LABEL_MAP[keymapMode];
|
|
|
+ const icon = (keymapMode !== 'default')
|
|
|
+ ? <img src={`/images/icons/${keymapMode}.png`} width="16px" className="mr-2"></img>
|
|
|
+ : null;
|
|
|
+ return <button key={keymapMode} className="dropdown-item" type="button" onClick={() => update({ keymapMode })}>{icon}{keymapLabel}</button>;
|
|
|
+ }) }
|
|
|
+ </>
|
|
|
+ ), [update]);
|
|
|
+
|
|
|
+ const selectedKeymapMode = editorSettings?.keymapMode ?? 'default';
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="input-group flex-nowrap">
|
|
|
+ <div className="input-group-prepend">
|
|
|
+ <span className="input-group-text" id="igt-keymap">Keymap</span>
|
|
|
+ </div>
|
|
|
+ <div className="input-group-append dropup">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ className="btn btn-outline-secondary dropdown-toggle"
|
|
|
+ data-toggle="dropdown"
|
|
|
+ aria-haspopup="true"
|
|
|
+ aria-expanded="false"
|
|
|
+ aria-describedby="igt-keymap"
|
|
|
+ >
|
|
|
+ { editorSettings != null && selectedKeymapMode}
|
|
|
+ </button>
|
|
|
+ <div className="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
|
|
+ {menuItems}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
|
|
|
- onChangeIndentSize(newValue) {
|
|
|
- const { editorContainer } = this.props;
|
|
|
- editorContainer.setState({ indentSize: newValue });
|
|
|
- }
|
|
|
+});
|
|
|
|
|
|
- renderThemeSelector() {
|
|
|
- const { editorContainer } = this.props;
|
|
|
|
|
|
- const selectedTheme = editorContainer.state.editorOptions.theme;
|
|
|
- const menuItems = this.availableThemes.map((theme) => {
|
|
|
- return <button key={theme} className="dropdown-item" type="button" onClick={() => this.onChangeTheme(theme)}>{theme}</button>;
|
|
|
- });
|
|
|
+type IndentSizeSelectorProps = {
|
|
|
+ isIndentSizeForced: boolean,
|
|
|
+ selectedIndentSize: number,
|
|
|
+ onChange: (indentSize: number) => void,
|
|
|
+}
|
|
|
|
|
|
- return (
|
|
|
- <div className="input-group flex-nowrap">
|
|
|
- <div className="input-group-prepend">
|
|
|
- <span className="input-group-text" id="igt-theme">Theme</span>
|
|
|
- </div>
|
|
|
- <div className="input-group-append dropup">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- className="btn btn-outline-secondary dropdown-toggle"
|
|
|
- data-toggle="dropdown"
|
|
|
- aria-haspopup="true"
|
|
|
- aria-expanded="false"
|
|
|
- aria-describedby="igt-theme"
|
|
|
- >
|
|
|
- {selectedTheme}
|
|
|
- </button>
|
|
|
- <div className="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
|
|
- {menuItems}
|
|
|
- </div>
|
|
|
+const IndentSizeSelector = memo(({ isIndentSizeForced, selectedIndentSize, onChange }: IndentSizeSelectorProps): JSX.Element => {
|
|
|
+ const menuItems = useMemo(() => (
|
|
|
+ <>
|
|
|
+ { TYPICAL_INDENT_SIZE.map((indent) => {
|
|
|
+ return <button key={indent} className="dropdown-item" type="button" onClick={() => onChange(indent)}>{indent}</button>;
|
|
|
+ }) }
|
|
|
+ </>
|
|
|
+ ), [onChange]);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="input-group flex-nowrap">
|
|
|
+ <div className="input-group-prepend">
|
|
|
+ <span className="input-group-text" id="igt-indent">Indent</span>
|
|
|
+ </div>
|
|
|
+ <div className="input-group-append dropup">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ className="btn btn-outline-secondary dropdown-toggle"
|
|
|
+ data-toggle="dropdown"
|
|
|
+ aria-haspopup="true"
|
|
|
+ aria-expanded="false"
|
|
|
+ aria-describedby="igt-indent"
|
|
|
+ disabled={isIndentSizeForced}
|
|
|
+ >
|
|
|
+ {selectedIndentSize}
|
|
|
+ </button>
|
|
|
+ <div className="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
|
|
+ {menuItems}
|
|
|
</div>
|
|
|
</div>
|
|
|
- );
|
|
|
- }
|
|
|
+ </div>
|
|
|
+ );
|
|
|
|
|
|
- renderKeymapModeSelector() {
|
|
|
- const { editorContainer } = this.props;
|
|
|
+});
|
|
|
|
|
|
- const selectedKeymapMode = editorContainer.state.editorOptions.keymapMode;
|
|
|
- const menuItems = Object.keys(this.keymapModes).map((mode) => {
|
|
|
- const label = this.keymapModes[mode];
|
|
|
- const icon = (mode !== 'default')
|
|
|
- ? <img src={`/images/icons/${mode}.png`} width="16px" className="mr-2"></img>
|
|
|
- : null;
|
|
|
- return <button key={mode} className="dropdown-item" type="button" onClick={() => this.onChangeKeymapMode(mode)}>{icon}{label}</button>;
|
|
|
- });
|
|
|
|
|
|
- return (
|
|
|
- <div className="input-group flex-nowrap">
|
|
|
- <div className="input-group-prepend">
|
|
|
- <span className="input-group-text" id="igt-keymap">Keymap</span>
|
|
|
- </div>
|
|
|
- <div className="input-group-append dropup">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- className="btn btn-outline-secondary dropdown-toggle"
|
|
|
- data-toggle="dropdown"
|
|
|
- aria-haspopup="true"
|
|
|
- aria-expanded="false"
|
|
|
- aria-describedby="igt-keymap"
|
|
|
- >
|
|
|
- {selectedKeymapMode}
|
|
|
- </button>
|
|
|
- <div className="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
|
|
- {menuItems}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
+type ConfigurationDropdownProps = {
|
|
|
+ isMathJaxEnabled: boolean,
|
|
|
+ onConfirmEnableTextlint?: () => void,
|
|
|
+}
|
|
|
|
|
|
- renderConfigurationDropdown() {
|
|
|
- return (
|
|
|
- <div className="my-0 form-group">
|
|
|
+const ConfigurationDropdown = memo(({ isMathJaxEnabled, onConfirmEnableTextlint }: ConfigurationDropdownProps): JSX.Element => {
|
|
|
+ const { t } = useTranslation();
|
|
|
|
|
|
- <Dropdown
|
|
|
- direction="up"
|
|
|
- className="grw-editor-configuration-dropdown"
|
|
|
- isOpen={this.state.isCddMenuOpened}
|
|
|
- toggle={this.onToggleConfigurationDropdown}
|
|
|
- >
|
|
|
+ const [isCddMenuOpened, setCddMenuOpened] = useState(false);
|
|
|
|
|
|
- <DropdownToggle color="outline-secondary" caret>
|
|
|
- <i className="icon-settings"></i>
|
|
|
- </DropdownToggle>
|
|
|
+ const { data: editorSettings, update } = useEditorSettings();
|
|
|
|
|
|
- <DropdownMenu>
|
|
|
- {this.renderActiveLineMenuItem()}
|
|
|
- {this.renderRealtimeMathJaxMenuItem()}
|
|
|
- {this.renderRealtimeDrawioMenuItem()}
|
|
|
- {this.renderMarkdownTableAutoFormattingMenuItem()}
|
|
|
- {this.renderIsTextlintEnabledMenuItem()}
|
|
|
- {/* <DropdownItem divider /> */}
|
|
|
- </DropdownMenu>
|
|
|
+ const { data: isTextlintEnabled, mutate: mutateTextlintEnabled } = useIsTextlintEnabled();
|
|
|
|
|
|
- </Dropdown>
|
|
|
-
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
+ const renderActiveLineMenuItem = useCallback(() => {
|
|
|
+ if (editorSettings == null) {
|
|
|
+ return <></>;
|
|
|
+ }
|
|
|
|
|
|
- renderActiveLineMenuItem() {
|
|
|
- const { t, editorContainer } = this.props;
|
|
|
- const isActive = editorContainer.state.editorOptions.styleActiveLine;
|
|
|
+ const isActive = editorSettings.styleActiveLine;
|
|
|
|
|
|
const iconClasses = ['text-info'];
|
|
|
if (isActive) {
|
|
|
@@ -286,7 +189,7 @@ class OptionsSelector extends React.Component {
|
|
|
const iconClassName = iconClasses.join(' ');
|
|
|
|
|
|
return (
|
|
|
- <DropdownItem toggle={false} onClick={this.onClickStyleActiveLine}>
|
|
|
+ <DropdownItem toggle={false} onClick={() => update({ styleActiveLine: !isActive })}>
|
|
|
<div className="d-flex justify-content-between">
|
|
|
<span className="icon-container"></span>
|
|
|
<span className="menuitem-label">{ t('page_edit.Show active line') }</span>
|
|
|
@@ -294,17 +197,18 @@ class OptionsSelector extends React.Component {
|
|
|
</div>
|
|
|
</DropdownItem>
|
|
|
);
|
|
|
- }
|
|
|
+ }, [editorSettings, update, t]);
|
|
|
|
|
|
- renderRealtimeMathJaxMenuItem() {
|
|
|
- if (!this.state.isMathJaxEnabled) {
|
|
|
- return;
|
|
|
+ const renderRealtimeMathJaxMenuItem = useCallback(() => {
|
|
|
+ if (editorSettings == null) {
|
|
|
+ return <></>;
|
|
|
}
|
|
|
|
|
|
- const { editorContainer } = this.props;
|
|
|
+ if (!isMathJaxEnabled) {
|
|
|
+ return <></>;
|
|
|
+ }
|
|
|
|
|
|
- const isEnabled = this.state.isMathJaxEnabled;
|
|
|
- const isActive = isEnabled && editorContainer.state.previewOptions.renderMathJaxInRealtime;
|
|
|
+ const isActive = editorSettings.renderMathJaxInRealtime;
|
|
|
|
|
|
const iconClasses = ['text-info'];
|
|
|
if (isActive) {
|
|
|
@@ -313,7 +217,7 @@ class OptionsSelector extends React.Component {
|
|
|
const iconClassName = iconClasses.join(' ');
|
|
|
|
|
|
return (
|
|
|
- <DropdownItem toggle={false} onClick={this.onClickRenderMathJaxInRealtime}>
|
|
|
+ <DropdownItem toggle={false} onClick={() => update({ renderMathJaxInRealtime: !isActive })}>
|
|
|
<div className="d-flex justify-content-between">
|
|
|
<span className="icon-container"><img src="/images/icons/fx.svg" width="14px" alt="fx"></img></span>
|
|
|
<span className="menuitem-label">MathJax Rendering</span>
|
|
|
@@ -321,12 +225,14 @@ class OptionsSelector extends React.Component {
|
|
|
</div>
|
|
|
</DropdownItem>
|
|
|
);
|
|
|
- }
|
|
|
+ }, [editorSettings, isMathJaxEnabled, update]);
|
|
|
|
|
|
- renderRealtimeDrawioMenuItem() {
|
|
|
- const { editorContainer } = this.props;
|
|
|
+ const renderRealtimeDrawioMenuItem = useCallback(() => {
|
|
|
+ if (editorSettings == null) {
|
|
|
+ return <></>;
|
|
|
+ }
|
|
|
|
|
|
- const isActive = editorContainer.state.previewOptions.renderDrawioInRealtime;
|
|
|
+ const isActive = editorSettings.renderDrawioInRealtime;
|
|
|
|
|
|
const iconClasses = ['text-info'];
|
|
|
if (isActive) {
|
|
|
@@ -335,7 +241,7 @@ class OptionsSelector extends React.Component {
|
|
|
const iconClassName = iconClasses.join(' ');
|
|
|
|
|
|
return (
|
|
|
- <DropdownItem toggle={false} onClick={this.onClickRenderDrawioInRealtime}>
|
|
|
+ <DropdownItem toggle={false} onClick={() => update({ renderDrawioInRealtime: !isActive })}>
|
|
|
<div className="d-flex justify-content-between">
|
|
|
<span className="icon-container"><img src="/images/icons/fx.svg" width="14px" alt="fx"></img></span>
|
|
|
<span className="menuitem-label">draw.io Rendering</span>
|
|
|
@@ -343,12 +249,14 @@ class OptionsSelector extends React.Component {
|
|
|
</div>
|
|
|
</DropdownItem>
|
|
|
);
|
|
|
- }
|
|
|
+ }, [editorSettings, update]);
|
|
|
|
|
|
- renderMarkdownTableAutoFormattingMenuItem() {
|
|
|
- const { t, editorContainer } = this.props;
|
|
|
- // Auto-formatting was enabled before optionalizing, so we made it a disabled option(ignoreMarkdownTableAutoFormatting).
|
|
|
- const isActive = !editorContainer.state.editorOptions.ignoreMarkdownTableAutoFormatting;
|
|
|
+ const renderMarkdownTableAutoFormattingMenuItem = useCallback(() => {
|
|
|
+ if (editorSettings == null) {
|
|
|
+ return <></>;
|
|
|
+ }
|
|
|
+
|
|
|
+ const isActive = editorSettings.autoFormatMarkdownTable;
|
|
|
|
|
|
const iconClasses = ['text-info'];
|
|
|
if (isActive) {
|
|
|
@@ -357,7 +265,7 @@ class OptionsSelector extends React.Component {
|
|
|
const iconClassName = iconClasses.join(' ');
|
|
|
|
|
|
return (
|
|
|
- <DropdownItem toggle={false} onClick={this.onClickMarkdownTableAutoFormatting}>
|
|
|
+ <DropdownItem toggle={false} onClick={() => update({ autoFormatMarkdownTable: !isActive })}>
|
|
|
<div className="d-flex justify-content-between">
|
|
|
<span className="icon-container"></span>
|
|
|
<span className="menuitem-label">{ t('page_edit.auto_format_table') }</span>
|
|
|
@@ -365,19 +273,37 @@ class OptionsSelector extends React.Component {
|
|
|
</div>
|
|
|
</DropdownItem>
|
|
|
);
|
|
|
- }
|
|
|
+ }, [editorSettings, t, update]);
|
|
|
|
|
|
- renderIsTextlintEnabledMenuItem() {
|
|
|
- const isActive = this.props.editorContainer.state.isTextlintEnabled;
|
|
|
+ const renderIsTextlintEnabledMenuItem = useCallback(() => {
|
|
|
+ if (editorSettings == null) {
|
|
|
+ return <></>;
|
|
|
+ }
|
|
|
+
|
|
|
+ const clickHandler = () => {
|
|
|
+ if (isTextlintEnabled) {
|
|
|
+ mutateTextlintEnabled(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (editorSettings.textlintSettings?.neverAskBeforeDownloadLargeFiles) {
|
|
|
+ mutateTextlintEnabled(true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (onConfirmEnableTextlint != null) {
|
|
|
+ onConfirmEnableTextlint();
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
const iconClasses = ['text-info'];
|
|
|
- if (isActive) {
|
|
|
+ if (isTextlintEnabled) {
|
|
|
iconClasses.push('ti-check');
|
|
|
}
|
|
|
const iconClassName = iconClasses.join(' ');
|
|
|
|
|
|
return (
|
|
|
- <DropdownItem toggle={false} onClick={this.switchTextlintEnabledHandler}>
|
|
|
+ <DropdownItem toggle={false} onClick={clickHandler}>
|
|
|
<div className="d-flex justify-content-between">
|
|
|
<span className="icon-container"></span>
|
|
|
<span className="menuitem-label">Textlint</span>
|
|
|
@@ -385,71 +311,99 @@ class OptionsSelector extends React.Component {
|
|
|
</div>
|
|
|
</DropdownItem>
|
|
|
);
|
|
|
- }
|
|
|
+ }, [editorSettings, isTextlintEnabled, mutateTextlintEnabled, onConfirmEnableTextlint]);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="my-0 form-group">
|
|
|
+ <Dropdown
|
|
|
+ direction="up"
|
|
|
+ className="grw-editor-configuration-dropdown"
|
|
|
+ isOpen={isCddMenuOpened}
|
|
|
+ toggle={() => setCddMenuOpened(!isCddMenuOpened)}
|
|
|
+ >
|
|
|
+
|
|
|
+ <DropdownToggle color="outline-secondary" caret>
|
|
|
+ <i className="icon-settings"></i>
|
|
|
+ </DropdownToggle>
|
|
|
+
|
|
|
+ <DropdownMenu>
|
|
|
+ {renderActiveLineMenuItem()}
|
|
|
+ {renderRealtimeMathJaxMenuItem()}
|
|
|
+ {renderRealtimeDrawioMenuItem()}
|
|
|
+ {renderMarkdownTableAutoFormattingMenuItem()}
|
|
|
+ {renderIsTextlintEnabledMenuItem()}
|
|
|
+ {/* <DropdownItem divider /> */}
|
|
|
+ </DropdownMenu>
|
|
|
+
|
|
|
+ </Dropdown>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+type Props = {
|
|
|
+ appContainer: AppContainer
|
|
|
+ editorContainer: EditorContainer,
|
|
|
+};
|
|
|
|
|
|
- renderIndentSizeSelector() {
|
|
|
- const { appContainer, editorContainer } = this.props;
|
|
|
- const menuItems = this.typicalIndentSizes.map((indent) => {
|
|
|
- return <button key={indent} className="dropdown-item" type="button" onClick={() => this.onChangeIndentSize(indent)}>{indent}</button>;
|
|
|
- });
|
|
|
- return (
|
|
|
- <div className="input-group flex-nowrap">
|
|
|
- <div className="input-group-prepend">
|
|
|
- <span className="input-group-text" id="igt-indent">Indent</span>
|
|
|
- </div>
|
|
|
- <div className="input-group-append dropup">
|
|
|
- <button
|
|
|
- type="button"
|
|
|
- className="btn btn-outline-secondary dropdown-toggle"
|
|
|
- data-toggle="dropdown"
|
|
|
- aria-haspopup="true"
|
|
|
- aria-expanded="false"
|
|
|
- aria-describedby="igt-indent"
|
|
|
- disabled={appContainer.config.isIndentSizeForced}
|
|
|
- >
|
|
|
- {editorContainer.state.indentSize}
|
|
|
- </button>
|
|
|
- <div className="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
|
|
- {menuItems}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
+const OptionsSelector = (props: Props): JSX.Element => {
|
|
|
+ const { appContainer, editorContainer } = props;
|
|
|
+ const config = appContainer.getConfig();
|
|
|
|
|
|
- render() {
|
|
|
- return (
|
|
|
- <>
|
|
|
- <div className="d-flex flex-row">
|
|
|
- <span>{this.renderThemeSelector()}</span>
|
|
|
- <span className="d-none d-sm-block ml-2 ml-sm-4">{this.renderKeymapModeSelector()}</span>
|
|
|
- <span className="ml-2 ml-sm-4">{this.renderIndentSizeSelector()}</span>
|
|
|
- <span className="ml-2 ml-sm-4">{this.renderConfigurationDropdown()}</span>
|
|
|
- </div>
|
|
|
+ const [isDownloadDictModalShown, setDownloadDictModalShown] = useState(false);
|
|
|
|
|
|
- {!this.state.isSkipAskingAgainChecked && (
|
|
|
- <DownloadDictModal
|
|
|
- isModalOpen={this.state.isDownloadDictModalShown}
|
|
|
- onConfirmEnableTextlint={this.confirmEnableTextlintHandler}
|
|
|
- onCancel={() => this.setState({ isDownloadDictModalShown: false })}
|
|
|
- />
|
|
|
- )}
|
|
|
- </>
|
|
|
- );
|
|
|
+ const { data: editorSettings, turnOffAskingBeforeDownloadLargeFiles } = useEditorSettings();
|
|
|
+ const { mutate: mutateTextlintEnabled } = useIsTextlintEnabled();
|
|
|
+
|
|
|
+ if (editorSettings == null) {
|
|
|
+ return <></>;
|
|
|
}
|
|
|
|
|
|
-}
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <div className="d-flex flex-row">
|
|
|
+ <span>
|
|
|
+ <ThemeSelector />
|
|
|
+ </span>
|
|
|
+ <span className="d-none d-sm-block ml-2 ml-sm-4">
|
|
|
+ <KeymapSelector />
|
|
|
+ </span>
|
|
|
+ <span className="ml-2 ml-sm-4">
|
|
|
+ <IndentSizeSelector
|
|
|
+ isIndentSizeForced={config.isIndentSizeForced}
|
|
|
+ selectedIndentSize={editorContainer.state.indentSize}
|
|
|
+ onChange={newIndentSize => editorContainer.setState({ indentSize: newIndentSize })}
|
|
|
+ />
|
|
|
+ </span>
|
|
|
+ <span className="ml-2 ml-sm-4">
|
|
|
+ <ConfigurationDropdown
|
|
|
+ isMathJaxEnabled={!!config.env.MATHJAX}
|
|
|
+ onConfirmEnableTextlint={() => setDownloadDictModalShown(true)}
|
|
|
+ />
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
|
|
|
-/**
|
|
|
- * Wrapper component for using unstated
|
|
|
- */
|
|
|
-const OptionsSelectorWrapper = withUnstatedContainers(OptionsSelector, [AppContainer, EditorContainer]);
|
|
|
+ { editorSettings != null && !editorSettings.textlintSettings?.neverAskBeforeDownloadLargeFiles && (
|
|
|
+ <DownloadDictModal
|
|
|
+ isModalOpen={isDownloadDictModalShown}
|
|
|
+ onEnableTextlint={(isSkipAskingAgainChecked) => {
|
|
|
+ mutateTextlintEnabled(true);
|
|
|
|
|
|
-OptionsSelector.propTypes = {
|
|
|
- t: PropTypes.func.isRequired, // i18next
|
|
|
+ if (isSkipAskingAgainChecked) {
|
|
|
+ turnOffAskingBeforeDownloadLargeFiles();
|
|
|
+ }
|
|
|
+
|
|
|
+ setDownloadDictModalShown(false);
|
|
|
+ }}
|
|
|
+ onCancel={() => setDownloadDictModalShown(false)}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ );
|
|
|
|
|
|
- appContainer: PropTypes.instanceOf(AppContainer).isRequired,
|
|
|
- editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
|
|
|
};
|
|
|
|
|
|
-export default withTranslation()(OptionsSelectorWrapper);
|
|
|
+
|
|
|
+const OptionsSelectorWrapper = withUnstatedContainers(OptionsSelector, [AppContainer, EditorContainer]);
|
|
|
+export default OptionsSelectorWrapper;
|