From 5e94b468daea9a916105943ea742df23130c6790 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Tue, 30 Jul 2024 22:11:31 -0700 Subject: [PATCH] Desktop: Refactor settings screen components and improve accessibility labelling --- .eslintignore | 6 +- .gitignore | 6 +- .../gui/ConfigScreen/ConfigScreen.tsx | 445 +----------------- .../{ => controls}/FontSearch.tsx | 2 + .../controls/SettingComponent.tsx | 381 +++++++++++++++ .../controls/SettingDescription.tsx | 12 + .../ConfigScreen/controls/SettingHeader.tsx | 15 + .../ConfigScreen/controls/SettingLabel.tsx | 16 + .../controls/plugins/PluginsStates.tsx | 13 +- .../controls/plugins/SearchPlugins.tsx | 5 +- .../app-desktop/gui/ConfigScreen/style.scss | 2 + .../gui/ConfigScreen/styles/index.scss | 4 + .../styles/setting-description.scss | 8 + .../ConfigScreen/styles/setting-header.scss | 9 + .../ConfigScreen/styles/setting-label.scss | 20 + packages/lib/models/Setting.ts | 2 +- 16 files changed, 507 insertions(+), 439 deletions(-) rename packages/app-desktop/gui/ConfigScreen/{ => controls}/FontSearch.tsx (99%) create mode 100644 packages/app-desktop/gui/ConfigScreen/controls/SettingComponent.tsx create mode 100644 packages/app-desktop/gui/ConfigScreen/controls/SettingDescription.tsx create mode 100644 packages/app-desktop/gui/ConfigScreen/controls/SettingHeader.tsx create mode 100644 packages/app-desktop/gui/ConfigScreen/controls/SettingLabel.tsx create mode 100644 packages/app-desktop/gui/ConfigScreen/styles/index.scss create mode 100644 packages/app-desktop/gui/ConfigScreen/styles/setting-description.scss create mode 100644 packages/app-desktop/gui/ConfigScreen/styles/setting-header.scss create mode 100644 packages/app-desktop/gui/ConfigScreen/styles/setting-label.scss diff --git a/.eslintignore b/.eslintignore index 10672a2d3f8..0d0c4c4e9d1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -167,9 +167,13 @@ packages/app-desktop/gui/Button/Button.js packages/app-desktop/gui/ClipperConfigScreen.js packages/app-desktop/gui/ConfigScreen/ButtonBar.js packages/app-desktop/gui/ConfigScreen/ConfigScreen.js -packages/app-desktop/gui/ConfigScreen/FontSearch.js packages/app-desktop/gui/ConfigScreen/Sidebar.js +packages/app-desktop/gui/ConfigScreen/controls/FontSearch.js packages/app-desktop/gui/ConfigScreen/controls/MissingPasswordHelpLink.js +packages/app-desktop/gui/ConfigScreen/controls/SettingComponent.js +packages/app-desktop/gui/ConfigScreen/controls/SettingDescription.js +packages/app-desktop/gui/ConfigScreen/controls/SettingHeader.js +packages/app-desktop/gui/ConfigScreen/controls/SettingLabel.js packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js diff --git a/.gitignore b/.gitignore index 710590954c6..9cc4e393b50 100644 --- a/.gitignore +++ b/.gitignore @@ -146,9 +146,13 @@ packages/app-desktop/gui/Button/Button.js packages/app-desktop/gui/ClipperConfigScreen.js packages/app-desktop/gui/ConfigScreen/ButtonBar.js packages/app-desktop/gui/ConfigScreen/ConfigScreen.js -packages/app-desktop/gui/ConfigScreen/FontSearch.js packages/app-desktop/gui/ConfigScreen/Sidebar.js +packages/app-desktop/gui/ConfigScreen/controls/FontSearch.js packages/app-desktop/gui/ConfigScreen/controls/MissingPasswordHelpLink.js +packages/app-desktop/gui/ConfigScreen/controls/SettingComponent.js +packages/app-desktop/gui/ConfigScreen/controls/SettingDescription.js +packages/app-desktop/gui/ConfigScreen/controls/SettingHeader.js +packages/app-desktop/gui/ConfigScreen/controls/SettingLabel.js packages/app-desktop/gui/ConfigScreen/controls/ToggleAdvancedSettingsButton.js packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginBox.js packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.js diff --git a/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx b/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx index 3778e00e373..55305d15e48 100644 --- a/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx +++ b/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx @@ -1,16 +1,14 @@ import * as React from 'react'; import Sidebar from './Sidebar'; import ButtonBar from './ButtonBar'; -import Button, { ButtonLevel, ButtonSize } from '../Button/Button'; +import Button, { ButtonLevel } from '../Button/Button'; import { _ } from '@joplin/lib/locale'; import bridge from '../../services/bridge'; -import Setting, { AppType, SettingItemSubType, SyncStartupOperation } from '@joplin/lib/models/Setting'; -import control_PluginsStates from './controls/plugins/PluginsStates'; +import Setting, { AppType, SettingValueType, SyncStartupOperation } from '@joplin/lib/models/Setting'; import EncryptionConfigScreen from '../EncryptionConfigScreen/EncryptionConfigScreen'; import { reg } from '@joplin/lib/registry'; const { connect } = require('react-redux'); -const { themeStyle } = require('@joplin/lib/theme'); -import * as pathUtils from '@joplin/lib/path-utils'; +import { themeStyle } from '@joplin/lib/theme'; import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry'; import * as shared from '@joplin/lib/components/shared/config/config-shared.js'; import ClipperConfigScreen from '../ClipperConfigScreen'; @@ -20,12 +18,8 @@ import ToggleAdvancedSettingsButton from './controls/ToggleAdvancedSettingsButto import shouldShowMissingPasswordWarning from '@joplin/lib/components/shared/config/shouldShowMissingPasswordWarning'; import MacOSMissingPasswordHelpLink from './controls/MissingPasswordHelpLink'; const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen'); -import FontSearch from './FontSearch'; +import SettingComponent, { UpdateSettingValueEvent } from './controls/SettingComponent'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied -const settingKeyToControl: any = { - 'plugins.states': control_PluginsStates, -}; interface Font { family: string; @@ -67,9 +61,6 @@ class ConfigScreenComponent extends React.Component { this.onCancelClick = this.onCancelClick.bind(this); this.onSaveClick = this.onSaveClick.bind(this); this.onApplyClick = this.onApplyClick.bind(this); - this.renderLabel = this.renderLabel.bind(this); - this.renderDescription = this.renderDescription.bind(this); - this.renderHeader = this.renderHeader.bind(this); this.handleSettingButton = this.handleSettingButton.bind(this); } @@ -298,420 +289,26 @@ class ConfigScreenComponent extends React.Component { ); } - private labelStyle(themeId: number) { - const theme = themeStyle(themeId); - return { ...theme.textStyle, display: 'block', - color: theme.color, - fontSize: theme.fontSize * 1.083333, - fontWeight: 500, - marginBottom: theme.mainPadding / 2 }; - } - - private descriptionStyle(themeId: number) { - const theme = themeStyle(themeId); - return { ...theme.textStyle, color: theme.colorFaded, - fontStyle: 'italic', - maxWidth: '70em', - marginTop: 5 }; - } - - private renderLabel(themeId: number, label: string) { - const labelStyle = this.labelStyle(themeId); - return ( -
- -
- ); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - private renderHeader(themeId: number, label: string, style: any = null) { - const theme = themeStyle(themeId); - - const labelStyle = { ...theme.textStyle, display: 'block', - color: theme.color, - fontSize: theme.fontSize * 1.25, - fontWeight: 500, - marginBottom: theme.mainPadding, - ...style }; - - return ( -
- -
- ); - } - - private renderDescription(themeId: number, description: string) { - return description ?
{description}
: null; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - public settingToComponent(key: string, value: any) { - const theme = themeStyle(this.props.themeId); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const output: any = null; - - const rowStyle = { - marginBottom: theme.mainPadding * 1.5, - }; - - const labelStyle = this.labelStyle(this.props.themeId); - - const subLabel = { ...labelStyle, display: 'block', - opacity: 0.7, - marginBottom: labelStyle.marginBottom }; - - const checkboxLabelStyle = { ...labelStyle, marginLeft: 8, - display: 'inline', - backgroundColor: 'transparent' }; - - const controlStyle = { - display: 'inline-block', - color: theme.color, - fontFamily: theme.fontFamily, - backgroundColor: theme.backgroundColor, - }; - - const textInputBaseStyle = { ...controlStyle, fontFamily: theme.fontFamily, - border: '1px solid', - padding: '4px 6px', - boxSizing: 'border-box', - borderColor: theme.borderColor4, - borderRadius: 3, - paddingLeft: 6, - paddingRight: 6, - paddingTop: 4, - paddingBottom: 4 }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const updateSettingValue = (key: string, value: any) => { - const md = Setting.settingMetadata(key); - if (md.needRestart) { - this.setState({ needRestart: true }); - } - shared.updateSettingValue(this, key, value); - }; - + private onUpdateSettingValue = ({ key, value }: UpdateSettingValueEvent) => { const md = Setting.settingMetadata(key); - - const descriptionText = Setting.keyDescription(key, AppType.Desktop); - const descriptionComp = this.renderDescription(this.props.themeId, descriptionText); - - if (settingKeyToControl[key]) { - const SettingComponent = settingKeyToControl[key]; - const label = md.label ? this.renderLabel(this.props.themeId, md.label()) : null; - return ( -
- {label} - {this.renderDescription(this.props.themeId, md.description ? md.description(AppType.Desktop) : null)} - { - updateSettingValue(key, event.value); - }} - renderLabel={this.renderLabel} - renderDescription={this.renderDescription} - renderHeader={this.renderHeader} - /> -
- ); - } else if (md.isEnum) { - const items = []; - const settingOptions = md.options(); - const array = Setting.enumOptionsToValueLabels(settingOptions, md.optionsOrder ? md.optionsOrder() : [], { - valueKey: 'key', - labelKey: 'label', - }); - - for (let i = 0; i < array.length; i++) { - const e = array[i]; - items.push( - , - ); - } - - const selectStyle = { ...controlStyle, paddingLeft: 6, - paddingRight: 6, - paddingTop: 4, - paddingBottom: 4, - borderColor: theme.borderColor4, - borderRadius: 3 }; - - return ( -
-
- -
- - {descriptionComp} -
- ); - } else if (md.type === Setting.TYPE_BOOL) { - const onCheckboxClick = () => { - updateSettingValue(key, !value); - }; - - const checkboxSize = theme.fontSize * 1.1666666666666; - - // Hack: The {key+value.toString()} is needed as otherwise the checkbox doesn't update when the state changes. - // There's probably a better way to do this but can't figure it out. - - return ( -
-
- { - onCheckboxClick(); - }} - style={{ marginLeft: 0, width: checkboxSize, height: checkboxSize }} - /> - -
- {descriptionComp} -
- ); - } else if (md.type === Setting.TYPE_STRING) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const inputStyle: any = { ...textInputBaseStyle, width: '50%', - minWidth: '20em' }; - const inputType = md.secure === true ? 'password' : 'text'; - - if (md.subType === 'file_path_and_args' || md.subType === 'file_path' || md.subType === 'directory_path') { - inputStyle.marginBottom = subLabel.marginBottom; - - const splitCmd = (cmdString: string) => { - // Normally not necessary but certain plugins found a way to - // set the set the value to "undefined", leading to a crash. - // This is now fixed at the model level but to be sure we - // check here too, to handle any already existing data. - // https://github.com/laurent22/joplin/issues/7621 - if (!cmdString) cmdString = ''; - const path = pathUtils.extractExecutablePath(cmdString); - const args = cmdString.substr(path.length + 1); - return [pathUtils.unquotePath(path), args]; - }; - - const joinCmd = (cmdArray: string[]) => { - if (!cmdArray[0] && !cmdArray[1]) return ''; - let cmdString = pathUtils.quotePath(cmdArray[0]); - if (!cmdString) cmdString = '""'; - if (cmdArray[1]) cmdString += ` ${cmdArray[1]}`; - return cmdString; - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const onPathChange = (event: any) => { - if (md.subType === 'file_path_and_args') { - const cmd = splitCmd(this.state.settings[key]); - cmd[0] = event.target.value; - updateSettingValue(key, joinCmd(cmd)); - } else { - updateSettingValue(key, event.target.value); - } - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const onArgsChange = (event: any) => { - const cmd = splitCmd(this.state.settings[key]); - cmd[1] = event.target.value; - updateSettingValue(key, joinCmd(cmd)); - }; - - const browseButtonClick = async () => { - if (md.subType === 'directory_path') { - const paths = await bridge().showOpenDialog({ - properties: ['openDirectory'], - }); - if (!paths || !paths.length) return; - updateSettingValue(key, paths[0]); - } else { - const paths = await bridge().showOpenDialog(); - if (!paths || !paths.length) return; - - if (md.subType === 'file_path') { - updateSettingValue(key, paths[0]); - } else { - const cmd = splitCmd(this.state.settings[key]); - cmd[0] = paths[0]; - updateSettingValue(key, joinCmd(cmd)); - } - } - }; - - const cmd = splitCmd(this.state.settings[key]); - const path = md.subType === 'file_path_and_args' ? cmd[0] : this.state.settings[key]; - - const argComp = md.subType !== 'file_path_and_args' ? null : ( -
-
{_('Arguments:')}
- { - onArgsChange(event); - }} - value={cmd[1]} - spellCheck={false} - /> -
- {descriptionComp} -
-
- ); - - return ( -
-
- -
-
-
-
-
{_('Path:')}
-
- { - onPathChange(event); - }} - value={path} - spellCheck={false} - /> -
-
- {descriptionComp} -
-
-
-
- {argComp} -
- ); - } else { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const onTextChange = (event: any) => { - updateSettingValue(key, event.target.value); - }; - return ( -
-
- -
- { - md.subType === SettingItemSubType.FontFamily || md.subType === SettingItemSubType.MonospaceFontFamily ? - updateSettingValue(key, fontFamily)} - subtype={md.subType} - /> : - { - onTextChange(event); - }} - spellCheck={false} - /> - } -
- {descriptionComp} -
-
- ); - } - } else if (md.type === Setting.TYPE_INT) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const onNumChange = (event: any) => { - updateSettingValue(key, event.target.value); - }; - - const label = [md.label()]; - if (md.unitLabel) label.push(`(${md.unitLabel(md.value)})`); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - const inputStyle: any = { ...textInputBaseStyle }; - - return ( -
-
- -
- { - onNumChange(event); - }} - min={md.minimum} - max={md.maximum} - step={md.step} - spellCheck={false} - /> - {descriptionComp} -
- ); - } else if (md.type === Setting.TYPE_BUTTON) { - const labelComp = md.hideLabel ? null : ( -
- -
- ); - - return ( -
- {labelComp} -
- ); - } else { - console.warn(`Type not implemented: ${key}`); + if (md.needRestart) { + this.setState({ needRestart: true }); } + shared.updateSettingValue(this, key, value); + }; - return output; + public settingToComponent(key: T, value: SettingValueType) { + return ( + + ); } private restartMessage() { diff --git a/packages/app-desktop/gui/ConfigScreen/FontSearch.tsx b/packages/app-desktop/gui/ConfigScreen/controls/FontSearch.tsx similarity index 99% rename from packages/app-desktop/gui/ConfigScreen/FontSearch.tsx rename to packages/app-desktop/gui/ConfigScreen/controls/FontSearch.tsx index d80a0d192b9..f7505300c30 100644 --- a/packages/app-desktop/gui/ConfigScreen/FontSearch.tsx +++ b/packages/app-desktop/gui/ConfigScreen/controls/FontSearch.tsx @@ -9,6 +9,7 @@ interface Props { style: CSSProperties; value: string; availableFonts: string[]; + inputId: string; onChange: (font: string)=> void; subtype: string; } @@ -108,6 +109,7 @@ const FontSearch = (props: Props) => { onFocus={handleFocus} onBlur={handleBlur} spellCheck={false} + id={props.inputId} ref={fontInputRef} />
= { + 'plugins.states': control_PluginsStates, +}; + +export interface UpdateSettingValueEvent { + key: string; + value: unknown; +} + +interface Props { + themeId: number; + settingKey: string; + value: unknown; + fonts: string[]; + onUpdateSettingValue: (event: UpdateSettingValueEvent)=> void; + onSettingButtonClick: (key: string)=> void; +} + +const SettingComponent: React.FC = props => { + const theme = themeStyle(props.themeId); + + const output: React.ReactNode = null; + + const updateSettingValue = useCallback((key: string, value: unknown) => { + props.onUpdateSettingValue({ key, value }); + }, [props.onUpdateSettingValue]); + + const rowStyle = { + marginBottom: theme.mainPadding * 1.5, + }; + + const controlStyle = { + display: 'inline-block', + color: theme.color, + fontFamily: theme.fontFamily, + backgroundColor: theme.backgroundColor, + }; + + const textInputBaseStyle = { ...controlStyle, fontFamily: theme.fontFamily, + border: '1px solid', + padding: '4px 6px', + boxSizing: 'border-box', + borderColor: theme.borderColor4, + borderRadius: 3, + paddingLeft: 6, + paddingRight: 6, + paddingTop: 4, + paddingBottom: 4 }; + + const key = props.settingKey; + const md = Setting.settingMetadata(key); + + const descriptionText = Setting.keyDescription(key, AppType.Desktop); + const inputId = useId(); + const descriptionId = useId(); + const descriptionComp = ; + + if (key in settingKeyToControl) { + const CustomSettingComponent = settingKeyToControl[key]; + const label = md.label ? : null; + return ( +
+ {label} + + { + updateSettingValue(key, event.value); + }} + /> +
+ ); + } else if (md.isEnum) { + const value = props.value as string; + + const items = []; + const settingOptions = md.options(); + const array = Setting.enumOptionsToValueLabels(settingOptions, md.optionsOrder ? md.optionsOrder() : [], { + valueKey: 'key', + labelKey: 'label', + }); + + for (let i = 0; i < array.length; i++) { + const e = array[i]; + items.push( + , + ); + } + + const selectStyle = { ...controlStyle, paddingLeft: 6, + paddingRight: 6, + paddingTop: 4, + paddingBottom: 4, + borderColor: theme.borderColor4, + borderRadius: 3 }; + + return ( +
+ + + {descriptionComp} +
+ ); + } else if (md.type === Setting.TYPE_BOOL) { + const value = props.value as boolean; + + const onCheckboxClick = () => { + updateSettingValue(key, !value); + }; + + const checkboxSize = theme.fontSize * 1.1666666666666; + + // Hack: The {key+value.toString()} is needed as otherwise the checkbox doesn't update when the state changes. + // There's probably a better way to do this but can't figure it out. + + return ( +
+
+ { + onCheckboxClick(); + }} + style={{ marginLeft: 0, width: checkboxSize, height: checkboxSize }} + aria-describedby={descriptionId} + /> + +
+ {descriptionComp} +
+ ); + } else if (md.type === Setting.TYPE_STRING) { + const value = props.value as string; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + const inputStyle: any = { ...textInputBaseStyle, width: '50%', + minWidth: '20em' }; + const inputType = md.secure === true ? 'password' : 'text'; + + if (md.subType === 'file_path_and_args' || md.subType === 'file_path' || md.subType === 'directory_path') { + inputStyle.marginBottom = theme.mainPadding / 2; + + const splitCmd = (cmdString: string) => { + // Normally not necessary but certain plugins found a way to + // set the set the value to "undefined", leading to a crash. + // This is now fixed at the model level but to be sure we + // check here too, to handle any already existing data. + // https://github.com/laurent22/joplin/issues/7621 + if (!cmdString) cmdString = ''; + const path = pathUtils.extractExecutablePath(cmdString); + const args = cmdString.substr(path.length + 1); + return [pathUtils.unquotePath(path), args]; + }; + + const joinCmd = (cmdArray: string[]) => { + if (!cmdArray[0] && !cmdArray[1]) return ''; + let cmdString = pathUtils.quotePath(cmdArray[0]); + if (!cmdString) cmdString = '""'; + if (cmdArray[1]) cmdString += ` ${cmdArray[1]}`; + return cmdString; + }; + + const onPathChange: React.ChangeEventHandler = event => { + if (md.subType === 'file_path_and_args') { + const cmd = splitCmd(value); + cmd[0] = event.target.value; + updateSettingValue(key, joinCmd(cmd)); + } else { + updateSettingValue(key, event.target.value); + } + }; + + const onArgsChange: React.ChangeEventHandler = event => { + const cmd = splitCmd(value); + cmd[1] = event.target.value; + updateSettingValue(key, joinCmd(cmd)); + }; + + const browseButtonClick = async () => { + if (md.subType === 'directory_path') { + const paths = await bridge().showOpenDialog({ + properties: ['openDirectory'], + }); + if (!paths || !paths.length) return; + updateSettingValue(key, paths[0]); + } else { + const paths = await bridge().showOpenDialog(); + if (!paths || !paths.length) return; + + if (md.subType === 'file_path') { + updateSettingValue(key, paths[0]); + } else { + const cmd = splitCmd(value); + cmd[0] = paths[0]; + updateSettingValue(key, joinCmd(cmd)); + } + } + }; + + const cmd = splitCmd(value); + const path = md.subType === 'file_path_and_args' ? cmd[0] : value; + + const argComp = md.subType !== 'file_path_and_args' ? null : ( +
+
{_('Arguments:')}
+ +
+ {descriptionComp} +
+
+ ); + + return ( +
+ +
+
+
+ +
+ +
+
+ {descriptionComp} +
+
+
+
+ {argComp} +
+ ); + } else { + const onTextChange: React.ChangeEventHandler = event => { + updateSettingValue(key, event.target.value); + }; + return ( +
+ + { + md.subType === SettingItemSubType.FontFamily || md.subType === SettingItemSubType.MonospaceFontFamily ? + updateSettingValue(key, fontFamily)} + subtype={md.subType} + inputId={inputId} + /> : + + } +
+ {descriptionComp} +
+
+ ); + } + } else if (md.type === Setting.TYPE_INT) { + const value = props.value as number; + + const onNumChange: React.ChangeEventHandler = (event) => { + updateSettingValue(key, event.target.value); + }; + + const label = [md.label()]; + if (md.unitLabel) label.push(`(${md.unitLabel(md.value)})`); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + const inputStyle: any = { ...textInputBaseStyle }; + + return ( +
+ + + {descriptionComp} +
+ ); + } else if (md.type === Setting.TYPE_BUTTON) { + const labelComp = md.hideLabel ? null : ( + + ); + + return ( +
+ {labelComp} +
+ ); + } else { + console.warn(`Type not implemented: ${key}`); + } + + return output; +}; + +export default SettingComponent; diff --git a/packages/app-desktop/gui/ConfigScreen/controls/SettingDescription.tsx b/packages/app-desktop/gui/ConfigScreen/controls/SettingDescription.tsx new file mode 100644 index 00000000000..505d6768103 --- /dev/null +++ b/packages/app-desktop/gui/ConfigScreen/controls/SettingDescription.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + text: string; + id?: string; +} + +const SettingDescription: React.FC = props => { + return props.text ?
{props.text}
: null; +}; + +export default SettingDescription; diff --git a/packages/app-desktop/gui/ConfigScreen/controls/SettingHeader.tsx b/packages/app-desktop/gui/ConfigScreen/controls/SettingHeader.tsx new file mode 100644 index 00000000000..e97d0ab5fd2 --- /dev/null +++ b/packages/app-desktop/gui/ConfigScreen/controls/SettingHeader.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; + +interface Props { + text: string; +} + +const SettingHeader: React.FC = props => { + return ( +
+ +
+ ); +}; + +export default SettingHeader; diff --git a/packages/app-desktop/gui/ConfigScreen/controls/SettingLabel.tsx b/packages/app-desktop/gui/ConfigScreen/controls/SettingLabel.tsx new file mode 100644 index 00000000000..a0afeb08f9d --- /dev/null +++ b/packages/app-desktop/gui/ConfigScreen/controls/SettingLabel.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; + +interface Props { + htmlFor: string|null; + text: string; +} + +const SettingLabel: React.FC = props => { + return ( +
+ +
+ ); +}; + +export default SettingLabel; diff --git a/packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.tsx b/packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.tsx index 119efe79ab6..6e516597688 100644 --- a/packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.tsx +++ b/packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.tsx @@ -17,6 +17,8 @@ import useOnDeleteHandler from '@joplin/lib/components/shared/config/plugins/use import Logger from '@joplin/utils/Logger'; import StyledMessage from '../../../style/StyledMessage'; import StyledLink from '../../../style/StyledLink'; +import SettingHeader from '../SettingHeader'; +import SettingDescription from '../SettingDescription'; const { space } = require('styled-system'); const logger = Logger.create('PluginState'); @@ -51,12 +53,6 @@ interface Props { themeId: number; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied onChange: Function; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - renderLabel: Function; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - renderDescription: Function; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - renderHeader: Function; } let repoApi_: RepositoryApi = null; @@ -281,7 +277,7 @@ export default function(props: Props) { if (!pluginItems.length || allDeleted) { return ( - {props.renderDescription(props.themeId, _('You do not have any installed plugin.'))} + ); } else { @@ -311,7 +307,6 @@ export default function(props: Props) { pluginSettings={pluginSettings} onSearchQueryChange={onSearchQueryChange} onPluginSettingsChange={onSearchPluginSettingsChange} - renderDescription={props.renderDescription} repoApi={repoApi} />
@@ -333,7 +328,7 @@ export default function(props: Props) {
- {props.renderHeader(props.themeId, _('Manage your plugins'))} +
{renderUserPlugins(pluginItems)} diff --git a/packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.tsx b/packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.tsx index 0190dffd6a3..fb9534d261e 100644 --- a/packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.tsx +++ b/packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.tsx @@ -10,6 +10,7 @@ import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/Plug import { _ } from '@joplin/lib/locale'; import useOnInstallHandler from '@joplin/lib/components/shared/config/plugins/useOnInstallHandler'; import { themeStyle } from '@joplin/lib/theme'; +import SettingDescription from '../SettingDescription'; const Root = styled.div` `; @@ -26,8 +27,6 @@ interface Props { pluginSettings: PluginSettings; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied onPluginSettingsChange(event: any): void; - // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied - renderDescription: Function; maxWidth: number; repoApi(): RepositoryApi; disabled: boolean; @@ -81,7 +80,7 @@ export default function(props: Props) { function renderResults(query: string, manifests: PluginManifest[]) { if (query && !manifests.length) { if (searchResultCount === null) return ''; // Search in progress - return props.renderDescription(props.themeId, _('No results')); + return ; } else { const output = []; diff --git a/packages/app-desktop/gui/ConfigScreen/style.scss b/packages/app-desktop/gui/ConfigScreen/style.scss index a6b5f367aef..82f83487aed 100644 --- a/packages/app-desktop/gui/ConfigScreen/style.scss +++ b/packages/app-desktop/gui/ConfigScreen/style.scss @@ -1,3 +1,5 @@ +@use "./styles/index.scss"; + .config-screen-content-wrapper { padding: 24px; overflow: auto; diff --git a/packages/app-desktop/gui/ConfigScreen/styles/index.scss b/packages/app-desktop/gui/ConfigScreen/styles/index.scss new file mode 100644 index 00000000000..473e027048d --- /dev/null +++ b/packages/app-desktop/gui/ConfigScreen/styles/index.scss @@ -0,0 +1,4 @@ + +@use "./setting-description.scss"; +@use "./setting-label.scss"; +@use "./setting-header.scss"; diff --git a/packages/app-desktop/gui/ConfigScreen/styles/setting-description.scss b/packages/app-desktop/gui/ConfigScreen/styles/setting-description.scss new file mode 100644 index 00000000000..d884c5ed70c --- /dev/null +++ b/packages/app-desktop/gui/ConfigScreen/styles/setting-description.scss @@ -0,0 +1,8 @@ + +.setting-description { + color: var(--joplin-color-faded); + font-size: var(--joplin-font-size); + font-style: italic; + max-width: 70em; + margin-top: 5px; +} diff --git a/packages/app-desktop/gui/ConfigScreen/styles/setting-header.scss b/packages/app-desktop/gui/ConfigScreen/styles/setting-header.scss new file mode 100644 index 00000000000..d26d322b8a9 --- /dev/null +++ b/packages/app-desktop/gui/ConfigScreen/styles/setting-header.scss @@ -0,0 +1,9 @@ + +.setting-header { + display: block; + color: var(--joplin-color); + font-size: calc(var(--joplin-font-size) * 1.25); + font-weight: 500; + margin-bottom: var(--joplin-main-padding); + line-height: var(--joplin-line-height); +} \ No newline at end of file diff --git a/packages/app-desktop/gui/ConfigScreen/styles/setting-label.scss b/packages/app-desktop/gui/ConfigScreen/styles/setting-label.scss new file mode 100644 index 00000000000..fd1893beead --- /dev/null +++ b/packages/app-desktop/gui/ConfigScreen/styles/setting-label.scss @@ -0,0 +1,20 @@ + +.setting-label { + display: block; + color: var(--joplin-color); + font-size: calc(var(--joplin-font-size) * 1.083333); + font-weight: 500; + margin-bottom: calc(var(--joplin-main-padding) / 2); + line-height: var(--joplin-line-height); + + &.-sub-label { + opacity: 0.7; + } + + &.-for-checkbox { + margin-left: 5px; + margin-bottom: 0; + display: inline; + background-color: transparent; + } +} \ No newline at end of file diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts index 472991cba65..8fb362383ed 100644 --- a/packages/lib/models/Setting.ts +++ b/packages/lib/models/Setting.ts @@ -17,7 +17,7 @@ const logger = Logger.create('models/Setting'); export * from './settings/types'; -type SettingValueType = ( +export type SettingValueType = ( T extends BuiltInMetadataKeys ? BuiltInMetadataValues[T] // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of old code before rule was applied