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 { Component, Prop, Vue } from 'vue-property-decorator'
import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue' import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue'
import { import {
elementNameToMenuState,
menuActionToCommand, menuActionToCommand,
getElementAndParentName, getActiveMarksAndBlocks,
marks,
blocks,
State, State,
movementKeys, movementKeys,
controlSequenceKeys controlSequenceKeys
@ -48,45 +45,33 @@ export default class DeckCardEditor extends Vue {
private contentInFocus = false private contentInFocus = false
private menuState: State = { private defaultMenuState (): State {
bold: false, return {
italic: false, bold: false,
paragraph: true, italic: false,
heading1: false, paragraph: true,
heading2: false, heading1: false,
heading3: false, heading2: false,
bulletList: false, heading3: false,
spacer: false, bulletList: false,
separator: false, spacer: false,
statBlock: false separator: false,
statBlock: false
}
} }
private clearMarks () { private menuState = this.defaultMenuState()
marks.forEach(mark => {
this.menuState[mark] = false
})
}
private toggleBlock (name: string) { private resetMenuState () {
blocks.forEach(block => { this.menuState = this.defaultMenuState()
this.menuState[block] = false
})
this.menuState[name] = true
} }
private setMenuState (elementName: string, parentName?: string) { private setMenuState (marks: string[], block: string) {
const stateName = elementNameToMenuState[elementName] this.resetMenuState()
marks.forEach(mark => { this.menuState[mark] = true })
// marks are always inside a block element if (block !== 'paragraph') {
if (marks.indexOf(stateName) >= 0 && parentName) { this.menuState.paragraph = false
const parentStateName = elementNameToMenuState[parentName] this.menuState[block] = true
// 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)
} }
} }
@ -105,11 +90,8 @@ export default class DeckCardEditor extends Vue {
const sel = window.getSelection()?.focusNode const sel = window.getSelection()?.focusNode
if (!sel) return if (!sel) return
const [elementName, parentName] = getElementAndParentName(sel) const { marks, block } = getActiveMarksAndBlocks(sel as HTMLElement)
console.log('focussed element', elementName, parentName) this.setMenuState(marks, block)
if (!elementName) return
this.setMenuState(elementName, parentName)
} }
private syncMenuStateIfFocussed () { private syncMenuStateIfFocussed () {

@ -1,8 +1,9 @@
import { elementNameToMenuState, marks, blocks } from './constants'
export type State = KV<boolean> export type State = KV<boolean>
export { export {
movementKeys, movementKeys,
controlSequenceKeys, controlSequenceKeys,
elementNameToMenuState,
marks, marks,
blocks blocks
} from './constants' } from './constants'
@ -33,10 +34,41 @@ export const menuActionToCommand: KV<() => boolean> = {
italic: simpleAction('italic') italic: simpleAction('italic')
} }
export function getElementAndParentName (el: Node) { export function getActiveMarksAndBlocks (el: HTMLElement): {
const element = el.nodeName === '#text' ? el.parentElement : el marks: string[];
return [ block: string;
element?.nodeName, } {
element?.parentElement?.nodeName 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