import React, { Dispatch, memo, FC, SetStateAction, useCallback, useEffect, useState, useMemo, } from 'react'; import { useTranslation } from 'next-i18next'; import { toastSuccess, toastError } from '~/client/util/toastr'; import { useEditorSettings } from '~/stores/editor'; type EditorSettingsBodyProps = Record; type RuleListGroupProps = { title: string; ruleList: RulesMenuItem[] textlintRules: LintRule[] setTextlintRules: Dispatch> } type LintRule = { name: string options?: unknown isEnabled?: boolean } type RulesMenuItem = { name: string description: string } const commonRulesMenuItems = [ { name: 'common-misspellings', description: 'editor_settings.common_settings.common_misspellings', }, { name: 'max-comma', description: 'editor_settings.common_settings.max_comma', }, { name: 'sentence-length', description: 'editor_settings.common_settings.sentence_length', }, // { // omit because en-pos package is too big // name: 'en-capitalization', // description: 'editor_settings.common_settings.en_capitalization', // }, { name: 'no-unmatched-pair', description: 'editor_settings.common_settings.no_unmatched_pair', }, { name: 'date-weekday-mismatch', description: 'editor_settings.common_settings.date_weekday_mismatch', }, { name: 'no-kangxi-radicals', description: 'editor_settings.common_settings.no_kangxi_radicals', }, { name: 'no-surrogate-pair', description: 'editor_settings.common_settings.no_surrogate_pair', }, { name: 'no-zero-width-spaces', description: 'editor_settings.common_settings.no_zero_width_spaces', }, { name: 'period-in-list-item', description: 'editor_settings.common_settings.period_in_list_item', }, { name: 'use-si-units', description: 'editor_settings.common_settings.use_si_units', }, ]; const japaneseRulesMenuItems = [ { name: 'ja-hiragana-keishikimeishi', description: 'editor_settings.japanese_settings.ja_hiragana_keishikimeishi', }, { name: 'ja-no-abusage', description: 'editor_settings.japanese_settings.ja_no_abusage', }, { name: 'ja-no-inappropriate-words', description: 'editor_settings.japanese_settings.ja_no_inappropriate_words', }, { name: 'ja-no-mixed-period', description: 'editor_settings.japanese_settings.ja_no_mixed_period', }, { name: 'ja-no-redundant-expression', description: 'editor_settings.japanese_settings.ja_no_redundant_expression', }, { name: 'max-kanji-continuous-len', description: 'editor_settings.japanese_settings.max_kanji_continuous_len', }, { name: 'max-ten', description: 'editor_settings.japanese_settings.max_ten', }, { name: 'no-double-negative-ja', description: 'editor_settings.japanese_settings.no_double_negative_ja', }, { name: 'no-doubled-conjunction', description: 'editor_settings.japanese_settings.no_doubled_conjunction', }, { name: 'no-doubled-joshi', description: 'editor_settings.japanese_settings.no_doubled_joshi', }, { name: 'no-dropping-the-ra', description: 'editor_settings.japanese_settings.no_dropping_the_ra', }, { name: 'no-hankaku-kana', description: 'editor_settings.japanese_settings.no_hankaku_kana', }, { name: 'prefer-tari-tari', description: 'editor_settings.japanese_settings.prefer_tari_tari', }, { name: 'ja-unnatural-alphabet', description: 'editor_settings.japanese_settings.ja_unnatural_alphabet', }, { name: 'no-mixed-zenkaku-and-hankaku-alphabet', description: 'editor_settings.japanese_settings.no_mixed_zenkaku_and_hankaku_alphabet', }, { name: 'no-nfd', description: 'editor_settings.japanese_settings.no_nfd', }, ]; const RuleListGroup: FC = ({ title, ruleList, textlintRules, setTextlintRules, }: RuleListGroupProps) => { const { t } = useTranslation(); const isCheckedRule = (ruleName: string) => ( textlintRules.find(stateRule => ( stateRule.name === ruleName ))?.isEnabled || false ); const ruleCheckboxHandler = (isChecked: boolean, ruleName: string) => { setTextlintRules(prevState => ( prevState.filter(rule => rule.name !== ruleName).concat({ name: ruleName, isEnabled: isChecked }) )); }; return ( <>

{t(title)}

{ruleList.map(rule => (
ruleCheckboxHandler(e.target.checked, rule.name)} />

{t(rule.description)}

))}
); }; const createRulesFromDefaultList = (rule: { name: string }) => ( { name: rule.name, isEnabled: true, } ); export const EditorSettings = memo((): JSX.Element => { const { t } = useTranslation(); const [textlintRules, setTextlintRules] = useState([]); const { data: dataEditorSettings, update: updateEditorSettings } = useEditorSettings(); const defaultRules = useMemo(() => { const defaultCommonRules = commonRulesMenuItems.map(rule => createRulesFromDefaultList(rule)); const defaultJapaneseRules = japaneseRulesMenuItems.map(rule => createRulesFromDefaultList(rule)); return [...defaultCommonRules, ...defaultJapaneseRules]; }, []); const initializeEditorSettings = useCallback(() => { if (dataEditorSettings == null) { return; } const retrievedRules: LintRule[] | undefined = dataEditorSettings?.textlintSettings?.textlintRules; // If database is empty, add default rules to state if (retrievedRules != null && retrievedRules.length > 0) { setTextlintRules(retrievedRules); return; } setTextlintRules(defaultRules); }, [dataEditorSettings, defaultRules]); const updateRulesHandler = useCallback(async() => { try { await updateEditorSettings({ textlintSettings: { textlintRules } }); toastSuccess(t('toaster.update_successed', { target: 'Updated Textlint Settings', ns: 'commons' })); } catch (err) { toastError(err); } }, [t, textlintRules, updateEditorSettings]); useEffect(() => { initializeEditorSettings(); }, [initializeEditorSettings]); if (textlintRules == null) { return (
); } return (
); }); EditorSettings.displayName = 'EditorSettings';