﻿<template>
  <div class="ui-rte" :disabled="disabled">
    <div ref="editor" :id="id">
      <editor-menu-bubble :editor="editor" :keep-in-bounds="true" v-slot="{ commands, isActive, menu }">
        <div class="ui-rte-overlay-controls theme-dark" :class="{ 'is-active': menu.isActive }" :style="`left: ${menu.left}px; bottom: ${menu.bottom}px;`">
          <button type="button" class="ui-rte-overlay-control" :class="{ 'is-active': isActive.bold() }" @click="commands.bold"><ui-icon symbol="fth-bold" /></button>
          <button type="button" class="ui-rte-overlay-control" :class="{ 'is-active': isActive.italic() }" @click="commands.italic"><ui-icon symbol="fth-italic" /></button>
          <button type="button" class="ui-rte-overlay-control" :class="{ 'is-active': isActive.underline() }" @click="commands.underline"><ui-icon symbol="fth-underline" /></button>
          <button type="button" class="ui-rte-overlay-control" :class="{ 'is-active': isActive.link() }" @click="commands.link"><ui-icon symbol="fth-link" /></button>
          <button type="button" class="ui-rte-overlay-control" :class="{ 'is-active': isActive.code() }" @click="commands.code"><ui-icon symbol="fth-code" /></button>
        </div>
      </editor-menu-bubble>
      <editor-menu-bar :editor="editor" v-slot="{ commands, isActive }"> <!--// TODO this triggers a recursive loop error when used in multiple properties per editor--> 
        <div class="ui-rte-controls">
          <span class="ui-rte-controls-label">Editor</span>
          <button type="button" title="Undo" class="ui-rte-control" @click="commands.undo" :disabled="commands.undoDepth() < 1"><ui-icon symbol="fth-chevron-left" /></button>
          <button type="button" title="Redo" class="ui-rte-control" @click="commands.redo" :disabled="commands.redoDepth() < 1"><ui-icon symbol="fth-chevron-right" /></button>
        </div>
      </editor-menu-bar>
      <editor-content class="ui-rte-input" :editor="editor" />
    </div>
  </div>
</template>


<script>
  import Strings from 'zero/helpers/strings.js';
  import { debounce as _debounce } from 'underscore';
  import { Editor, EditorContent, EditorMenuBubble, EditorMenuBar } from 'tiptap';
  import { HardBreak } from './rte.extensions.js';
  import { Bold, Code, Italic, Link, Strike, Underline, History, Placeholder, HorizontalRule, ListItem, BulletList, OrderedList, Heading } from 'tiptap-extensions';


  export default {
    name: 'uiRte',

    components: { EditorContent, EditorMenuBubble, EditorMenuBar  },

    props: {
      value: {
        type: String,
        default: ''
      },
      disabled: {
        type: Boolean,
        default: false
      },
      placeholder: {
        type: String,
        default: null
      },
      extensions: {
        type: Array,
        default: () => []
      },
      disabledExtensions: {
        type: Array,
        default: () => []
      }
    },

    data: () => ({
      id: 'rte-' + Strings.guid(),
      blocked: false,
      onDebouncedChange: null,
      editor: null
    }),

    watch: {
      value()
      {
        if (!this.blocked)
        {
          this.init();
        }
      },
      disabled(value)
      {
        this.editor.setOptions({
          editable: !value
        })
      },
    },

    created()
    {
      this.onDebouncedChange = _debounce(this.onChange, 350);
    },

    mounted()
    {
      let extensions = [
        ...this.extensions,
        new HardBreak(),
        new Link(),
        new Bold(),
        new Code(),
        new Italic(),
        new Strike(),
        new Underline(),
        new History(),
        new ListItem(),
        new BulletList(),
        new OrderedList(),
        new HorizontalRule(),
        new Heading({
          levels: [2, 3, 4],
        })
      ];

      if (this.placeholder)
      {
        extensions.push(new Placeholder({
          emptyEditorClass: 'is-editor-empty',
          emptyNodeClass: 'is-empty',
          emptyNodeText: this.placeholder,
          showOnlyWhenEditable: true,
          showOnlyCurrent: true
        }));
      }

      if (this.disabledExtensions.length)
      {
        extensions = extensions.filter(x => this.disabledExtensions.indexOf(x.name) < 0);
      }

      this.editor = new Editor({
        editable: !this.disabled,
        extensions: extensions,
        onUpdate: opts => this.onDebouncedChange(opts.getHTML())
      });

      this.init();
    },

    methods: {
      init()
      {
        this.editor.setContent(this.value);
      },

      onChange(content)
      {
        this.blocked = true;
        this.$emit('input', content);
        this.$nextTick(() => this.blocked = false);
      }
    },

    beforeDestroy()
    {
      this.editor.destroy();
    }
  }
</script>

<style lang="scss">

  .ui-rte-input
  {
    height: auto;
    min-height: 48px;
    padding-top: 9px;

    p
    {
      margin: 0;
    }

    p + p
    {
      margin-top: 1em;
    }

    p.is-editor-empty:first-child:before
    {
      content: attr(data-empty-text);
      float: left;
      color: var(--color-input-placeholder);
      opacity: .7;
      pointer-events: none;
      height: 0;
    }

    a
    {
      color: var(--color-primary);
      text-decoration: underline;
      cursor: pointer;
    }
  }

  .ui-rte-overlay-controls
  {
    position: absolute;
    display: flex;
    z-index: 20;
    background: var(--color-bg);
    border-radius: var(--radius);
    padding: 5px;
    margin-bottom: 8px;
    transform: translateX(-50%);
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.2s, visibility 0.2s;
    pointer-events: none;

    &:after
    {
      content: '';
      position: absolute;
      left: 50%;
      top: 100%;
      width: 0;
      height: 0;
      border: 8px solid transparent;
      margin-left: -7px;
      border-top-color: var(--color-bg);
    }
  }

  .ui-rte-overlay-controls.is-active
  {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  .ui-rte-overlay-control
  {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 0;
    color: var(--color-text);
    height: 32px;
    width: 32px;
    border-radius: var(--radius);
    cursor: pointer;
  }

  .ui-rte-overlay-control + .ui-rte-overlay-control
  {
    margin-left: 4px;
  }

  .ui-rte-overlay-control:hover
  {
    background-color: var(--color-bg-shade-1);
  }

  .ui-rte-overlay-control.is-active
  {
    background-color: var(--color-bg-shade-1);
    color: var(--color-text-dim);
  }


  .ui-rte-controls
  {
    background: var(--color-input);
    border-radius: var(--radius) var(--radius) 0 0;
    padding: 5px 5px 0;
    display: flex;
    align-items: center;
    justify-content: flex-start;
  }

  .ui-rte-controls + .ui-rte-input
  {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }

  .ui-rte-controls-label
  {
    color: var(--color-text-dim);
    margin: 0 14px 0 8px;
    font-size: 10px;
    text-transform: uppercase;
    pointer-events: none;
    user-select: none;
  }

  .ui-rte-control
  {
    background-color: transparent;
    cursor: pointer;
    height: 30px;
    width: 30px;
    text-align: center;
    border-radius: 5px;
    font-size: 14px;
    vertical-align: bottom;
  }

  .ui-rte-control[disabled]
  {
    opacity: .5;
  }

  .ui-rte-control + .ui-rte-control
  {
    margin-left: 5px;
  }

  .ui-rte-control:hover, .ui-rte-control.is-active
  {
    background: var(--color-box);
    color: var(--color-text);
  }

  .ui-rte-input .ProseMirror
  {
    > *
    {
      margin: 1rem 0;
    }

    > :first-child
    {
      margin-top: 0;
    }
    > :last-child
    {
      margin-bottom: 0;
    }

    h1,h2,h3,h4,h5,h6
    {
      margin-bottom: .5rem;
      font-weight: bold;
    }

    h1 
    {
      font-size: 1.4em;
    }

    h2 
    {
      font-size: 1.2em;
    }

    h3
    {
      font-size: 1.1em;
    }

    h4, h5, h6
    {
      font-size: 1em;
    } 

    hr
    {
      border-bottom-style: dashed;
      border-bottom-color: var(--color-line-dashed);
    }
  }
</style>