diff --git a/packages/text-editor/src/components/ReferenceInput.svelte b/packages/text-editor/src/components/ReferenceInput.svelte index 42ccd1773d..5b1d912f83 100644 --- a/packages/text-editor/src/components/ReferenceInput.svelte +++ b/packages/text-editor/src/components/ReferenceInput.svelte @@ -19,11 +19,8 @@ AnySvelteComponent, Button, ButtonKind, - EmojiPopup, - IconEmoji, handler, registerFocus, - showPopup, deviceOptionsStore as deviceInfo, checkAdaptiveMatching } from '@hcengineering/ui' @@ -35,9 +32,8 @@ import { completionConfig } from './extensions' import { EmojiExtension } from './extension/emoji' import { IsEmptyContentExtension } from './extension/isEmptyContent' - import Attach from './icons/Attach.svelte' - import RIMention from './icons/RIMention.svelte' import Send from './icons/Send.svelte' + import { generateDefaultActions } from './editor/actions' export let content: string = '' export let showHeader = false @@ -48,7 +44,7 @@ export let kindSend: ButtonKind = 'ghost' export let haveAttachment = false export let placeholder: IntlString | undefined = undefined - export let extraActions: RefAction[] | undefined = undefined + export let extraActions: RefAction[] = [] export let loading: boolean = false export let focusable: boolean = false export let boundary: HTMLElement | undefined = undefined @@ -57,7 +53,7 @@ const dispatch = createEventDispatcher() const buttonSize = 'medium' - let textEditor: TextEditor + let textEditor: TextEditor | undefined = undefined let isEmpty = true @@ -68,42 +64,22 @@ function setContent (content: string) { textEditor?.setContent(content) } - const defActions: RefAction[] = [ - { - label: textEditorPlugin.string.Attach, - icon: Attach, - action: () => { - dispatch('attach') - }, - order: 1001 + + const editorHandler: TextEditorHandler = { + insertText: (text) => { + textEditor?.insertText(text) }, - { - label: textEditorPlugin.string.Mention, - icon: RIMention, - action: () => textEditor.insertText('@'), - order: 3000 + insertTemplate: (name, text) => { + textEditor?.insertText(text) }, - { - label: textEditorPlugin.string.Emoji, - icon: IconEmoji, - action: (element) => { - showPopup( - EmojiPopup, - {}, - element, - (emoji) => { - if (!emoji) return - textEditor.insertText(emoji) - textEditor.focus() - }, - () => {} - ) - }, - order: 4001 + focus: () => { + textEditor?.focus() } - ] + } - let actions: RefAction[] = [] + let actions: RefAction[] = generateDefaultActions(editorHandler) + .concat(...extraActions) + .sort((a, b) => a.order - b.order) client.findAll(textEditorPlugin.class.RefInputActionItem, {}).then(async (res) => { const cont: RefAction[] = [] for (const r of res) { @@ -114,21 +90,13 @@ action: await getResource(r.action) }) } - actions = defActions.concat(...cont).sort((a, b) => a.order - b.order) + actions = actions.concat(...cont).sort((a, b) => a.order - b.order) }) export function submit (): void { - textEditor.submit() + textEditor?.submit() } - const editorHandler: TextEditorHandler = { - insertText: (text) => { - textEditor.insertText(text) - }, - insertTemplate: (name, text) => { - textEditor.insertText(text) - } - } function handleAction (a: RefAction, evt?: Event): void { a.action(evt?.target as HTMLElement, editorHandler) } @@ -138,10 +106,10 @@ export let focusIndex = -1 const { idx, focusManager } = registerFocus(focusIndex, { focus: () => { - const editable = textEditor?.isEditable() + const editable = textEditor?.isEditable() ?? false if (editable) { focused = true - textEditor.focus() + textEditor?.focus() } return editable }, @@ -175,7 +143,7 @@ if (!isEmpty || haveAttachment) { dispatch('message', ev.detail) content = '' - textEditor.clear() + textEditor?.clear() } }} on:blur={() => { @@ -205,42 +173,28 @@ {#if showActions || showSend}
- {#if showActions} -
+
+ {#if showActions} {#each actions as a}
{/if} - {/if} +
{#if showSend}
diff --git a/packages/text-editor/src/components/StyledTextBox.svelte b/packages/text-editor/src/components/StyledTextBox.svelte index 48968b0665..af5f82cc4f 100644 --- a/packages/text-editor/src/components/StyledTextBox.svelte +++ b/packages/text-editor/src/components/StyledTextBox.svelte @@ -26,6 +26,7 @@ import { ImageRef, FileAttachFunction } from './imageExt' import { Node as ProseMirrorNode } from '@tiptap/pm/model' + import { RefAction } from '../types' export let label: IntlString | undefined = undefined export let content: string @@ -33,8 +34,8 @@ export let kind: 'normal' | 'emphasized' | 'indented' = 'normal' export let alwaysEdit: boolean = false + export let extraActions: RefAction[] = [] export let showButtons: boolean = true - export let hideAttachments: boolean = false export let buttonSize: ButtonSize = 'medium' export let formatButtonSize: IconSize = 'small' export let hideExtraButtons: boolean = false @@ -225,7 +226,6 @@ { rawValue = evt.detail if (alwaysEdit) { diff --git a/packages/text-editor/src/components/StyledTextEditor.svelte b/packages/text-editor/src/components/StyledTextEditor.svelte index de0b5ca84d..e88b692e75 100644 --- a/packages/text-editor/src/components/StyledTextEditor.svelte +++ b/packages/text-editor/src/components/StyledTextEditor.svelte @@ -16,22 +16,12 @@ import { createEventDispatcher } from 'svelte' import { AnyExtension, mergeAttributes } from '@tiptap/core' import { Node as ProseMirrorNode } from '@tiptap/pm/model' - import { Asset, getResource, IntlString } from '@hcengineering/platform' + import { getResource, IntlString } from '@hcengineering/platform' import { getClient } from '@hcengineering/presentation' - import { - AnySvelteComponent, - Button, - ButtonSize, - EmojiPopup, - IconEmoji, - IconSize, - Scroller, - showPopup - } from '@hcengineering/ui' - + import { Button, ButtonSize, IconSize, Scroller } from '@hcengineering/ui' import textEditorPlugin from '../plugin' - import { RefInputAction, RefInputActionItem, TextEditorHandler, TextFormatCategory } from '../types' - import Attach from './icons/Attach.svelte' + import { RefAction, RefInputActionItem, TextEditorHandler, TextFormatCategory } from '../types' + import { generateDefaultActions } from './editor/actions' import TextEditor from './TextEditor.svelte' const dispatch = createEventDispatcher() @@ -39,7 +29,6 @@ export let content: string = '' export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder export let showButtons: boolean = true - export let hideAttachments: boolean = false export let buttonSize: ButtonSize = 'medium' export let formatButtonSize: IconSize = 'small' export let isScrollable: boolean = true @@ -49,6 +38,7 @@ export let full = false export let extensions: AnyExtension[] = [] export let editorAttributes: { [name: string]: string } = {} + export let extraActions: RefAction[] = [] export let boundary: HTMLElement | undefined = undefined export let textFormatCategories: TextFormatCategory[] = [ TextFormatCategory.Heading, @@ -60,30 +50,30 @@ TextFormatCategory.Table ] - let textEditor: TextEditor + let textEditor: TextEditor | undefined = undefined let contentHeight: number export function submit (): void { - textEditor.submit() + textEditor?.submit() } export function focus (): void { - textEditor.focus() + textEditor?.focus() } export function isEditable (): boolean { - return textEditor.isEditable() + return textEditor?.isEditable() ?? false } export function setEditable (editable: boolean): void { - textEditor.setEditable(editable) + textEditor?.setEditable(editable) } export function getContent (): string { return content } export function setContent (data: string): void { - textEditor.setContent(data) + textEditor?.setContent(data) } export function insertText (text: string): void { - textEditor.insertText(text) + textEditor?.insertText(text) } $: varsStyle = @@ -95,44 +85,22 @@ ? 'max-content' : maxHeight - interface RefAction { - label: IntlString - icon: Asset | AnySvelteComponent - action: RefInputAction - order: number - hidden?: boolean - } - const defActions: RefAction[] = [ - { - label: textEditorPlugin.string.Attach, - icon: Attach, - action: () => { - dispatch('attach') - }, - order: 1001 + const client = getClient() + const editorHandler: TextEditorHandler = { + insertText: (text) => { + textEditor?.insertText(text) }, - { - label: textEditorPlugin.string.Emoji, - icon: IconEmoji, - action: (element) => { - showPopup( - EmojiPopup, - {}, - element, - (emoji) => { - if (!emoji) return - textEditor.insertText(emoji) - textEditor.focus() - }, - () => {} - ) - }, - order: 4001 + insertTemplate: (name, text) => { + textEditor?.insertText(text) + dispatch('template', name) + }, + focus: () => { + textEditor?.focus() } - ] - - const client = getClient() - let actions: RefAction[] = [] + } + let actions: RefAction[] = generateDefaultActions(editorHandler) + .concat(...extraActions) + .sort((a, b) => a.order - b.order) client.findAll(textEditorPlugin.class.RefInputActionItem, {}).then(async (res) => { const cont: RefAction[] = [] for (const r of res) { @@ -143,7 +111,7 @@ action: await getResource(r.action) }) } - actions = defActions.concat(...cont).sort((a, b) => a.order - b.order) + actions = actions.concat(...cont).sort((a, b) => a.order - b.order) }) const mergedEditorAttributes = mergeAttributes( @@ -151,15 +119,6 @@ full ? { class: 'text-editor-view_full-height' } : { class: 'text-editor-view_compact' } ) - const editorHandler: TextEditorHandler = { - insertText: (text) => { - textEditor.insertText(text) - }, - insertTemplate: (name, text) => { - textEditor.insertText(text) - dispatch('template', name) - } - } function handleAction (a: RefAction, evt?: Event): void { a.action(evt?.target as HTMLElement, editorHandler) } @@ -185,7 +144,7 @@ * @public */ export function removeNode (nde: ProseMirrorNode): void { - textEditor.removeNode(nde) + textEditor?.removeNode(nde) } @@ -218,7 +177,7 @@ on:content={(ev) => { dispatch('message', ev.detail) content = '' - textEditor.clear() + textEditor?.clear() }} on:blur on:focus @@ -237,7 +196,7 @@ on:content={(ev) => { dispatch('message', ev.detail) content = '' - textEditor.clear() + textEditor?.clear() }} on:blur on:focus @@ -250,7 +209,7 @@ {#if showButtons}
- {#each actions.filter((it) => it.hidden !== true) as a} + {#each actions as a}
- {#if $$slots.right} -
- -
- {/if}
{/if} diff --git a/packages/text-editor/src/components/editor/actions.ts b/packages/text-editor/src/components/editor/actions.ts new file mode 100644 index 0000000000..9ad907f67e --- /dev/null +++ b/packages/text-editor/src/components/editor/actions.ts @@ -0,0 +1,36 @@ +import { EmojiPopup, IconEmoji, showPopup } from '@hcengineering/ui' +import RiMention from '../icons/RIMention.svelte' +import textEditorPlugin from '../../plugin' +import { RefAction, TextEditorHandler } from '../../types' + +export const generateDefaultActions = (editorHandler: TextEditorHandler): RefAction[] => { + return [ + { + label: textEditorPlugin.string.Mention, + icon: RiMention, + action: () => editorHandler.insertText('@'), + order: 3000 + }, + { + label: textEditorPlugin.string.Emoji, + icon: IconEmoji, + action: (element) => { + showPopup( + EmojiPopup, + {}, + element, + (emoji) => { + if (emoji === null || emoji === undefined) { + return + } + + editorHandler.insertText(emoji) + editorHandler.focus() + }, + () => {} + ) + }, + order: 4001 + } + ] +} diff --git a/packages/text-editor/src/index.ts b/packages/text-editor/src/index.ts index 47e941d40e..5926964a11 100644 --- a/packages/text-editor/src/index.ts +++ b/packages/text-editor/src/index.ts @@ -28,6 +28,7 @@ export { default as StyledTextBox } from './components/StyledTextBox.svelte' export { default as StyledTextEditor } from './components/StyledTextEditor.svelte' export { default as TextEditor } from './components/TextEditor.svelte' export { default as TextEditorStyleToolbar } from './components/TextEditorStyleToolbar.svelte' +export { default as AttachIcon } from './components/icons/Attach.svelte' export { default } from './plugin' export * from './types' diff --git a/packages/text-editor/src/types.ts b/packages/text-editor/src/types.ts index 447133dfa0..62db5cd748 100644 --- a/packages/text-editor/src/types.ts +++ b/packages/text-editor/src/types.ts @@ -8,6 +8,7 @@ import type { AnySvelteComponent } from '@hcengineering/ui' export interface TextEditorHandler { insertText: (html: string) => void insertTemplate: (name: string, html: string) => void + focus: () => void } /** * @public diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index 3053a9106c..cfc1b5e6ef 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -661,6 +661,7 @@ input.search { .h-14 { height: 3.5rem; } .h-16 { height: 4rem; } .h-18 { height: 4.5rem; } +.h-32 { height: 8rem; } .h-50 { height: 12.5rem; } .h-60 { height: 15.0rem; } .w-min { width: min-content; } diff --git a/plugins/attachment-resources/src/components/AttachmentRefInput.svelte b/plugins/attachment-resources/src/components/AttachmentRefInput.svelte index 7366463f84..9f9fed7f63 100644 --- a/plugins/attachment-resources/src/components/AttachmentRefInput.svelte +++ b/plugins/attachment-resources/src/components/AttachmentRefInput.svelte @@ -13,16 +13,15 @@ // limitations under the License. --> @@ -377,7 +390,6 @@ {placeholder} {alwaysEdit} {showButtons} - hideAttachments={!enableAttachments} {buttonSize} {formatButtonSize} {maxHeight} @@ -386,14 +398,12 @@ {enableBackReferences} {isScrollable} {boundary} + {extraActions} on:changeSize on:changeContent on:blur on:focus on:open-document - on:attach={() => { - attach() - }} attachFile={async (file) => { return createAttachment(file) }}