diff --git a/docs/PREFERENCES.md b/docs/PREFERENCES.md index e4838a1c..4a2a72a0 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 35f40727..ffa7c22e 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 88c7f210..f24358c5 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 feb630d1..b670563e 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 baedb065..6a7b45c1 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 00000000..2678baf1 --- /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 00000000..bde97241 --- /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 91fe316e..139f435d 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 7ad78612..0207e4f5 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 bdf6b551..c2b23e17 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 a068583a..019de891 100644 --- a/static/preference.json +++ b/static/preference.json @@ -32,6 +32,7 @@ "hideQuickInsertHint": false, "imageInsertAction": "path", "hideLinkPopup": false, + "autoCheck": false, "preferLooseListItem": true, "bulletListMarker": "-",