
* menu now sends command events and button states * menu state is synced with the elements at caret postion * menu functionalities are not yet implemented
184 lines
4.1 KiB
Vue
184 lines
4.1 KiB
Vue
<template>
|
|
<main>
|
|
<deck-card-editor-menu
|
|
:active="contentInFocus"
|
|
@action="editorAction"
|
|
v-model="menuState"
|
|
/>
|
|
|
|
<div
|
|
:ref="content"
|
|
class="card-content"
|
|
:contenteditable="active"
|
|
@focus="start"
|
|
@click="syncMenuStateIfFocussed"
|
|
@keyup="syncMenuStateOnKeyPress"
|
|
@blur="stop"
|
|
>
|
|
<h2>card content</h2>
|
|
<hr />
|
|
<p><b>foo:</b> boom</p>
|
|
<p><b>bar:</b> blam</p>
|
|
<hr />
|
|
<p>Some description maybe?</p>
|
|
</div>
|
|
</main>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Prop, Vue } from 'vue-property-decorator'
|
|
import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue'
|
|
import {
|
|
elementNameToMenuState,
|
|
getElementAndParentName,
|
|
marks,
|
|
blocks,
|
|
State,
|
|
movementKeys,
|
|
controlSequenceKeys
|
|
} from '@/editor.ts'
|
|
|
|
@Component({
|
|
components: { DeckCardEditorMenu }
|
|
})
|
|
export default class DeckCardEditor extends Vue {
|
|
@Prop() public readonly active!: boolean
|
|
@Prop() public readonly content!: Card['content']
|
|
|
|
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 clearMarks () {
|
|
marks.forEach(mark => {
|
|
this.menuState[mark] = false
|
|
})
|
|
}
|
|
|
|
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 editorAction (action: string) {
|
|
console.log('action', action)
|
|
// const content = this.$refs.content
|
|
}
|
|
|
|
private syncMenuState () {
|
|
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)
|
|
}
|
|
|
|
private syncMenuStateIfFocussed () {
|
|
if (this.contentInFocus) this.syncMenuState()
|
|
}
|
|
|
|
private syncMenuStateOnKeyPress (event: KeyboardEvent) {
|
|
// undo/redo/cut/paste
|
|
const isCtrlSq = event.ctrlKey && controlSequenceKeys.indexOf(event.key) >= 0
|
|
// arrow keys, enter, delete, etc
|
|
const isMove = movementKeys.indexOf(event.key) >= 0
|
|
|
|
if (isCtrlSq || isMove) this.syncMenuState()
|
|
}
|
|
|
|
private start () {
|
|
this.contentInFocus = true
|
|
this.syncMenuState()
|
|
}
|
|
|
|
private stop () {
|
|
this.contentInFocus = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.card-content p {
|
|
margin: 0;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.card-content ul {
|
|
list-style-position: inside;
|
|
margin: 0;
|
|
padding-left: .5em;
|
|
}
|
|
.card-content li > p {
|
|
display: inline;
|
|
}
|
|
|
|
.card-content h2 {
|
|
font-size: 1.4rem;
|
|
color: var(--highlight-color);
|
|
margin: 0;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.card-content h3 {
|
|
font-size: 1.4rem;
|
|
color: var(--highlight-color);
|
|
margin: 0 0 .2em 0;
|
|
font-weight: normal;
|
|
font-variant: small-caps;
|
|
line-height: .9em;
|
|
border-bottom: 1px solid var(--highlight-color);
|
|
}
|
|
|
|
.card-content hr {
|
|
height: 0;
|
|
margin: .2em 0;
|
|
border: 2px solid var(--highlight-color);
|
|
}
|
|
.card-content hr.pointing-right {
|
|
height: 0;
|
|
margin: .2em 0;
|
|
border-style: solid;
|
|
border-width: 2px 0 2px 220px;
|
|
border-color: transparent transparent transparent var(--highlight-color);
|
|
}
|
|
.card-content hr.pointing-left {
|
|
height: 0;
|
|
margin: .2em 0;
|
|
border-style: solid;
|
|
border-width: 2px 220px 2px 0;
|
|
border-color: transparent var(--highlight-color) transparent transparent;
|
|
}
|
|
[contenteditable="true"] { outline: none; }
|
|
</style>
|