From 3103aee8edbc617caeaef86d7542288bf8681dd3 Mon Sep 17 00:00:00 2001 From: He Linming Date: Tue, 11 Feb 2020 21:46:24 +0800 Subject: [PATCH] A task checked should update related task (#1737) * A task checked should update related task * Improve code quality * feat: add a option for autoCheck * refactor: put autoCheck in Editor part * refactor: refactor some functions and methods 1. fix autoCheck was not passed to the editor 2. put getParentCheckBox and cumputeChecboxStatus functions in utils folder. 3. put setCheckBoxState, updateParentsCheckBoxState, updateChildrenCheckBoxState and listItemCheckBoxClick methods in clickCtrl.js file. --- docs/PREFERENCES.md | 1 + src/main/preferences/schema.json | 5 ++ src/muya/lib/config/index.js | 1 + src/muya/lib/contentState/clickCtrl.js | 46 +++++++++++++++++++ src/muya/lib/contentState/updateCtrl.js | 13 +----- src/muya/lib/utils/cumputeCheckBoxStatus.js | 11 +++++ src/muya/lib/utils/getParentCheckBox.js | 10 ++++ .../components/editorWithTabs/editor.vue | 11 ++++- src/renderer/prefComponents/editor/index.vue | 6 +++ src/renderer/store/preferences.js | 1 + static/preference.json | 1 + 11 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/muya/lib/utils/cumputeCheckBoxStatus.js create mode 100644 src/muya/lib/utils/getParentCheckBox.js diff --git a/docs/PREFERENCES.md b/docs/PREFERENCES.md index e4838a1c0..4a2a72a03 100644 --- a/docs/PREFERENCES.md +++ b/docs/PREFERENCES.md @@ -40,6 +40,7 @@ Preferences can be controlled and modified in the settings window or via the `pr | autoGuessEncoding | Boolean | true | Try to automatically guess the file encoding when opening files | | trimTrailingNewline | Enum | `2` | Ensure a single trailing newline or whether trailing newlines should be removed: `0`: trim all trailing newlines, `1`: ensure single newline, `2`: auto detect, `3`: disabled. | | hideLinkPopup | Boolean | false | It will not show the link popup when hover over the link if set `hideLinkPopup` to true | +| autoCheck | Boolean | false | Whether to automatically check related task. Optional value: true, false | #### Markdown diff --git a/src/main/preferences/schema.json b/src/main/preferences/schema.json index 35f407275..ffa7c22e4 100644 --- a/src/main/preferences/schema.json +++ b/src/main/preferences/schema.json @@ -200,6 +200,11 @@ "type": "boolean", "default": false }, + "autoCheck": { + "description": "Editor--Whether to automatically check related task.", + "type": "boolean", + "default": false + }, "preferLooseListItem": { "description": "Markdown--The preferred list type", "type": "boolean" diff --git a/src/muya/lib/config/index.js b/src/muya/lib/config/index.js index 88c7f2102..f24358c5b 100644 --- a/src/muya/lib/config/index.js +++ b/src/muya/lib/config/index.js @@ -264,6 +264,7 @@ export const MUYA_DEFAULT_OPTION = { vegaTheme: 'latimes', // excel / ggplot2 / quartz / vox / fivethirtyeight / dark / latimes hideQuickInsertHint: false, hideLinkPopup: false, + autoCheck: false, // Whether we should set spellcheck attribute on our container to highlight misspelled words. // NOTE: The browser is not able to correct misspelled words words without a custom // implementation like in Mark Text. diff --git a/src/muya/lib/contentState/clickCtrl.js b/src/muya/lib/contentState/clickCtrl.js index feb630d1b..b670563e5 100644 --- a/src/muya/lib/contentState/clickCtrl.js +++ b/src/muya/lib/contentState/clickCtrl.js @@ -1,6 +1,8 @@ import selection from '../selection' import { isMuyaEditorElement } from '../selection/dom' import { HAS_TEXT_BLOCK_REG, CLASS_OR_ID } from '../config' +import { getParentCheckBox } from '../utils/getParentCheckBox' +import { cumputeCheckboxStatus } from '../utils/cumputeCheckBoxStatus' const clickCtrl = ContentState => { ContentState.prototype.clickHandler = function (event) { @@ -193,6 +195,50 @@ const clickCtrl = ContentState => { this.cursor = { start, end } } } + + ContentState.prototype.setCheckBoxState = function (checkbox, checked) { + checkbox.checked = checked + const block = this.getBlock(checkbox.id) + block.checked = checked + checkbox.classList.toggle(CLASS_OR_ID.AG_CHECKBOX_CHECKED) + } + + ContentState.prototype.updateParentsCheckBoxState = function (checkbox) { + let parent = getParentCheckBox(checkbox) + while (parent !== null) { + const checked = cumputeCheckboxStatus(parent) + if (parent.checked !== checked) { + this.setCheckBoxState(parent, checked) + parent = getParentCheckBox(parent) + } else { + break + } + } + } + + ContentState.prototype.updateChildrenCheckBoxState = function (checkbox, checked) { + const checkboxes = checkbox.parentElement.querySelectorAll(`input ~ ul .${CLASS_OR_ID.AG_TASK_LIST_ITEM_CHECKBOX}`) + const len = checkboxes.length + for (let i = 0; i < len; i++) { + const checkbox = checkboxes[i] + if (checkbox.checked !== checked) { + this.setCheckBoxState(checkbox, checked) + } + } + } + + // handle task list item checkbox click + ContentState.prototype.listItemCheckBoxClick = function (checkbox) { + const { checked } = checkbox + this.setCheckBoxState(checkbox, checked) + + // A task checked, then related task should be update + const { autoCheck } = this.muya.options + if (autoCheck) { + this.updateChildrenCheckBoxState(checkbox, checked) + this.updateParentsCheckBoxState(checkbox) + } + } } export default clickCtrl diff --git a/src/muya/lib/contentState/updateCtrl.js b/src/muya/lib/contentState/updateCtrl.js index baedb0651..6a7b45c11 100644 --- a/src/muya/lib/contentState/updateCtrl.js +++ b/src/muya/lib/contentState/updateCtrl.js @@ -1,6 +1,5 @@ import { tokenizer } from '../parser/' import { conflict } from '../utils' -import { CLASS_OR_ID } from '../config' const INLINE_UPDATE_FRAGMENTS = [ '(?:^|\n) {0,3}([*+-] {1,4})', // Bullet list @@ -17,14 +16,6 @@ const INLINE_UPDATE_FRAGMENTS = [ const INLINE_UPDATE_REG = new RegExp(INLINE_UPDATE_FRAGMENTS.join('|'), 'i') const updateCtrl = ContentState => { - // handle task list item checkbox click - ContentState.prototype.listItemCheckBoxClick = function (checkbox) { - const { checked, id } = checkbox - const block = this.getBlock(id) - block.checked = checked - checkbox.classList.toggle(CLASS_OR_ID.AG_CHECKBOX_CHECKED) - } - ContentState.prototype.checkSameMarkerOrDelimiter = function (list, markerOrDelimiter) { if (!/ol|ul/.test(list.type)) return false return list.children[0].bulletMarkerOrDelimiter === markerOrDelimiter @@ -142,7 +133,7 @@ const updateCtrl = ContentState => { for (const l of lines) { /* eslint-disable no-useless-escape */ if (/ {0,3}(?:\* *\* *\*|- *- *-|_ *_ *_)[ \*\-\_]*$/.test(l) && !thematicLineHasPushed) { - /* eslint-enable no-useless-escape */ + /* eslint-enable no-useless-escape */ thematicLine = l thematicLineHasPushed = true } else if (!thematicLineHasPushed) { @@ -303,7 +294,7 @@ const updateCtrl = ContentState => { } } if (TASK_LIST_REG.test(listItemText)) { - const [,, tasklist,,,,] = listItemText.match(INLINE_UPDATE_REG) || [] // eslint-disable-line comma-spacing + const [, , tasklist, , , ,] = listItemText.match(INLINE_UPDATE_REG) || [] // eslint-disable-line comma-spacing return this.updateTaskListItem(block, 'tasklist', tasklist) } else { return block diff --git a/src/muya/lib/utils/cumputeCheckBoxStatus.js b/src/muya/lib/utils/cumputeCheckBoxStatus.js new file mode 100644 index 000000000..2678baf1c --- /dev/null +++ b/src/muya/lib/utils/cumputeCheckBoxStatus.js @@ -0,0 +1,11 @@ +export const cumputeCheckboxStatus = function (parentCheckbox) { + const children = parentCheckbox.parentElement.lastElementChild.children + const len = children.length + for (let i = 0; i < len; i++) { + const checkbox = children[i].firstElementChild + if (checkbox.checked === false) { + return false + } + } + return true +} diff --git a/src/muya/lib/utils/getParentCheckBox.js b/src/muya/lib/utils/getParentCheckBox.js new file mode 100644 index 000000000..bde972410 --- /dev/null +++ b/src/muya/lib/utils/getParentCheckBox.js @@ -0,0 +1,10 @@ +import { CLASS_OR_ID } from '../config' + +export const getParentCheckBox = function (checkbox) { + const parent = checkbox.parentElement.parentElement.parentElement + if (parent.id !== CLASS_OR_ID.AG_EDITOR_ID) { + return parent.firstElementChild + } else { + return null + } +} diff --git a/src/renderer/components/editorWithTabs/editor.vue b/src/renderer/components/editorWithTabs/editor.vue index 91fe316ea..139f435da 100644 --- a/src/renderer/components/editorWithTabs/editor.vue +++ b/src/renderer/components/editorWithTabs/editor.vue @@ -147,6 +147,7 @@ export default { editorFontFamily: state => state.preferences.editorFontFamily, hideQuickInsertHint: state => state.preferences.hideQuickInsertHint, hideLinkPopup: state => state.preferences.hideLinkPopup, + autoCheck: state => state.preferences.autoCheck, editorLineWidth: state => state.preferences.editorLineWidth, imageInsertAction: state => state.preferences.imageInsertAction, imageFolderPath: state => state.preferences.imageFolderPath, @@ -319,6 +320,12 @@ export default { editor.setOptions({ hideLinkPopup: value }) } }, + autoCheck: function (value, oldValue) { + const { editor } = this + if (value !== oldValue && editor) { + editor.setOptions({ autoCheck: value }) + } + }, codeFontSize: function (value, oldValue) { if (value !== oldValue) { addCommonStyle({ @@ -477,7 +484,8 @@ export default { theme, sequenceTheme, spellcheckerEnabled, - hideLinkPopup + hideLinkPopup, + autoCheck } = this // use muya UI plugins @@ -520,6 +528,7 @@ export default { footnote, hideQuickInsertHint, hideLinkPopup, + autoCheck, sequenceTheme, spellcheckEnabled: spellcheckerEnabled, imageAction: this.imageAction.bind(this), diff --git a/src/renderer/prefComponents/editor/index.vue b/src/renderer/prefComponents/editor/index.vue index 7ad786126..0207e4f5d 100644 --- a/src/renderer/prefComponents/editor/index.vue +++ b/src/renderer/prefComponents/editor/index.vue @@ -109,6 +109,11 @@ :bool="hideLinkPopup" :onChange="value => onSelectChange('hideLinkPopup', value)" > + state.preferences.trimUnnecessaryCodeBlockEmptyLines, hideQuickInsertHint: state => state.preferences.hideQuickInsertHint, hideLinkPopup: state => state.preferences.hideLinkPopup, + autoCheck: state => state.preferences.autoCheck, editorLineWidth: state => state.preferences.editorLineWidth, defaultEncoding: state => state.preferences.defaultEncoding, autoGuessEncoding: state => state.preferences.autoGuessEncoding, diff --git a/src/renderer/store/preferences.js b/src/renderer/store/preferences.js index bdf6b5514..c2b23e17f 100644 --- a/src/renderer/store/preferences.js +++ b/src/renderer/store/preferences.js @@ -36,6 +36,7 @@ const state = { hideQuickInsertHint: false, imageInsertAction: 'folder', hideLinkPopup: false, + autoCheck: false, preferLooseListItem: true, bulletListMarker: '-', diff --git a/static/preference.json b/static/preference.json index a068583ac..019de8912 100644 --- a/static/preference.json +++ b/static/preference.json @@ -32,6 +32,7 @@ "hideQuickInsertHint": false, "imageInsertAction": "path", "hideLinkPopup": false, + "autoCheck": false, "preferLooseListItem": true, "bulletListMarker": "-",