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 { 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,7 +45,8 @@ export default class DeckCardEditor extends Vue {
|
||||||
|
|
||||||
private contentInFocus = false
|
private contentInFocus = false
|
||||||
|
|
||||||
private menuState: State = {
|
private defaultMenuState (): State {
|
||||||
|
return {
|
||||||
bold: false,
|
bold: false,
|
||||||
italic: false,
|
italic: false,
|
||||||
paragraph: true,
|
paragraph: true,
|
||||||
|
@ -60,33 +58,20 @@ export default class DeckCardEditor extends Vue {
|
||||||
separator: false,
|
separator: false,
|
||||||
statBlock: false
|
statBlock: false
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearMarks () {
|
|
||||||
marks.forEach(mark => {
|
|
||||||
this.menuState[mark] = false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleBlock (name: string) {
|
private menuState = this.defaultMenuState()
|
||||||
blocks.forEach(block => {
|
|
||||||
this.menuState[block] = false
|
private resetMenuState () {
|
||||||
})
|
this.menuState = this.defaultMenuState()
|
||||||
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…
Add table
Reference in a new issue