diff --git a/src/assets/editor/text.svg.txt b/src/assets/editor/text.svg.txt
new file mode 100644
index 0000000..aeff875
--- /dev/null
+++ b/src/assets/editor/text.svg.txt
@@ -0,0 +1,3 @@
+
diff --git a/src/components/deck-card-editor.vue b/src/components/deck-card-editor.vue
index 4f42cfe..fd0928e 100644
--- a/src/components/deck-card-editor.vue
+++ b/src/components/deck-card-editor.vue
@@ -7,7 +7,7 @@ import { Component, Prop, Vue } from 'vue-property-decorator'
import Editor from '@editorjs/editorjs'
import List from '@editorjs/list'
-import { Heading, Delimiter } from '@/editor'
+import { /* Heading, */ Delimiter, Boop } from '@/editor'
@Component
export default class DeckCardEditor extends Vue {
@@ -26,9 +26,10 @@ export default class DeckCardEditor extends Vue {
holderId: this.id,
autofocus: false,
tools: {
- header: Heading,
- list: List,
- delimiter: Delimiter
+ // header: Heading,
+ list: { class: List, inlineToolbar: true },
+ delimiter: { class: Delimiter, inlineToolbar: true },
+ boop: { class: Boop, inlineToolbar: true }
},
// data: {},
placeholder: 'Click here to write your card.'
diff --git a/src/editor/content-block.ts b/src/editor/content-block.ts
new file mode 100644
index 0000000..c37a5b0
--- /dev/null
+++ b/src/editor/content-block.ts
@@ -0,0 +1,168 @@
+import { BlockTool, BlockToolData, ToolboxConfig, API, HTMLPasteEvent, ToolConstructable, ToolSettings } from '@editorjs/editorjs'
+import icon from '@/assets/editor/text.svg.txt'
+
+interface PasteConfig {
+ tags: string[];
+}
+
+interface ContentBlockConfig extends ToolSettings {
+ placeholder?: string;
+}
+
+interface ContentBlockArgs {
+ api: API;
+ config?: ContentBlockConfig;
+ data?: BlockToolData;
+}
+
+interface CSSClasses {
+ [key: string]: string;
+}
+
+interface ContentBlockData extends BlockToolData {
+ text: string;
+}
+
+export class ContentBlock implements BlockTool {
+ // Default placeholder for Paragraph Tool
+ static get DEFAULT_PLACEHOLDER (): string {
+ return ''
+ }
+
+ static _supportedTags: string[] = []
+
+ static _toolboxConfig: ToolboxConfig = {
+ // icon: '',
+ icon,
+ title: 'UnnamedContentPlugin'
+ }
+
+ protected _defaultPlaceholder (): string {
+ return ContentBlock.DEFAULT_PLACEHOLDER
+ }
+
+ protected api: API
+ protected _element: HTMLElement
+ protected _data: ContentBlockData
+ protected _config: ContentBlockConfig
+ protected _placeholder: string
+ protected _CSS: CSSClasses
+ protected onKeyUp: (event: KeyboardEvent) => void
+
+ constructor ({ data, config, api }: ContentBlockArgs) {
+ this.api = api
+ this._config = config as ContentBlockConfig
+
+ this._CSS = {
+ block: this.api.styles.block,
+ wrapper: 'card-content-block'
+ }
+
+ this.onKeyUp = (event: KeyboardEvent) => this._onKeyUp(event)
+
+ // Placeholder it is first Block
+ this._placeholder = config?.placeholder ? config.placeholder : this._defaultPlaceholder()
+ this._data = data as ContentBlockData
+ this._element = this._render()
+ }
+
+ // Check if text content is empty and set empty string to inner html.
+ // We need this because some browsers (e.g. Safari) insert
into empty contenteditanle elements
+ _onKeyUp (event: KeyboardEvent) {
+ if (event.code !== 'Backspace' && event.code !== 'Delete') return
+
+ if (this._element.textContent === '') {
+ this._element.innerHTML = ''
+ }
+ }
+
+ // render tool view
+ // whenever a redraw is needed the result is saved in this._element
+ protected _render (): HTMLElement {
+ const el = document.createElement('DIV')
+ el.classList.add(this._CSS.wrapper, this._CSS.block)
+ el.dataset.placeholder = this._placeholder
+ el.addEventListener('keyup', this.onKeyUp)
+ el.innerHTML = this.data.text
+ el.contentEditable = 'true'
+
+ return el
+ }
+
+ // Return Tool's view
+ public render (): HTMLElement {
+ return this._element
+ }
+
+ // Method that specified how to merge two Text blocks.
+ // Called by Editor.js by backspace at the beginning of the Block
+ public merge (data: ContentBlockData) {
+ this.data = {
+ text: this.data.text + data.text
+ }
+ }
+
+ // Validate Paragraph block data (by default checks for emptiness)
+ public validate (savedData: ContentBlockData): boolean {
+ if (!savedData.text) return false
+ return savedData.text.trim() !== ''
+ }
+
+ // Extract Tool's data from the view
+ public save (toolsContent: HTMLElement): ContentBlockData {
+ return {
+ text: toolsContent.innerHTML
+ }
+ }
+
+ // On paste callback fired from Editor.
+ public onPaste (event: HTMLPasteEvent) {
+ this.data = {
+ text: event.detail.data.innerHTML
+ }
+ }
+
+ /**
+ * Enable Conversion Toolbar. Paragraph can be converted to/from other tools
+ */
+ static get conversionConfig () {
+ return {
+ export: 'text', // to convert Paragraph to other block, use 'text' property of saved data
+ import: 'text' // to covert other block's exported string to Paragraph, fill 'text' property of tool data
+ }
+ }
+
+ // Sanitizer rules
+ static get sanitize () {
+ return {
+ text: { br: true }
+ }
+ }
+
+ get data (): ContentBlockData {
+ const text = this._element?.innerHTML
+ if (text !== undefined) this._data.text = text
+ if (this._data.text === undefined) this._data.text = ''
+ return this._data
+ }
+
+ set data (data: ContentBlockData) {
+ this._data = data || {}
+ this._element.innerHTML = this._data.text || ''
+ }
+
+ // Used by Editor.js paste handling API.
+ // Provides configuration to handle the tools tags.
+ static get pasteConfig (): PasteConfig {
+ return {
+ tags: this._supportedTags
+ }
+ }
+
+ // Icon and title for displaying at the Toolbox
+ static get toolbox (): ToolboxConfig {
+ return this._toolboxConfig
+ }
+}
+
+export default ContentBlock as ToolConstructable
diff --git a/src/editor/block-tool.ts b/src/editor/contentless-block.ts
similarity index 83%
rename from src/editor/block-tool.ts
rename to src/editor/contentless-block.ts
index d2d6e6b..beb7872 100644
--- a/src/editor/block-tool.ts
+++ b/src/editor/contentless-block.ts
@@ -1,12 +1,16 @@
import { BlockTool, BlockToolData, ToolConfig, ToolboxConfig, API } from '@editorjs/editorjs'
+interface BlockToolConfig extends ToolConfig {
+ [key: string]: string;
+}
+
export interface BlockToolArgs {
api: API;
- config: ToolConfig;
+ config: BlockToolConfig;
data?: BlockToolData;
}
-export class BlockToolExt implements BlockTool {
+export class ContentlessBlock implements BlockTool {
protected api: API
protected _element: HTMLElement
protected _data: object
@@ -42,4 +46,4 @@ export class BlockToolExt implements BlockTool {
}
}
-export default BlockToolExt
+export default ContentlessBlock
diff --git a/src/editor/delimiter.ts b/src/editor/delimiter.ts
index 4cb8c71..7bd9db6 100644
--- a/src/editor/delimiter.ts
+++ b/src/editor/delimiter.ts
@@ -1,13 +1,9 @@
import { ToolConstructable } from '@editorjs/editorjs'
-import BlockTool from './block-tool'
+import ContentlessBlock from './contentless-block'
import icon from '../assets/editor/delimiter.svg.txt'
const title = 'Delimiter'
-export class Delimiter extends BlockTool {
- static get contentless () {
- return true
- }
-
+export class Delimiter extends ContentlessBlock {
protected get _CSS () {
return {
block: this.api.styles.block,
diff --git a/src/editor/heading.ts b/src/editor/heading.ts
index 7c1836c..4f857b2 100644
--- a/src/editor/heading.ts
+++ b/src/editor/heading.ts
@@ -9,6 +9,10 @@ import icon5 from '../assets/editor/header5.svg.txt'
import icon6 from '../assets/editor/header6.svg.txt'
const title = 'Heading'
+interface PasteConfig {
+ tags: string[];
+}
+
enum HeadingLevel {
One = 1,
Two = 2,
@@ -153,7 +157,7 @@ class Heading extends BlockToolExt {
// Used by Editor.js paste handling API.
// Provides configuration to handle H1-H6 tags.
- static get pasteConfig () {
+ static get pasteConfig (): PasteConfig {
return {
tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']
}
diff --git a/src/editor/index.ts b/src/editor/index.ts
index 61f8ae3..7b1ec63 100644
--- a/src/editor/index.ts
+++ b/src/editor/index.ts
@@ -1,2 +1,3 @@
export { default as Delimiter } from './delimiter'
-export { default as Heading } from './heading'
+// export { default as Heading } from './heading'
+export { default as Boop } from './content-block'