From 5986e599fdab63a3ac3f64ef1275d2fed7e39ad9 Mon Sep 17 00:00:00 2001
From: koehr <n@koehr.in>
Date: Wed, 1 Apr 2020 16:54:04 +0200
Subject: [PATCH] implements basic menu functionalities

this includes standard block elements (headers, paragraphs, lists, rulers)
but not marks (bold, italic)
and for sure not special elements like the stat block
---
 src/components/deck-card-editor-menu.vue |  4 +--
 src/components/deck-card-editor.vue      | 17 +++++++---
 src/{editor.ts => editor/constants.ts}   | 10 ------
 src/editor/index.ts                      | 40 ++++++++++++++++++++++++
 4 files changed, 55 insertions(+), 16 deletions(-)
 rename src/{editor.ts => editor/constants.ts} (72%)
 create mode 100644 src/editor/index.ts

diff --git a/src/components/deck-card-editor-menu.vue b/src/components/deck-card-editor-menu.vue
index 8c5004a..7d830b7 100644
--- a/src/components/deck-card-editor-menu.vue
+++ b/src/components/deck-card-editor-menu.vue
@@ -17,7 +17,7 @@
 
 <script lang="ts">
 import { Component, Prop, Vue } from 'vue-property-decorator'
-import { blocks, State } from '@/editor.ts'
+import { blocks, State } from '@/editor'
 
 @Component
 export default class DeckCardEditorMenu extends Vue {
@@ -50,7 +50,7 @@ export default class DeckCardEditorMenu extends Vue {
   padding: .2rem 1rem;
   visibility: hidden;
   opacity: 0;
-  transition: opacity 0.2s, visibility 0.2s;
+  transition: opacity .3s .2s, visibility .3s .2s;
   background-color: var(--highlight-color);
   z-index: 2;
 }
diff --git a/src/components/deck-card-editor.vue b/src/components/deck-card-editor.vue
index 765a2ce..e161209 100644
--- a/src/components/deck-card-editor.vue
+++ b/src/components/deck-card-editor.vue
@@ -7,7 +7,7 @@
     />
 
     <div
-      :ref="content"
+      ref="content"
       class="card-content"
       :contenteditable="active"
       @focus="start"
@@ -30,13 +30,14 @@ import { Component, Prop, Vue } from 'vue-property-decorator'
 import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue'
 import {
   elementNameToMenuState,
+  menuActionToCommand,
   getElementAndParentName,
   marks,
   blocks,
   State,
   movementKeys,
   controlSequenceKeys
-} from '@/editor.ts'
+} from '@/editor'
 
 @Component({
   components: { DeckCardEditorMenu }
@@ -91,7 +92,13 @@ export default class DeckCardEditor extends Vue {
 
   private editorAction (action: string) {
     console.log('action', action)
-    // const content = this.$refs.content
+    const content = this.$refs.content as HTMLElement
+    content.focus()
+
+    const cmd = menuActionToCommand[action]
+    cmd()
+
+    this.$nextTick(() => this.syncMenuState())
   }
 
   private syncMenuState () {
@@ -121,6 +128,8 @@ export default class DeckCardEditor extends Vue {
   private start () {
     this.contentInFocus = true
     this.syncMenuState()
+    // insert paragraphs instead of DIVs on enter
+    document.execCommand('defaultParagraphSeparator', false, 'p')
   }
 
   private stop () {
@@ -129,7 +138,7 @@ export default class DeckCardEditor extends Vue {
 }
 </script>
 
-<style scoped>
+<style>
 .card-content p {
   margin: 0;
   line-height: 1.2;
diff --git a/src/editor.ts b/src/editor/constants.ts
similarity index 72%
rename from src/editor.ts
rename to src/editor/constants.ts
index a5aefc4..10146f6 100644
--- a/src/editor.ts
+++ b/src/editor/constants.ts
@@ -1,5 +1,3 @@
-export type State = KV<boolean>
-
 export const movementKeys = [
   'ArrowLeft',
   'ArrowRight',
@@ -37,11 +35,3 @@ export const blocks = [
   'separator',
   'statBlock'
 ]
-
-export function getElementAndParentName (el: Node) {
-  const element = el.nodeName === '#text' ? el.parentElement : el
-  return [
-    element?.nodeName,
-    element?.parentElement?.nodeName
-  ]
-}
diff --git a/src/editor/index.ts b/src/editor/index.ts
new file mode 100644
index 0000000..9c51e66
--- /dev/null
+++ b/src/editor/index.ts
@@ -0,0 +1,40 @@
+export type State = KV<boolean>
+export {
+  movementKeys,
+  controlSequenceKeys,
+  elementNameToMenuState,
+  marks,
+  blocks
+} from './constants'
+
+function simpleAction (cmd: string, arg?: string): () => boolean {
+  return () => {
+    return document.execCommand(cmd, false, arg)
+  }
+}
+
+function insertHorizontalRule (): () => boolean {
+  return () => {
+    const hr = document.execCommand('insertHorizontalRule')
+    const p = document.execCommand('formatblock', false, 'P')
+    return hr && p
+  }
+}
+
+export const menuActionToCommand: KV<() => boolean> = {
+  paragraph: simpleAction('formatblock', 'P'),
+  heading1: simpleAction('formatblock', 'H1'),
+  heading2: simpleAction('formatblock', 'H2'),
+  heading3: simpleAction('formatblock', 'H3'),
+  bulletList: simpleAction('insertUnorderedList'),
+  numberedList: simpleAction('insertOrderedList'),
+  separator: insertHorizontalRule()
+}
+
+export function getElementAndParentName (el: Node) {
+  const element = el.nodeName === '#text' ? el.parentElement : el
+  return [
+    element?.nodeName,
+    element?.parentElement?.nodeName
+  ]
+}