fix menu sync
this loops the search for marks until a block element is found, because marks are nested elements like for example `<p><b><i>bold and italic</i></b></p>`.
This commit is contained in:
parent
bfa4ef8ba0
commit
8668838238
2 changed files with 65 additions and 51 deletions
|
@ -29,11 +29,8 @@
|
|||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue'
|
||||
import {
|
||||
elementNameToMenuState,
|
||||
menuActionToCommand,
|
||||
getElementAndParentName,
|
||||
marks,
|
||||
blocks,
|
||||
getActiveMarksAndBlocks,
|
||||
State,
|
||||
movementKeys,
|
||||
controlSequenceKeys
|
||||
|
@ -48,45 +45,33 @@ export default class DeckCardEditor extends Vue {
|
|||
|
||||
private contentInFocus = false
|
||||
|
||||
private menuState: State = {
|
||||
bold: false,
|
||||
italic: false,
|
||||
paragraph: true,
|
||||
heading1: false,
|
||||
heading2: false,
|
||||
heading3: false,
|
||||
bulletList: false,
|
||||
spacer: false,
|
||||
separator: false,
|
||||
statBlock: false
|
||||
private defaultMenuState (): State {
|
||||
return {
|
||||
bold: false,
|
||||
italic: false,
|
||||
paragraph: true,
|
||||
heading1: false,
|
||||
heading2: false,
|
||||
heading3: false,
|
||||
bulletList: false,
|
||||
spacer: false,
|
||||
separator: false,
|
||||
statBlock: false
|
||||
}
|
||||
}
|
||||
|
||||
private clearMarks () {
|
||||
marks.forEach(mark => {
|
||||
this.menuState[mark] = false
|
||||
})
|
||||
private menuState = this.defaultMenuState()
|
||||
|
||||
private resetMenuState () {
|
||||
this.menuState = this.defaultMenuState()
|
||||
}
|
||||
|
||||
private toggleBlock (name: string) {
|
||||
blocks.forEach(block => {
|
||||
this.menuState[block] = false
|
||||
})
|
||||
this.menuState[name] = true
|
||||
}
|
||||
|
||||
private setMenuState (elementName: string, parentName?: string) {
|
||||
const stateName = elementNameToMenuState[elementName]
|
||||
|
||||
// marks are always inside a block element
|
||||
if (marks.indexOf(stateName) >= 0 && parentName) {
|
||||
const parentStateName = elementNameToMenuState[parentName]
|
||||
// marks are inclusive like checkboxes
|
||||
this.menuState[stateName] = true
|
||||
// but blocks are exclusive like radio buttons
|
||||
this.toggleBlock(parentStateName)
|
||||
} else {
|
||||
this.clearMarks()
|
||||
this.toggleBlock(stateName)
|
||||
private setMenuState (marks: string[], block: string) {
|
||||
this.resetMenuState()
|
||||
marks.forEach(mark => { this.menuState[mark] = true })
|
||||
if (block !== 'paragraph') {
|
||||
this.menuState.paragraph = false
|
||||
this.menuState[block] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,11 +90,8 @@ export default class DeckCardEditor extends Vue {
|
|||
const sel = window.getSelection()?.focusNode
|
||||
if (!sel) return
|
||||
|
||||
const [elementName, parentName] = getElementAndParentName(sel)
|
||||
console.log('focussed element', elementName, parentName)
|
||||
if (!elementName) return
|
||||
|
||||
this.setMenuState(elementName, parentName)
|
||||
const { marks, block } = getActiveMarksAndBlocks(sel as HTMLElement)
|
||||
this.setMenuState(marks, block)
|
||||
}
|
||||
|
||||
private syncMenuStateIfFocussed () {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { elementNameToMenuState, marks, blocks } from './constants'
|
||||
|
||||
export type State = KV<boolean>
|
||||
export {
|
||||
movementKeys,
|
||||
controlSequenceKeys,
|
||||
elementNameToMenuState,
|
||||
marks,
|
||||
blocks
|
||||
} from './constants'
|
||||
|
@ -33,10 +34,41 @@ export const menuActionToCommand: KV<() => boolean> = {
|
|||
italic: simpleAction('italic')
|
||||
}
|
||||
|
||||
export function getElementAndParentName (el: Node) {
|
||||
const element = el.nodeName === '#text' ? el.parentElement : el
|
||||
return [
|
||||
element?.nodeName,
|
||||
element?.parentElement?.nodeName
|
||||
]
|
||||
export function getActiveMarksAndBlocks (el: HTMLElement): {
|
||||
marks: string[];
|
||||
block: string;
|
||||
} {
|
||||
let activeBlock = 'paragraph'
|
||||
const activeMarks: string[] = []
|
||||
|
||||
const focussedEl = el.nodeName === '#text' ? el.parentElement : el
|
||||
if (!focussedEl) return { marks: activeMarks, block: activeBlock }
|
||||
|
||||
const focussedState = elementNameToMenuState[focussedEl.nodeName]
|
||||
if (!focussedState) return { marks: activeMarks, block: activeBlock }
|
||||
|
||||
if (blocks.indexOf(focussedState) >= 0) {
|
||||
activeBlock = focussedState
|
||||
return { marks: activeMarks, block: activeBlock }
|
||||
}
|
||||
|
||||
let wrappingEl = focussedEl.parentElement
|
||||
let wrappingState: string
|
||||
|
||||
if (marks.indexOf(focussedState) >= 0) {
|
||||
activeMarks.push(focussedState)
|
||||
|
||||
while (wrappingEl) {
|
||||
wrappingState = elementNameToMenuState[wrappingEl.nodeName]
|
||||
if (marks.indexOf(wrappingState) < 0) {
|
||||
if (blocks.indexOf(wrappingState) >= 0) activeBlock = wrappingState
|
||||
break
|
||||
}
|
||||
|
||||
activeMarks.push(wrappingState)
|
||||
wrappingEl = wrappingEl.parentElement
|
||||
}
|
||||
}
|
||||
|
||||
return { marks: activeMarks, block: activeBlock }
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue