From d06f3316b330aaa8206aafeb91fc21f534f508da Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Tue, 23 May 2023 17:42:39 +0700 Subject: [PATCH] UBER-134: Back references (#3233) Signed-off-by: Andrey Sobolev --- packages/panel/src/components/Panel.svelte | 1 + packages/presentation/lang/en.json | 1 + packages/presentation/lang/ru.json | 1 + packages/presentation/src/plugin.ts | 1 + packages/text-editor/src/Completion.ts | 48 ++- .../src/components/FullDescriptionBox.svelte | 12 +- .../src/components/ReferenceInput.svelte | 60 +--- .../src/components/StyledTextBox.svelte | 10 + .../src/components/StyledTextEditor.svelte | 15 +- .../text-editor/src/components/extensions.ts | 40 +++ packages/theme/styles/button.scss | 292 +++++++++++++++++ packages/theme/styles/global.scss | 1 + packages/ui/src/components/Button.svelte | 295 +----------------- packages/ui/src/components/Panel.svelte | 4 +- plugins/activity-resources/src/activity.ts | 2 +- .../AttachmentStyleBoxEditor.svelte | 109 +++++++ .../src/components/AttachmentStyledBox.svelte | 4 + plugins/attachment-resources/src/index.ts | 2 + plugins/chunter-resources/src/backlinks.ts | 14 +- .../activity/TxBacklinkReference.svelte | 12 - plugins/chunter-resources/src/index.ts | 1 + .../src/components/EditApplication.svelte | 17 +- .../src/components/EditVacancy.svelte | 147 +++++---- .../src/components/CreateIssue.svelte | 5 + .../components/EditComponent.svelte | 24 +- .../components/components/NewComponent.svelte | 1 + .../components/issues/edit/EditIssue.svelte | 91 ++---- .../milestones/EditMilestone.svelte | 20 +- .../components/milestones/NewMilestone.svelte | 6 +- .../templates/EditIssueTemplate.svelte | 186 ++++------- .../src/components/EditDoc.svelte | 44 +-- .../src/components/UpDownNavigator.svelte | 15 +- plugins/view-resources/src/utils.ts | 11 +- tests/sanity/tests/recruit.spec.ts | 2 +- tests/sanity/tests/tracker.spec.ts | 4 +- tests/sanity/tests/tracker.utils.ts | 8 +- 36 files changed, 835 insertions(+), 671 deletions(-) create mode 100644 packages/theme/styles/button.scss create mode 100644 plugins/attachment-resources/src/components/AttachmentStyleBoxEditor.svelte diff --git a/packages/panel/src/components/Panel.svelte b/packages/panel/src/components/Panel.svelte index 6f247663bc..7769385c28 100644 --- a/packages/panel/src/components/Panel.svelte +++ b/packages/panel/src/components/Panel.svelte @@ -141,6 +141,7 @@ + {#if isUtils && $$slots.utils}
diff --git a/packages/presentation/lang/en.json b/packages/presentation/lang/en.json index 8c6b78c720..91446c5659 100644 --- a/packages/presentation/lang/en.json +++ b/packages/presentation/lang/en.json @@ -4,6 +4,7 @@ "Cancel": "Cancel", "Ok": "Ok", "Save": "Save", + "Saved": "Saved...", "Download": "Download", "Delete": "Delete", "Close": "Close", diff --git a/packages/presentation/lang/ru.json b/packages/presentation/lang/ru.json index 3b9393f8f3..346ee9e09c 100644 --- a/packages/presentation/lang/ru.json +++ b/packages/presentation/lang/ru.json @@ -4,6 +4,7 @@ "Cancel": "Отменить", "Ok": "Ок", "Save": "Сохранить", + "Saved": "Сохранено...", "Download": "Скачать", "Delete": "Удалить", "Close": "Закрыть", diff --git a/packages/presentation/src/plugin.ts b/packages/presentation/src/plugin.ts index b3e9f9e7ae..1d885c4186 100644 --- a/packages/presentation/src/plugin.ts +++ b/packages/presentation/src/plugin.ts @@ -33,6 +33,7 @@ export default plugin(presentationId, { Cancel: '' as IntlString, Ok: '' as IntlString, Save: '' as IntlString, + Saved: '' as IntlString, Download: '' as IntlString, Delete: '' as IntlString, Close: '' as IntlString, diff --git a/packages/text-editor/src/Completion.ts b/packages/text-editor/src/Completion.ts index f648728fb6..28d34f1dff 100644 --- a/packages/text-editor/src/Completion.ts +++ b/packages/text-editor/src/Completion.ts @@ -1,13 +1,38 @@ import { Node, mergeAttributes } from '@tiptap/core' import Suggestion, { SuggestionOptions } from '@tiptap/suggestion' +import { Plugin, PluginKey } from '@tiptap/pm/state' + export interface CompletionOptions { HTMLAttributes: Record renderLabel: (props: { options: CompletionOptions, node: any }) => string suggestion: Omit + showDoc?: (event: MouseEvent, _id: string, _class: string) => void } -// export const CompletionPluginKey = new PluginKey('completion') +export function clickHandler (opt: CompletionOptions): Plugin { + return new Plugin({ + key: new PluginKey('completion-handleClickLink'), + props: { + handleClick: (view, pos, event) => { + if (event.button !== 0) { + return false + } + + const link = (event.target as HTMLElement)?.closest('span') + if (link != null) { + const _class = link.getAttribute('data-objectclass') + const _id = link.getAttribute('data-id') + if (_id != null && _class != null) { + opt.showDoc?.(event, _id, _class) + } + } + + return false + } + } + }) +} export const Completion = Node.create({ name: 'reference', @@ -63,10 +88,16 @@ export const Completion = Node.create({ inline: true, - selectable: false, + selectable: true, atom: true, + draggable: true, + + onFocus () { + console.log('focus') + }, + addAttributes () { return { id: { @@ -126,7 +157,15 @@ export const Completion = Node.create({ renderHTML ({ node, HTMLAttributes }) { return [ 'span', - mergeAttributes({ 'data-type': this.name }, this.options.HTMLAttributes, HTMLAttributes), + mergeAttributes( + { + 'data-type': this.name, + class: 'antiButton secondary cursor-pointer', + style: 'width: fit-content;display: inline-flex;' + }, + this.options.HTMLAttributes, + HTMLAttributes + ), this.options.renderLabel({ options: this.options, node @@ -174,7 +213,8 @@ export const Completion = Node.create({ Suggestion({ editor: this.editor, ...this.options.suggestion - }) + }), + clickHandler(this.options) ] } }) diff --git a/packages/text-editor/src/components/FullDescriptionBox.svelte b/packages/text-editor/src/components/FullDescriptionBox.svelte index c1a1444f66..dba2923f56 100644 --- a/packages/text-editor/src/components/FullDescriptionBox.svelte +++ b/packages/text-editor/src/components/FullDescriptionBox.svelte @@ -26,6 +26,7 @@ export let icon: Asset | AnySvelteComponent = IconDescription export let content: string = '' export let maxHeight: string = '40vh' + export let enableBackReferences = false const dispatch = createEventDispatcher() @@ -44,5 +45,14 @@
- + diff --git a/packages/text-editor/src/components/ReferenceInput.svelte b/packages/text-editor/src/components/ReferenceInput.svelte index e93705a3b2..46814c348f 100644 --- a/packages/text-editor/src/components/ReferenceInput.svelte +++ b/packages/text-editor/src/components/ReferenceInput.svelte @@ -27,32 +27,30 @@ showPopup, tooltip } from '@hcengineering/ui' - import { AnyExtension } from '@tiptap/core' import { createEventDispatcher } from 'svelte' import { Completion } from '../Completion' import textEditorPlugin from '../plugin' import { FORMAT_MODES, FormatMode, RefAction, RefInputActionItem, TextEditorHandler } from '../types' import LinkPopup from './LinkPopup.svelte' - import MentionList from './MentionList.svelte' - import { SvelteRenderer } from './SvelteRenderer' import TextEditor from './TextEditor.svelte' + import { completionConfig } from './extensions' import Attach from './icons/Attach.svelte' import Bold from './icons/Bold.svelte' - import RIBold from './icons/RIBold.svelte' import Code from './icons/Code.svelte' - import RICode from './icons/RICode.svelte' import CodeBlock from './icons/CodeBlock.svelte' - import RILink from './icons/RILink.svelte' - import RIMention from './icons/RIMention.svelte' import Italic from './icons/Italic.svelte' - import RIItalic from './icons/RIItalic.svelte' import Link from './icons/Link.svelte' import ListBullet from './icons/ListBullet.svelte' import ListNumber from './icons/ListNumber.svelte' import Quote from './icons/Quote.svelte' + import RIBold from './icons/RIBold.svelte' + import RICode from './icons/RICode.svelte' + import RIItalic from './icons/RIItalic.svelte' + import RILink from './icons/RILink.svelte' + import RIMention from './icons/RIMention.svelte' + import RIStrikethrough from './icons/RIStrikethrough.svelte' import Send from './icons/Send.svelte' import Strikethrough from './icons/Strikethrough.svelte' - import RIStrikethrough from './icons/RIStrikethrough.svelte' import TextStyle from './icons/TextStyle.svelte' const dispatch = createEventDispatcher() @@ -185,42 +183,6 @@ textEditor.submit() } - const editorExtensions: AnyExtension[] = [ - Completion.configure({ - HTMLAttributes: { - class: 'reference' - }, - suggestion: { - items: async (query: { query: string }) => { - return [] - }, - render: () => { - let component: any - - return { - onStart: (props: any) => { - component = new SvelteRenderer(MentionList, { - ...props, - close: () => { - component.destroy() - } - }) - }, - onUpdate (props: any) { - component.updateProps(props) - }, - onKeyDown (props: any) { - return component.onKeyDown(props) - }, - onExit () { - component.destroy() - } - } - } - } - }) - ] - const editorHandler: TextEditorHandler = { insertText: (text) => { textEditor.insertText(text) @@ -277,6 +239,12 @@ focusManager?.setFocus(idx) } } + const completionPlugin = Completion.configure({ + ...completionConfig, + showDoc (event: MouseEvent, _id: string, _class: string) { + dispatch('open-document', { event, _id, _class }) + } + })
@@ -380,7 +348,7 @@ focused = true updateFocus() }} - extensions={editorExtensions} + extensions={[completionPlugin]} on:selection-update={updateFormattingState} on:update placeholder={placeholder ?? textEditorPlugin.string.EditorPlaceholder} diff --git a/packages/text-editor/src/components/StyledTextBox.svelte b/packages/text-editor/src/components/StyledTextBox.svelte index 01123a84c0..dd595a816f 100644 --- a/packages/text-editor/src/components/StyledTextBox.svelte +++ b/packages/text-editor/src/components/StyledTextBox.svelte @@ -13,8 +13,10 @@ resizeObserver } from '@hcengineering/ui' import { createEventDispatcher } from 'svelte' + import { Completion } from '../Completion' import textEditorPlugin from '../plugin' import StyledTextEditor from './StyledTextEditor.svelte' + import { completionConfig } from './extensions' export let label: IntlString | undefined = undefined export let content: string @@ -33,6 +35,7 @@ export let focusable: boolean = false export let enableFormatting = false export let autofocus = false + export let enableBackReferences: boolean = false const Mode = { View: 1, @@ -119,6 +122,12 @@ focusManager?.setFocus(idx) } } + const completionPlugin = Completion.configure({ + ...completionConfig, + showDoc (event: MouseEvent, _id: string, _class: string) { + dispatch('open-document', { event, _id, _class }) + } + }) @@ -149,6 +158,7 @@ {focusable} {enableFormatting} {autofocus} + extensions={enableBackReferences ? [completionPlugin] : []} bind:content={rawValue} bind:this={textEditor} on:attach diff --git a/packages/text-editor/src/components/StyledTextEditor.svelte b/packages/text-editor/src/components/StyledTextEditor.svelte index ecb09aae37..a6fad53f0e 100644 --- a/packages/text-editor/src/components/StyledTextEditor.svelte +++ b/packages/text-editor/src/components/StyledTextEditor.svelte @@ -32,22 +32,23 @@ import { headingLevels, mInsertTable } from './extensions' import Attach from './icons/Attach.svelte' import Bold from './icons/Bold.svelte' - import RIBold from './icons/RIBold.svelte' import Code from './icons/Code.svelte' - import RICode from './icons/RICode.svelte' import CodeBlock from './icons/CodeBlock.svelte' import Header from './icons/Header.svelte' import IconTable from './icons/IconTable.svelte' import Italic from './icons/Italic.svelte' - import RIItalic from './icons/RIItalic.svelte' import Link from './icons/Link.svelte' - import RILink from './icons/RILink.svelte' import ListBullet from './icons/ListBullet.svelte' import ListNumber from './icons/ListNumber.svelte' import Quote from './icons/Quote.svelte' - import Strikethrough from './icons/Strikethrough.svelte' + import RIBold from './icons/RIBold.svelte' + import RICode from './icons/RICode.svelte' + import RIItalic from './icons/RIItalic.svelte' + import RILink from './icons/RILink.svelte' import RIStrikethrough from './icons/RIStrikethrough.svelte' + import Strikethrough from './icons/Strikethrough.svelte' // import RIMention from './icons/RIMention.svelte' + import { AnyExtension } from '@tiptap/core' import AddColAfter from './icons/table/AddColAfter.svelte' import AddColBefore from './icons/table/AddColBefore.svelte' import AddRowAfter from './icons/table/AddRowAfter.svelte' @@ -75,6 +76,7 @@ export let enableFormatting = false export let autofocus = false export let full = false + export let extensions: AnyExtension[] = [] let textEditor: TextEditor let isEmpty = true @@ -555,6 +557,7 @@ { diff --git a/packages/text-editor/src/components/extensions.ts b/packages/text-editor/src/components/extensions.ts index 8ca304fa95..2ecd523320 100644 --- a/packages/text-editor/src/components/extensions.ts +++ b/packages/text-editor/src/components/extensions.ts @@ -14,6 +14,9 @@ import TipTapCodeBlock from '@tiptap/extension-code-block' import Gapcursor from '@tiptap/extension-gapcursor' import Link from '@tiptap/extension-link' +import { CompletionOptions } from '../Completion' +import MentionList from './MentionList.svelte' +import { SvelteRenderer } from './SvelteRenderer' export const tableExtensions = [ Table.configure({ @@ -126,3 +129,40 @@ export const mInsertTable = [ header: true } ] + +/** + * @public + */ +export const completionConfig: Partial = { + HTMLAttributes: { + class: 'reference' + }, + suggestion: { + items: async (query: { query: string }) => { + return [] + }, + render: () => { + let component: any + + return { + onStart: (props: any) => { + component = new SvelteRenderer(MentionList, { + ...props, + close: () => { + component.destroy() + } + }) + }, + onUpdate (props: any) { + component.updateProps(props) + }, + onKeyDown (props: any) { + return component.onKeyDown(props) + }, + onExit () { + component.destroy() + } + } + } + } +} diff --git a/packages/theme/styles/button.scss b/packages/theme/styles/button.scss new file mode 100644 index 0000000000..feb674ca54 --- /dev/null +++ b/packages/theme/styles/button.scss @@ -0,0 +1,292 @@ +.antiButton { + display: flex; + align-items: center; + flex-shrink: 0; + padding: 0 0.625rem; + min-width: 1.375rem; + white-space: nowrap; + color: var(--theme-caption-color); + background-color: transparent; + border: 1px solid transparent; + transition-property: border, background-color, color, box-shadow; + transition-duration: 0.15s; + + &.accent { + font-weight: 500; + } + .btn-icon { + color: var(--theme-content-color); + transition: color 0.15s; + pointer-events: none; + } + .btn-right-icon { + margin-left: 0.5rem; + color: var(--theme-halfcontent-color); + transition: color 0.15s; + pointer-events: none; + } + &:not(.only-icon) .btn-icon:not(.spinner) { + margin-right: 0.5rem; + } + &:not(.only-icon) .btn-right-icon { + margin-left: 0.5rem; + } + &.no-border:not(.only-icon) .btn-icon, + &.link-bordered:not(.only-icon) .btn-icon, + &.list:not(.only-icon) .btn-icon, + &.sh-circle:not(.only-icon) .btn-icon { + margin-right: 0.25rem; + } + + &.short { + max-width: 7rem; + } + &.sh-no-shape { + border-radius: 0.25rem; + } + &.sh-round { + border-radius: 0.5rem; + } + &.sh-circle { + border-radius: 1rem; + &.link { + padding: 0 0.5rem 0 0.25rem; + } + } + &.sh-rectangle-right { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + } + &.sh-rectangle-left { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + } + &.sh-filter { + border-radius: 0 0 0.5rem 0.5rem; + } + &.bs-solid { + border-style: solid; + } + &.bs-dashed { + border-style: dashed; + } + + &.highlight { + box-shadow: inset 0 0 1px 1px var(--primary-button-enabled); + + &:hover { + box-shadow: inset 0 0 1px 1px var(--primary-button-hovered); + } + } + &:hover { + .btn-icon { + color: var(--theme-caption-color); + } + } + &:focus { + &:not(.sh-filter) { + box-shadow: 0 0 0 2px var(--primary-button-focused-border); + } + &.sh-filter { + border-color: var(--primary-button-focused-border); + } + } + &:disabled { + color: var(--theme-dark-color); + cursor: not-allowed; + + .btn-icon { + opacity: 0.5; + } + } + + &.jf-left { + justify-content: flex-start; + } + &.jf-center { + justify-content: center; + } + &.only-icon { + padding: 0; + } + + &.secondary { + background-color: var(--theme-button-enabled); + border-color: var(--theme-button-border); + + // &.medium:not(.only-icon) { + // padding: 0 0.75rem; + // } + &:hover { + background-color: var(--theme-button-hovered); + } + &:active { + background-color: var(--theme-button-pressed); + } + &:focus { + background-color: var(--theme-button-focused); + } + &:disabled { + background-color: var(--theme-button-disabled); + } + + &.selected { + background-color: var(--theme-button-hovered); + .btn-icon { + color: var(--theme-caption-color); + } + } + } + &.no-border { + font-weight: 400; + color: var(--theme-content-color); + background-color: var(--theme-button-enabled); + box-shadow: var(--button-shadow); + + &:hover { + color: var(--theme-caption-color); + background-color: var(--theme-button-hovered); + + .btn-icon { + color: var(--theme-caption-color); + } + } + &:disabled { + color: var(--theme-trans-color); + background-color: var(--theme-list-button-color); + cursor: default; + .btn-icon { + color: var(--theme-trans-color); + } + &:hover { + color: var(--theme-trans-color); + .btn-icon { + color: var(--theme-trans-color); + } + } + } + } + &.transparent { + &:hover { + background-color: var(--theme-button-hovered); + } + &.selected { + background-color: var(--highlight-select); + } + &.selected:hover { + background-color: var(--highlight-select-hover); + } + } + &.link { + padding: 0 0.875rem; + &:hover { + color: var(--theme-caption-color); + background-color: var(--theme-bg-color); + border-color: var(--theme-divider-color); + .btn-icon { + color: var(--theme-content-color); + } + } + &:disabled { + color: var(--theme-dark-color); + background-color: transparent; + border-color: transparent; + cursor: auto; + + .btn-icon { + color: var(--theme-content-color); + } + } + } + &.link-bordered { + padding: 0 0.5rem; + color: var(--theme-content-color); + background-color: var(--theme-link-button-color); + border-color: var(--theme-button-border); + &:hover { + color: var(--theme-caption-color); + background-color: var(--theme-link-button-hover); + border-color: var(--theme-list-divider-color); + .btn-icon { + color: var(--theme-caption-color); + } + } + &.small { + padding: 0 0.25rem; + } + } + &.list { + padding: 0 0.625em; + min-height: 1.75rem; + color: var(--theme-content-color); + background-color: var(--theme-button-enabled); + border: 1px solid var(--theme-button-border); + border-radius: 1.5rem; + // transition-property: border, color, background-color; + // transition-duration: 0.15s; + + .btn-icon { + color: var(--theme-dark-color); + } + &:hover { + color: var(--theme-caption-color); + background-color: var(--theme-button-hovered); + border-color: var(--theme-button-border); + } + &:focus { + box-shadow: none; + } + } + &.primary { + padding: 0 0.75rem; + font-weight: 500; + color: var(--primary-button-color); + background-color: var(--primary-button-enabled); + border-color: var(--primary-button-border); + + .btn-icon { + color: var(--white-color); + } + &:hover { + background-color: var(--primary-button-hovered); + } + &:active { + background-color: var(--primary-button-pressed); + } + &:focus { + background-color: var(--primary-button-focused); + } + &:disabled { + background-color: var(--primary-button-disabled); + } + } + + &.notSelected { + color: var(--theme-dark-color); + + .btn-icon { + color: var(--theme-darker-color); + } + &:hover, + &:hover .btn-icon { + color: var(--theme-content-color); + } + } + + &.dangerous { + color: var(--white-color); + background-color: var(--dangerous-bg-color); + border-color: var(--dangerous-bg-color); + + &:hover { + background-color: var(--dangerous-bg-hover); + } + &:focus { + box-shadow: var(--dangerous-shadow); + } + } + + .resetIconSize { + font-size: 16px; + } +} \ No newline at end of file diff --git a/packages/theme/styles/global.scss b/packages/theme/styles/global.scss index 96b9c4bc07..60217cfc5b 100644 --- a/packages/theme/styles/global.scss +++ b/packages/theme/styles/global.scss @@ -22,6 +22,7 @@ @import "./mixins.scss"; @import "./panel.scss"; @import "./prose.scss"; +@import "./button.scss"; @font-face { font-family: 'IBM Plex Sans'; diff --git a/packages/ui/src/components/Button.svelte b/packages/ui/src/components/Button.svelte index 35d43d5d60..f1973ecdb6 100644 --- a/packages/ui/src/components/Button.svelte +++ b/packages/ui/src/components/Button.svelte @@ -96,7 +96,7 @@
+
+
- - { - if (rawDesc !== object.description) onChange('description', object.description) + +
+ { + saved = evt.detail + }} + updateBacklinks={(doc, description) => { + updateBacklinks(client, doc._id, doc._class, doc._id, description) }} /> - { - onChange('fullDescription', res.detail) - }} - /> - +
+ +
+
+
- +
{/if} diff --git a/plugins/tracker-resources/src/components/CreateIssue.svelte b/plugins/tracker-resources/src/components/CreateIssue.svelte index 2b65c99497..3f25c9d9fe 100644 --- a/plugins/tracker-resources/src/components/CreateIssue.svelte +++ b/plugins/tracker-resources/src/components/CreateIssue.svelte @@ -71,6 +71,7 @@ import SetDueDateActionPopup from './SetDueDateActionPopup.svelte' import SetParentIssueActionPopup from './SetParentIssueActionPopup.svelte' import SubIssues from './SubIssues.svelte' + import { createBacklinks } from '@hcengineering/chunter-resources' export let space: Ref export let status: Ref | undefined = undefined @@ -400,6 +401,9 @@ issueUrl: currentProject && generateIssueShortLink(getIssueId(currentProject, value as Issue)) }) + // Create an backlink to document + await createBacklinks(client, _id, tracker.class.Issue, _id, object.description) + draftController.remove() resetObject() descriptionBox?.removeDraft(false) @@ -607,6 +611,7 @@ alwaysEdit showButtons={false} emphasized + enableBackReferences={true} bind:content={object.description} placeholder={tracker.string.IssueDescriptionPlaceholder} on:changeSize={() => dispatch('changeContent')} diff --git a/plugins/tracker-resources/src/components/components/EditComponent.svelte b/plugins/tracker-resources/src/components/components/EditComponent.svelte index e2eb8e343a..d0986fe8ef 100644 --- a/plugins/tracker-resources/src/components/components/EditComponent.svelte +++ b/plugins/tracker-resources/src/components/components/EditComponent.svelte @@ -1,10 +1,11 @@ { const trimmedLabel = rawLabel.trim() @@ -41,3 +44,16 @@ } }} /> + +
+ { + updateBacklinks(client, doc._id, doc._class, doc._id, description) + }} + /> +
diff --git a/plugins/tracker-resources/src/components/components/NewComponent.svelte b/plugins/tracker-resources/src/components/components/NewComponent.svelte index dd2776d109..71139d4988 100644 --- a/plugins/tracker-resources/src/components/components/NewComponent.svelte +++ b/plugins/tracker-resources/src/components/components/NewComponent.svelte @@ -62,6 +62,7 @@ bind:content={object.description} placeholder={tracker.string.ComponentDescriptionPlaceholder} emphasized + showButtons={false} /> {#if !embedded} @@ -165,21 +146,21 @@ {#if issue !== undefined} dispatch('close')} > - - - - + {#if !embedded} + + {/if} + + {#if embedded} {#if issueId}{issueId}{/if} @@ -187,11 +168,10 @@ {:else if issueId}{issueId}{/if} - + {#if saved} -