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>`.
master
koehr 5 years ago committed by Norman
parent 44eabe831e
commit 941118494a

@ -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 toggleBlock (name: string) {
blocks.forEach(block => {
this.menuState[block] = false
})
this.menuState[name] = true
private resetMenuState () {
this.menuState = this.defaultMenuState()
}
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…
Cancel
Save