From 435d094c7d3bbea4cca34fb68c385b9bc7aaf76b Mon Sep 17 00:00:00 2001 From: Alexander Onnikov Date: Sun, 22 Oct 2023 21:55:39 +0700 Subject: [PATCH] UBER-1076 Wiki images support (#3864) Signed-off-by: Alexander Onnikov --- .../src/components/PDFViewer.svelte | 11 +- packages/text-editor/lang/en.json | 6 + packages/text-editor/lang/ru.json | 6 + packages/text-editor/src/Completion.ts | 47 +---- .../src/components/CollaboratorEditor.svelte | 78 +++++++-- .../src/components/ImageStyleToolbar.svelte | 133 ++++++++++++++ .../src/components/StyledTextBox.svelte | 7 +- .../src/components/TextEditor.svelte | 77 +++++--- .../components/{ => extension}/imageExt.ts | 164 ++++++++---------- .../extension/inlineStyleToolbar.ts | 4 + .../src/components/icons/AlignCenter.svelte | 13 ++ .../src/components/icons/AlignLeft.svelte | 19 ++ .../src/components/icons/AlignRight.svelte | 19 ++ .../src/components/icons/ScaleOut.svelte | 19 ++ packages/text-editor/src/index.ts | 1 + packages/text-editor/src/plugin.ts | 7 + packages/text-editor/src/utils.ts | 20 +++ packages/theme/styles/_colors.scss | 2 + packages/theme/styles/_text-editor.scss | 30 ++++ 19 files changed, 470 insertions(+), 193 deletions(-) create mode 100644 packages/text-editor/src/components/ImageStyleToolbar.svelte rename packages/text-editor/src/components/{ => extension}/imageExt.ts (63%) create mode 100644 packages/text-editor/src/components/icons/AlignCenter.svelte create mode 100644 packages/text-editor/src/components/icons/AlignLeft.svelte create mode 100644 packages/text-editor/src/components/icons/AlignRight.svelte create mode 100644 packages/text-editor/src/components/icons/ScaleOut.svelte diff --git a/packages/presentation/src/components/PDFViewer.svelte b/packages/presentation/src/components/PDFViewer.svelte index d45dc6c358..82daf25a7c 100644 --- a/packages/presentation/src/components/PDFViewer.svelte +++ b/packages/presentation/src/components/PDFViewer.svelte @@ -26,6 +26,7 @@ export let contentType: string | undefined export let popupOptions: PopupOptions export let value: Doc + export let showIcon = true const dispatch = createEventDispatcher() // let imgView: 'img-horizontal-fit' | 'img-vertical-fit' | 'img-original-fit' = 'img-vertical-fit' @@ -51,11 +52,13 @@ >
-
-
- {iconLabel(name)} + {#if showIcon} +
+
+ {iconLabel(name)} +
-
+ {/if} {name}
diff --git a/packages/text-editor/lang/en.json b/packages/text-editor/lang/en.json index 8a5ccc7911..bd32d6946e 100644 --- a/packages/text-editor/lang/en.json +++ b/packages/text-editor/lang/en.json @@ -21,6 +21,12 @@ "GIF": "GIF", "Mention": "Mention", "Underlined": "Underlined", + "AlignCenter": "Align center", + "AlignLeft": "Align left", + "AlignRight": "Align right", + "ViewImage": "View image", + "ViewOriginal": "View original", + "MoreActions": "More actions", "FullDescription": "Full description", "NoFullDescription": "There are no detailed description", "EnableDiffMode": "Diff mode", diff --git a/packages/text-editor/lang/ru.json b/packages/text-editor/lang/ru.json index f236d0f94d..ae1f0e5a9f 100644 --- a/packages/text-editor/lang/ru.json +++ b/packages/text-editor/lang/ru.json @@ -21,6 +21,12 @@ "GIF": "GIF", "Mention": "Упомянуть", "Underlined": "Подчеркнутый", + "AlignCenter": "По центру", + "AlignLeft": "По левому краю", + "AlignRight": "По правому краю", + "ViewImage": "Открыть изображение", + "ViewOriginal": "Открыть оригинал", + "MoreActions": "Дополнительные действия", "FullDescription": "Детальное описание", "NoFullDescription": "Нет детального описания", "EnableDiffMode": "Режим сравнения", diff --git a/packages/text-editor/src/Completion.ts b/packages/text-editor/src/Completion.ts index 59a0f12198..47af741a1a 100644 --- a/packages/text-editor/src/Completion.ts +++ b/packages/text-editor/src/Completion.ts @@ -2,6 +2,7 @@ import { Node, mergeAttributes } from '@tiptap/core' import Suggestion, { SuggestionOptions } from '@tiptap/suggestion' import { Plugin, PluginKey } from '@tiptap/pm/state' +import { getDataAttribute } from './utils' export interface CompletionOptions { HTMLAttributes: Record @@ -96,49 +97,9 @@ export const Completion = Node.create({ addAttributes () { return { - id: { - default: null, - parseHTML: (element) => element.getAttribute('data-id'), - renderHTML: (attributes) => { - // eslint-disable-next-line - if (!attributes.id) { - return {} - } - - return { - 'data-id': attributes.id - } - } - }, - - label: { - default: null, - parseHTML: (element) => element.getAttribute('data-label'), - renderHTML: (attributes) => { - // eslint-disable-next-line - if (!attributes.label) { - return {} - } - - return { - 'data-label': attributes.label - } - } - }, - objectclass: { - default: null, - parseHTML: (element) => element.getAttribute('data-objectclass'), - renderHTML: (attributes) => { - // eslint-disable-next-line - if (!attributes.objectclass) { - return {} - } - - return { - 'data-objectclass': attributes.objectclass - } - } - } + id: getDataAttribute('id', null), + label: getDataAttribute('label', null), + objectclass: getDataAttribute('objectclass', null) } }, diff --git a/packages/text-editor/src/components/CollaboratorEditor.svelte b/packages/text-editor/src/components/CollaboratorEditor.svelte index 61edcbf8fe..2cdff2c3c4 100644 --- a/packages/text-editor/src/components/CollaboratorEditor.svelte +++ b/packages/text-editor/src/components/CollaboratorEditor.svelte @@ -45,8 +45,11 @@ import { noSelectionRender } from './editor/collaboration' import { defaultEditorAttributes } from './editor/editorProps' import { completionConfig, defaultExtensions } from './extensions' + import { InlinePopupExtension } from './extension/inlinePopup' import { InlineStyleToolbarExtension } from './extension/inlineStyleToolbar' + import { FileAttachFunction, ImageExtension } from './extension/imageExt' import { NodeUuidExtension } from './extension/nodeUuid' + import ImageStyleToolbar from './ImageStyleToolbar.svelte' import StyleButton from './StyleButton.svelte' import TextEditorStyleToolbar from './TextEditorStyleToolbar.svelte' @@ -72,6 +75,8 @@ export let onExtensions: () => AnyExtension[] = () => [] export let boundary: HTMLElement | undefined = undefined + export let attachFile: FileAttachFunction | undefined = undefined + let element: HTMLElement const ydoc = (getContext(CollaborationIds.Doc) as Y.Doc | undefined) ?? new Y.Doc() @@ -99,7 +104,8 @@ const currentUser = getCurrentAccount() let editor: Editor - let inlineToolbar: HTMLElement + let textToolbarElement: HTMLElement + let imageToolbarElement: HTMLElement let placeHolderStr: string = '' @@ -234,6 +240,34 @@ $: updateEditor(editor, field, comparedVersion) $: if (editor) dispatch('editor', editor) + const tippyOptions = { + zIndex: 100000, + popperOptions: { + modifiers: [ + { + name: 'preventOverflow', + options: { + boundary, + padding: 8, + altAxis: true, + tether: false + } + } + ] + } + } + + const optionalExtensions: AnyExtension[] = [] + + if (attachFile !== undefined) { + optionalExtensions.push( + ImageExtension.configure({ + inline: true, + attachFile + }) + ) + } + onMount(() => { ph.then(() => { editor = new Editor({ @@ -242,27 +276,25 @@ editorProps: { attributes: mergeAttributes(defaultEditorAttributes, editorAttributes, { class: 'flex-grow' }) }, extensions: [ ...defaultExtensions, + ...optionalExtensions, Placeholder.configure({ placeholder: placeHolderStr }), InlineStyleToolbarExtension.configure({ - tippyOptions: { - popperOptions: { - modifiers: [ - { - name: 'preventOverflow', - options: { - boundary, - padding: 8, - altAxis: true, - tether: false - } - } - ] - } - }, - element: inlineToolbar, + tippyOptions, + element: textToolbarElement, isSupported: () => !readonly, isSelectionOnly: () => false }), + InlinePopupExtension.configure({ + pluginKey: 'show-image-actions-popup', + element: imageToolbarElement, + tippyOptions, + shouldShow: () => { + if (!visible && !readonly) { + return false + } + return editor?.isActive('image') + } + }), Collaboration.configure({ document: ydoc, field @@ -331,7 +363,7 @@ const { idx, focusManager } = registerFocus(focusIndex, { focus: () => { if (visible) { - focus('start') + focus() } return visible && element !== null }, @@ -346,6 +378,10 @@ $: if (element) { element.addEventListener('focus', updateFocus, { once: true }) } + + function handleFocus () { + needFocus = true + } @@ -375,7 +411,7 @@
{/if} -
+
+
+ +
+
diff --git a/packages/text-editor/src/components/ImageStyleToolbar.svelte b/packages/text-editor/src/components/ImageStyleToolbar.svelte new file mode 100644 index 0000000000..44e071e84d --- /dev/null +++ b/packages/text-editor/src/components/ImageStyleToolbar.svelte @@ -0,0 +1,133 @@ + + + +{#if textEditor} + {#if textEditor.isActive('image')} + + + +
+ + {/if} +{/if} diff --git a/packages/text-editor/src/components/StyledTextBox.svelte b/packages/text-editor/src/components/StyledTextBox.svelte index af5f82cc4f..99f442797e 100644 --- a/packages/text-editor/src/components/StyledTextBox.svelte +++ b/packages/text-editor/src/components/StyledTextBox.svelte @@ -15,6 +15,7 @@ } from '@hcengineering/ui' import { createEventDispatcher } from 'svelte' import type { AnyExtension } from '@tiptap/core' + import { Node as ProseMirrorNode } from '@tiptap/pm/model' import { Completion } from '../Completion' import textEditorPlugin from '../plugin' @@ -23,9 +24,7 @@ import { completionConfig } from './extensions' import { EmojiExtension } from './extension/emoji' import { FocusExtension } from './extension/focus' - - import { ImageRef, FileAttachFunction } from './imageExt' - import { Node as ProseMirrorNode } from '@tiptap/pm/model' + import { ImageExtension, FileAttachFunction } from './extension/imageExt' import { RefAction } from '../types' export let label: IntlString | undefined = undefined @@ -170,7 +169,7 @@ } function configureExtensions () { - const imagePlugin = ImageRef.configure({ + const imagePlugin = ImageExtension.configure({ inline: true, HTMLAttributes: {}, attachFile, diff --git a/packages/text-editor/src/components/TextEditor.svelte b/packages/text-editor/src/components/TextEditor.svelte index 60fc8d8a02..efad036f10 100644 --- a/packages/text-editor/src/components/TextEditor.svelte +++ b/packages/text-editor/src/components/TextEditor.svelte @@ -1,6 +1,6 @@ -
+
+ +
+ { + needFocus = true + }} + /> +
+