From e464b2b5f9d24d989107e93a69e22f76fb02c29a Mon Sep 17 00:00:00 2001 From: Ilya Date: Sun, 3 Nov 2019 22:37:03 +0200 Subject: [PATCH] feat(images): image extensions support for adding images (Image extension from tiptap) re #16 --- README.md | 5 +- demo/main.js | 2 +- demo/pages/Index.vue | 3 +- .../nativeExtensions/image/Image.ts | 72 +++++++ .../image/ImageUploadArea.vue | 106 ++++++++++ .../nativeExtensions/image/ImageWindow.vue | 185 ++++++++++++++++++ src/extensions/nativeExtensions/index.ts | 1 + src/extensions/nativeExtensions/link/Link.ts | 2 +- src/i18n/en/index.ts | 16 ++ src/i18n/index.ts | 19 +- src/i18n/ru/index.ts | 16 ++ 11 files changed, 417 insertions(+), 10 deletions(-) create mode 100644 src/extensions/nativeExtensions/image/Image.ts create mode 100644 src/extensions/nativeExtensions/image/ImageUploadArea.vue create mode 100644 src/extensions/nativeExtensions/image/ImageWindow.vue diff --git a/README.md b/README.md index faf3560..600e578 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ You can use the necessary extensions. The corresponding buttons are added automa How to import and use them can be seen in the example above. -Available extensions: +Available extensions (native tiptap extensions from `tiptap-extensions` package): - `Bold` - `Italic` @@ -207,6 +207,7 @@ Available extensions: - `Underline` - `Code` - `CodeBlock` +- `Image` - `Paragraph` - `BulletList` - `OrderedList` @@ -362,7 +363,7 @@ You can add content after the toolbar. ## TODO -- images uploading (free hosting by default) [Relevant issue.](https://github.com/iliyaZelenko/tiptap-vuetify/issues/16) Ability to choose your uploading strategy. +- better images support: uploading (free hosting by default) [Relevant issue.](https://github.com/iliyaZelenko/tiptap-vuetify/issues/16) Ability to choose your uploading strategy. [Resize](https://github.com/scrumpy/tiptap/issues/333) image and change other params. - site with full-docs and examples - emoticons - tests diff --git a/demo/main.js b/demo/main.js index c031949..c8f5033 100644 --- a/demo/main.js +++ b/demo/main.js @@ -20,7 +20,7 @@ import { MAIN_MODULE } from './config' const vuetify = new Vuetify({ lang: { - current: 'he' // en | es | fr | pl | ru | uk | ptbr | tr | he + current: 'es' // en | es | fr | pl | ru | uk | ptbr | tr | he } }) diff --git a/demo/pages/Index.vue b/demo/pages/Index.vue index b01b4f2..edc56f2 100644 --- a/demo/pages/Index.vue +++ b/demo/pages/Index.vue @@ -36,7 +36,7 @@ export default { async created () { const { Heading, Bold, Italic, Strike, Underline, Code, CodeBlock, Paragraph, BulletList, OrderedList, ListItem, - Link, Blockquote, HardBreak, HorizontalRule, History + Link, Blockquote, HardBreak, HorizontalRule, History, Image } = await MAIN_MODULE this.extensions = [ @@ -52,6 +52,7 @@ export default { ListItem, // если нужно использовать список (BulletList, OrderedList) BulletList, OrderedList, + Image, [Heading, { // Опции которые попадают в расширение tiptap options: { diff --git a/src/extensions/nativeExtensions/image/Image.ts b/src/extensions/nativeExtensions/image/Image.ts new file mode 100644 index 0000000..c2832aa --- /dev/null +++ b/src/extensions/nativeExtensions/image/Image.ts @@ -0,0 +1,72 @@ +import { Image as ImageOriginal } from 'tiptap-extensions' +import { VuetifyIconsGroups } from '~/configs/theme' +import VuetifyIcon from '~/extensions/nativeExtensions/icons/VuetifyIcon' +import I18nText from '~/i18n/I18nText' +import AbstractExtension from '~/extensions/AbstractExtension' +import ExtensionActionInterface from '~/extensions/actions/ExtensionActionInterface' +import Vue from 'vue' +import ExtensionActionRenderBtn from '~/extensions/actions/renders/btn/ExtensionActionRenderBtn.ts' + +export default class Image extends AbstractExtension { + constructor (options) { + super(options, ImageOriginal) + } + + get availableActions (): ExtensionActionInterface[] { + const nativeExtensionName = 'image' + + return [ + { + render: new ExtensionActionRenderBtn({ + tooltip: new I18nText('extensions.Image.buttons.tooltip'), + icons: { + [VuetifyIconsGroups.md]: new VuetifyIcon('image'), + [VuetifyIconsGroups.fa]: new VuetifyIcon('fas fa-image'), + [VuetifyIconsGroups.mdi]: new VuetifyIcon('mdi-image'), + [VuetifyIconsGroups.mdiSvg]: new VuetifyIcon('M8.5,13.5L11,16.5L14.5,12L19,18H5M21,19V5C21,3.89 20.1,3 19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19Z') + }, + nativeExtensionName, + async onClick ({ context, editor }) { + const ImageWindow = (await import('~/extensions/nativeExtensions/image/ImageWindow.vue')).default + const LinkWindowComponent = Vue.extend(ImageWindow) + const instance = new LinkWindowComponent({ + vuetify: Vue.prototype.tiptapVuetifyPlugin.vuetify, + propsData: { + value: true, + nativeExtensionName, + context, + editor + } + }) + + instance.$mount() + document.querySelector('body')!.appendChild(instance.$el) + } + }) + } + ] + } +} + +// export const icons = { +// save: { +// [VuetifyIconsGroups.md]: new VuetifyIcon('check'), +// [VuetifyIconsGroups.fa]: new VuetifyIcon('fas fa-check'), +// [VuetifyIconsGroups.mdi]: new VuetifyIcon('mdi-check') +// }, +// cancel: { +// [VuetifyIconsGroups.md]: new VuetifyIcon('link_off'), +// [VuetifyIconsGroups.fa]: new VuetifyIcon('fas fa-unlink'), +// [VuetifyIconsGroups.mdi]: new VuetifyIcon('mdi-link-off') +// }, +// linkUpdate: { +// [VuetifyIconsGroups.md]: new VuetifyIcon('link'), +// [VuetifyIconsGroups.fa]: new VuetifyIcon('fas fas fa-link'), +// [VuetifyIconsGroups.mdi]: new VuetifyIcon('mdi-link') +// }, +// linkAdd: { +// [VuetifyIconsGroups.md]: new VuetifyIcon('link'), +// [VuetifyIconsGroups.fa]: new VuetifyIcon('fas fa-link'), +// [VuetifyIconsGroups.mdi]: new VuetifyIcon('mdi-link-plus') +// } +// } diff --git a/src/extensions/nativeExtensions/image/ImageUploadArea.vue b/src/extensions/nativeExtensions/image/ImageUploadArea.vue new file mode 100644 index 0000000..6aad112 --- /dev/null +++ b/src/extensions/nativeExtensions/image/ImageUploadArea.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/src/extensions/nativeExtensions/image/ImageWindow.vue b/src/extensions/nativeExtensions/image/ImageWindow.vue new file mode 100644 index 0000000..bbe309d --- /dev/null +++ b/src/extensions/nativeExtensions/image/ImageWindow.vue @@ -0,0 +1,185 @@ + + + diff --git a/src/extensions/nativeExtensions/index.ts b/src/extensions/nativeExtensions/index.ts index ba092bc..9845d96 100644 --- a/src/extensions/nativeExtensions/index.ts +++ b/src/extensions/nativeExtensions/index.ts @@ -51,3 +51,4 @@ export { default as HardBreak } from '~/extensions/nativeExtensions/HardBreak' export { default as HorizontalRule } from '~/extensions/nativeExtensions/HorizontalRule' export { default as History } from '~/extensions/nativeExtensions/History' export { default as Link } from '~/extensions/nativeExtensions/link/Link' +export { default as Image } from '~/extensions/nativeExtensions/image/Image' diff --git a/src/extensions/nativeExtensions/link/Link.ts b/src/extensions/nativeExtensions/link/Link.ts index 958d419..a159a34 100644 --- a/src/extensions/nativeExtensions/link/Link.ts +++ b/src/extensions/nativeExtensions/link/Link.ts @@ -25,7 +25,7 @@ export default class Link extends AbstractExtension { : 'extensions.Link.buttons.notActive.tooltip'), icons: { [VuetifyIconsGroups.md]: new VuetifyIcon('link'), - [VuetifyIconsGroups.fa]: new VuetifyIcon('fas fas fa-link'), + [VuetifyIconsGroups.fa]: new VuetifyIcon('fas fa-link'), [VuetifyIconsGroups.mdi]: new VuetifyIcon('mdi-link'), [VuetifyIconsGroups.mdiSvg]: new VuetifyIcon('M3.9,12C3.9,10.29 5.29,8.9 7,8.9H11V7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H11V15.1H7C5.29,15.1 3.9,13.71 3.9,12M8,13H16V11H8V13M17,7H13V8.9H17C18.71,8.9 20.1,10.29 20.1,12C20.1,13.71 18.71,15.1 17,15.1H13V17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7Z') }, diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index ecf2cf1..746a8f0 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -114,6 +114,22 @@ export default { apply: 'Apply' } } + }, + Image: { + buttons: { + tooltip: 'Image' + }, + window: { + title: 'Add Image', + or: 'OR', + form: { + sourceLink: 'Image URL' + }, + buttons: { + close: 'Close', + apply: 'Apply' + } + } } } } diff --git a/src/i18n/index.ts b/src/i18n/index.ts index caa8fbf..1a86abf 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -27,8 +27,8 @@ export function getCurrentLang () { return TiptapVuetifyPlugin.vuetifyLang || defaultLanguage } -export function getMsg (path: string, args?): string { - let currentLang = getCurrentLang() +export function getMsg (path: string, args?, lang: null | string = null): string { + let currentLang = lang || getCurrentLang() if (!dictionary[currentLang]) { currentLang = defaultLanguage @@ -37,9 +37,18 @@ export function getMsg (path: string, args?): string { } const dictionaryByLang = dictionary[currentLang] - const target = path.split('.').reduce((prev: string, curr: string) => { - return prev[curr] - }, dictionaryByLang) + let target + + try { + target = path.split('.').reduce((prev: string, curr: string) => { + return prev[curr] + }, dictionaryByLang) + } catch (e) { + ConsoleLogger.warn(`Cannot get translation "${path}" for language "${currentLang}". Fallback "${defaultLanguage}" is used instead. Contribution to github is welcome.`) + + // Использовать defaultLanguage если фраза не переведена на текущий язык + return getMsg(path, args, defaultLanguage) + } if (target instanceof Function) { return target(args) diff --git a/src/i18n/ru/index.ts b/src/i18n/ru/index.ts index 0a37812..142d42e 100644 --- a/src/i18n/ru/index.ts +++ b/src/i18n/ru/index.ts @@ -114,6 +114,22 @@ export default { apply: 'Применить' } } + }, + Image: { + buttons: { + tooltip: 'Картинка' + }, + window: { + title: 'Добавить картинку', + or: 'ИЛИ', + form: { + sourceLink: 'Ссылка на картинку' + }, + buttons: { + close: 'Закрыть', + apply: 'Применить' + } + } } } }