425 lines
17 KiB
Svelte
425 lines
17 KiB
Svelte
<script lang="ts">
|
||
import { onMount, onDestroy } from 'svelte';
|
||
import { Color } from '@tiptap/extension-color'
|
||
import ListItem from '@tiptap/extension-list-item'
|
||
import TextStyle from '@tiptap/extension-text-style'
|
||
import StarterKit from "@tiptap/starter-kit";
|
||
import { Editor } from "@tiptap/core";
|
||
// import Highlight from '@tiptap/extension-highlight';
|
||
|
||
// import Highlight from '@tiptap/extension-highlight' ????
|
||
// import Typography from '@tiptap/extension-typography' ????
|
||
|
||
import "./element_tiptap_editor.scss";
|
||
|
||
// https://tiptap.dev/docs/examples/basics/default-text-editor
|
||
// https://tiptap.dev/docs/examples/basics/formatting
|
||
// <code class="language-css">
|
||
|
||
export let html_text: string = '';
|
||
export let default_minimal: boolean = false;
|
||
export let show_menu: boolean = true;
|
||
|
||
if (default_minimal) {
|
||
show_menu = false;
|
||
}
|
||
|
||
// export let html_text: string = `
|
||
// <h2>
|
||
// Hi there,
|
||
// </h2>
|
||
// <p>
|
||
// this is a <em>basic</em> example of <strong>Tiptap</strong>. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists:
|
||
// </p>
|
||
// <ul>
|
||
// <li>
|
||
// That’s a bullet list with one …
|
||
// </li>
|
||
// <li>
|
||
// … or two list items.
|
||
// </li>
|
||
// </ul>
|
||
// <p>
|
||
// Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block:
|
||
// </p>
|
||
// <pre><code class="language-css">body {
|
||
// display: none;
|
||
// }</code></pre>
|
||
// <p>
|
||
// I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too.
|
||
// </p>
|
||
// <blockquote>
|
||
// Wow, that’s amazing. Good work, boy! 👏
|
||
// <br />
|
||
// — Mom
|
||
// </blockquote>
|
||
// `;
|
||
|
||
let element: HTMLDivElement;
|
||
let editor: any;
|
||
let show_button_kv_defaults: any = {
|
||
bold: true,
|
||
italic: true,
|
||
strike: true,
|
||
code: true,
|
||
paragraph: true,
|
||
heading__h1: true,
|
||
heading__h2: true,
|
||
heading__h3: true,
|
||
unsetAllMarks: true,
|
||
bulletList: true,
|
||
orderedList: true,
|
||
hardBreak: true,
|
||
undo: true,
|
||
redo: true,
|
||
};
|
||
export let show_button_kv: any;
|
||
|
||
if (show_button_kv) {
|
||
show_button_kv = { ...show_button_kv_defaults, ...show_button_kv };
|
||
} else {
|
||
show_button_kv = show_button_kv_defaults;
|
||
}
|
||
|
||
// export let new_json = editor?.getJSON();
|
||
export let new_html: string = '';
|
||
|
||
onMount(() => {
|
||
editor = new Editor({
|
||
element: element,
|
||
extensions: [
|
||
Color.configure({ types: [TextStyle.name, ListItem.name] }),
|
||
TextStyle.configure({ types: [ListItem.name] }),
|
||
StarterKit,
|
||
],
|
||
content: html_text,
|
||
onTransaction: () => {
|
||
// force re-render so `editor.isActive` works as expected
|
||
editor = editor;
|
||
|
||
let updated_html = editor.getHTML();
|
||
if (updated_html == '<p></p>') {
|
||
new_html = '';
|
||
} else {
|
||
new_html = updated_html;
|
||
}
|
||
},
|
||
onUpdate: ({ editor }) => {
|
||
let updated_html = editor.getHTML();
|
||
if (updated_html == '<p></p>') {
|
||
new_html = '';
|
||
} else {
|
||
new_html = updated_html;
|
||
}
|
||
}
|
||
});
|
||
});
|
||
|
||
onDestroy(() => {
|
||
if (editor) {
|
||
editor.destroy();
|
||
}
|
||
});
|
||
|
||
function getContent() {
|
||
if (editor) {
|
||
return editor.getHTML();
|
||
}
|
||
return '';
|
||
}
|
||
</script>
|
||
|
||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||
<div
|
||
on:click={() => {
|
||
if (default_minimal) {
|
||
editor.chain().focus().setParagraph().run();
|
||
show_menu = true;
|
||
}
|
||
}}
|
||
on:mouseleave={() => {
|
||
if (default_minimal) {
|
||
show_menu = false;
|
||
}
|
||
}}
|
||
class="editor border border-gray-300 rounded p-1 pb-2 bg-gray-200"
|
||
>
|
||
|
||
{#if editor && show_menu}
|
||
<div class="control-group bg-gray-200 border-b border-gray-400 p-1">
|
||
<div class="button-group flex flex-row flex-wrap gap-4 items-center justify-between">
|
||
|
||
<span>
|
||
<button
|
||
type="button"
|
||
on:click={() => console.log(editor?.getHTML()) && editor.chain().toggleBold().run()}
|
||
disabled={!editor.can().chain().focus().toggleBold().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md font-bold"
|
||
class:variant-ghost-secondary={editor.isActive("bold") ?? false}
|
||
class:hidden={!show_button_kv.bold}
|
||
>
|
||
<span class="fas fa-bold mx-1"></span>
|
||
<!-- Bold -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleItalic().run()}
|
||
disabled={!editor.can().chain().focus().toggleItalic().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md italic"
|
||
class:variant-ghost-secondary={editor.isActive("italic") ?? false}
|
||
class:hidden={!show_button_kv.italic}
|
||
>
|
||
<span class="fas fa-italic mx-1"></span>
|
||
<!-- Italic -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleStrike().run()}
|
||
disabled={!editor.can().chain().focus().toggleStrike().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md line-through"
|
||
class:variant-ghost-secondary={editor.isActive("strike") ?? false}
|
||
class:hidden={!show_button_kv.strike}
|
||
>
|
||
<span class="fas fa-strikethrough mx-1"></span>
|
||
<!-- Strike -->
|
||
</button>
|
||
</span>
|
||
|
||
|
||
<span>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleCode().run()}
|
||
disabled={!editor.can().chain().focus().toggleCode().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("code") ?? false}
|
||
class:hidden={!show_button_kv.code}
|
||
>
|
||
<span class="fas fa-code mx-1"></span>
|
||
<!-- Code -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().unsetAllMarks().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:hidden={!show_button_kv.unsetAllMarks}
|
||
>
|
||
<!-- <span class="fas fa-broom mx-1"></span> -->
|
||
<span class="fas fa-remove-format mx-1"></span>
|
||
<!-- Clear marks -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().clearNodes().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:hidden={!show_button_kv.clearNodes}
|
||
>
|
||
<!-- <span class="fas fa-broom mx-1"></span> -->
|
||
<span class="fas fa-remove-format mx-1"></span>
|
||
Clear nodes
|
||
</button>
|
||
</span>
|
||
|
||
<span>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().setParagraph().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("paragraph") ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.paragraph}
|
||
title="Paragraph"
|
||
>
|
||
<span class="fas fa-paragraph mx-1"></span>
|
||
<!-- Paragraph -->
|
||
</button>
|
||
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md text-xs"
|
||
class:variant-ghost-secondary={editor.isActive("heading", { level: 1 }) ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.heading__h1}
|
||
title="Heading 1 <h1>"
|
||
>
|
||
<span class="fas fa-heading mx-1"></span>1
|
||
<!-- H1 -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md text-xs"
|
||
class:variant-ghost-secondary={editor.isActive("heading", { level: 2 }) ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.heading__h2}
|
||
title="Heading 2 <h2>"
|
||
>
|
||
<span class="fas fa-heading mx-1"></span>2
|
||
<!-- <span class="text-xs">2</span> -->
|
||
<!-- 2 -->
|
||
<!-- H2 -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md text-xs"
|
||
class:variant-ghost-secondary={editor.isActive("heading", { level: 3 }) ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.heading__h3}
|
||
title="Heading 3 <h3>"
|
||
>
|
||
<span class="fas fa-heading mx-1"></span>3
|
||
<!-- H3 -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleHeading({ level: 4 }).run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("heading", { level: 4 }) ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.heading__h4}
|
||
title="Heading 4 <h4>"
|
||
>
|
||
H4
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleHeading({ level: 5 }).run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("heading", { level: 5 }) ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.heading__h5}
|
||
title="Heading 5 <h5>"
|
||
>
|
||
H5
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleHeading({ level: 6 }).run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("heading", { level: 6 }) ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.heading__h6}
|
||
title="Heading 6 <h6>"
|
||
>
|
||
H6
|
||
</button>
|
||
</span>
|
||
|
||
|
||
<span>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleBulletList().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("bulletList") ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.bulletList}
|
||
title="Bullet list"
|
||
>
|
||
<span class="fas fa-list-ul mx-1"></span>
|
||
<!-- Bullet -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleOrderedList().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("orderedList") ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.orderedList}
|
||
title="Ordered list"
|
||
>
|
||
<span class="fas fa-list-ol mx-1"></span>
|
||
<!-- Ordered -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleCodeBlock().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("codeBlock") ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.codeBlock}
|
||
title="Code block"
|
||
>
|
||
Code block
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().toggleBlockquote().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive("blockquote") ? "is-active" : ""}
|
||
class:hidden={!show_button_kv.blockquote}
|
||
title="Blockquote"
|
||
>
|
||
Blockquote
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().setHorizontalRule().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:hidden={!show_button_kv.horizontalRule}
|
||
title="Horizontal rule"
|
||
>
|
||
Horizontal rule
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().setHardBreak().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:hidden={!show_button_kv.hardBreak}
|
||
title="Hard break"
|
||
>
|
||
<span class="fas fa-level-down-alt mx-1"></span>
|
||
<!-- Hard break -->
|
||
</button>
|
||
</span>
|
||
|
||
|
||
<span
|
||
class="justify-self-end"
|
||
>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().undo().run()}
|
||
disabled={!editor.can().chain().focus().undo().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:hidden={!show_button_kv.undo}
|
||
title="Undo"
|
||
>
|
||
<span class="fas fa-undo mx-1"></span>
|
||
<!-- Undo -->
|
||
</button>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().redo().run()}
|
||
disabled={!editor.can().chain().focus().redo().run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:hidden={!show_button_kv.redo}
|
||
title="Redo"
|
||
>
|
||
<span class="fas fa-redo mx-1"></span>
|
||
<!-- Redo -->
|
||
</button>
|
||
</span>
|
||
|
||
|
||
<!-- <span>
|
||
<button
|
||
type="button"
|
||
on:click={() => editor.chain().focus().setColor('#958DF1').run()}
|
||
class="btn btn-sm variant-glass-secondary hover:variant-filled-secondary rounded-md"
|
||
class:variant-ghost-secondary={editor.isActive('textStyle', { color: '#958DF1' }) ? 'is-active' : ''}
|
||
class:hidden={!show_button_kv.color__purple}
|
||
>
|
||
Purple
|
||
</button>
|
||
</span> -->
|
||
|
||
|
||
</div>
|
||
</div>
|
||
{/if}
|
||
|
||
<div
|
||
bind:this={element}
|
||
class="tiptap bg-slate-100 px-1 py-2"
|
||
/>
|
||
|
||
|
||
</div>
|
||
|
||
<style lang="scss">
|
||
</style>
|