mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-13 21:27:46 +02:00
Tables support for Markup editor (#2385)
Signed-off-by: Alexander Platov <sas_lord@mail.ru>
This commit is contained in:
@@ -97,6 +97,16 @@
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{:else if node.nodeName === 'TABLE'}
|
||||
<table class={node.className}><svelte:self nodes={node.childNodes} /></table>
|
||||
{:else if node.nodeName === 'TBODY'}
|
||||
<tbody><svelte:self nodes={node.childNodes} /></tbody>
|
||||
{:else if node.nodeName === 'TR'}
|
||||
<tr><svelte:self nodes={node.childNodes} /></tr>
|
||||
{:else if node.nodeName === 'TH'}
|
||||
<th><svelte:self nodes={node.childNodes} /></th>
|
||||
{:else if node.nodeName === 'TD'}
|
||||
<td><svelte:self nodes={node.childNodes} /></td>
|
||||
{:else}
|
||||
Unknown {node.nodeName}
|
||||
{/if}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
import CodeBlock from './icons/CodeBlock.svelte'
|
||||
|
||||
import { calculateDecorations } from './diff/decorations'
|
||||
import { defaultExtensions, headingLevels } from './extensions'
|
||||
import { defaultExtensions, headingLevels, mInsertTable } from './extensions'
|
||||
import Header from './icons/Header.svelte'
|
||||
import IconTable from './icons/IconTable.svelte'
|
||||
import Italic from './icons/Italic.svelte'
|
||||
@@ -316,74 +316,12 @@
|
||||
}
|
||||
|
||||
function insertTable (event: MouseEvent) {
|
||||
const tables = [
|
||||
{
|
||||
label: '2x2',
|
||||
rows: 2,
|
||||
cols: 2,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '3x3',
|
||||
rows: 3,
|
||||
cols: 3,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '2x1',
|
||||
rows: 2,
|
||||
cols: 1,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '5x5',
|
||||
rows: 5,
|
||||
cols: 5,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '1x2',
|
||||
rows: 1,
|
||||
cols: 2,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: 'Headed 2x2',
|
||||
rows: 2,
|
||||
cols: 2,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 3x3',
|
||||
rows: 3,
|
||||
cols: 3,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 2x1',
|
||||
rows: 2,
|
||||
cols: 1,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 5x5',
|
||||
rows: 5,
|
||||
cols: 5,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 1x2',
|
||||
rows: 1,
|
||||
cols: 2,
|
||||
header: true
|
||||
}
|
||||
]
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{
|
||||
value: [
|
||||
{ id: '#delete', label: presentation.string.Remove },
|
||||
...tables.map((it) => ({ id: it.label, text: it.label }))
|
||||
...mInsertTable.map((it) => ({ id: it.label, text: it.label }))
|
||||
]
|
||||
},
|
||||
getEventPositionElement(event),
|
||||
@@ -395,7 +333,7 @@
|
||||
updateFormattingState()
|
||||
return
|
||||
}
|
||||
const tab = tables.find((it) => it.label === val)
|
||||
const tab = mInsertTable.find((it) => it.label === val)
|
||||
if (tab) {
|
||||
editor.commands.insertTable({
|
||||
cols: tab.cols,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
export let emphasized: boolean = false
|
||||
export let alwaysEdit: boolean = false
|
||||
export let showButtons: boolean = true
|
||||
export let showAttach: boolean = false
|
||||
export let buttonSize: IconSize = 'small'
|
||||
export let hideExtraButtons: boolean = false
|
||||
export let maxHeight: 'max' | 'card' | 'limited' | string = 'max'
|
||||
@@ -78,6 +79,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="antiComponent styled-box clear-mins"
|
||||
class:emphasized
|
||||
@@ -98,11 +100,13 @@
|
||||
<StyledTextEditor
|
||||
{placeholder}
|
||||
{showButtons}
|
||||
{showAttach}
|
||||
{buttonSize}
|
||||
{maxHeight}
|
||||
{focusable}
|
||||
bind:content={rawValue}
|
||||
bind:this={textEditor}
|
||||
on:attach
|
||||
on:focus={() => {
|
||||
focused = true
|
||||
}}
|
||||
|
||||
@@ -13,19 +13,29 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
|
||||
import { IconSize, Scroller, showPopup } from '@hcengineering/ui'
|
||||
import { IntlString, getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import {
|
||||
IconSize,
|
||||
Scroller,
|
||||
showPopup,
|
||||
SelectPopup,
|
||||
AnySvelteComponent,
|
||||
getEventPositionElement
|
||||
} from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import textEditorPlugin from '../plugin'
|
||||
import EmojiPopup from './EmojiPopup.svelte'
|
||||
import Emoji from './icons/Emoji.svelte'
|
||||
import TextStyle from './icons/TextStyle.svelte'
|
||||
import TextEditor from './TextEditor.svelte'
|
||||
|
||||
import { Asset } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { FormatMode, FORMAT_MODES, RefInputAction, TextEditorHandler } from '../types'
|
||||
import { headingLevels, mInsertTable } from './extensions'
|
||||
import { Level } from '@tiptap/extension-heading'
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import Header from './icons/Header.svelte'
|
||||
import IconTable from './icons/IconTable.svelte'
|
||||
import Attach from './icons/Attach.svelte'
|
||||
import Bold from './icons/Bold.svelte'
|
||||
import Code from './icons/Code.svelte'
|
||||
import CodeBlock from './icons/CodeBlock.svelte'
|
||||
@@ -37,12 +47,20 @@
|
||||
import Strikethrough from './icons/Strikethrough.svelte'
|
||||
import LinkPopup from './LinkPopup.svelte'
|
||||
import StyleButton from './StyleButton.svelte'
|
||||
import AddRowBefore from './icons/table/AddRowBefore.svelte'
|
||||
import AddRowAfter from './icons/table/AddRowAfter.svelte'
|
||||
import AddColBefore from './icons/table/AddColBefore.svelte'
|
||||
import AddColAfter from './icons/table/AddColAfter.svelte'
|
||||
import DeleteRow from './icons/table/DeleteRow.svelte'
|
||||
import DeleteCol from './icons/table/DeleteCol.svelte'
|
||||
import DeleteTable from './icons/table/DeleteTable.svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let content: string = ''
|
||||
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
|
||||
export let showButtons: boolean = true
|
||||
export let showAttach: boolean = false
|
||||
export let buttonSize: IconSize = 'large'
|
||||
export let isScrollable: boolean = true
|
||||
export let focusable: boolean = false
|
||||
@@ -76,8 +94,19 @@
|
||||
icon: Asset | AnySvelteComponent
|
||||
action: RefInputAction
|
||||
order: number
|
||||
hidden?: boolean
|
||||
}
|
||||
const defActions: RefAction[] = [
|
||||
let defActions: RefAction[]
|
||||
$: defActions = [
|
||||
{
|
||||
label: textEditorPlugin.string.Attach,
|
||||
icon: Attach,
|
||||
action: () => {
|
||||
dispatch('attach')
|
||||
},
|
||||
order: 1000,
|
||||
hidden: showAttach
|
||||
},
|
||||
{
|
||||
label: textEditorPlugin.string.TextStyle,
|
||||
icon: TextStyle,
|
||||
@@ -115,8 +144,21 @@
|
||||
|
||||
function updateFormattingState () {
|
||||
activeModes = new Set(FORMAT_MODES.filter(textEditor.checkIsActive))
|
||||
for (const l of headingLevels) {
|
||||
if (textEditor.checkIsActive('heading', { level: l })) {
|
||||
headingLevel = l
|
||||
activeModes.add('heading')
|
||||
}
|
||||
}
|
||||
if (!activeModes.has('heading')) {
|
||||
headingLevel = 0
|
||||
}
|
||||
isSelectionEmpty = textEditor.checkIsSelectionEmpty()
|
||||
}
|
||||
// function updateFormattingState () {
|
||||
// activeModes = new Set(FORMAT_MODES.filter(textEditor.checkIsActive))
|
||||
// isSelectionEmpty = textEditor.checkIsSelectionEmpty()
|
||||
// }
|
||||
|
||||
function getToggler (toggle: () => void) {
|
||||
return () => {
|
||||
@@ -145,11 +187,172 @@
|
||||
function handleAction (a: RefAction, evt?: Event): void {
|
||||
a.action(evt?.target as HTMLElement, editorHandler)
|
||||
}
|
||||
|
||||
let needFocus = false
|
||||
const focused = false
|
||||
|
||||
$: if (textEditor && needFocus) {
|
||||
if (!focused) textEditor.focus()
|
||||
needFocus = false
|
||||
}
|
||||
|
||||
let headingLevel = 0
|
||||
|
||||
function toggleHeader (event: MouseEvent) {
|
||||
if (activeModes.has('heading')) {
|
||||
textEditor.toggleHeading({ level: headingLevel as Level })
|
||||
needFocus = true
|
||||
updateFormattingState()
|
||||
} else {
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{
|
||||
value: Array.from(headingLevels).map((it) => ({ id: it.toString(), text: it.toString() }))
|
||||
},
|
||||
getEventPositionElement(event),
|
||||
(val) => {
|
||||
if (val !== undefined) {
|
||||
textEditor.toggleHeading({ level: parseInt(val) as Level })
|
||||
needFocus = true
|
||||
updateFormattingState()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function insertTable (event: MouseEvent) {
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{
|
||||
value: [
|
||||
{ id: '#delete', label: presentation.string.Remove },
|
||||
...mInsertTable.map((it) => ({ id: it.label, text: it.label }))
|
||||
]
|
||||
},
|
||||
getEventPositionElement(event),
|
||||
(val) => {
|
||||
if (val !== undefined) {
|
||||
if (val === '#delete') {
|
||||
textEditor.deleteTable()
|
||||
needFocus = true
|
||||
updateFormattingState()
|
||||
return
|
||||
}
|
||||
const tab = mInsertTable.find((it) => it.label === val)
|
||||
if (tab) {
|
||||
textEditor.insertTable({
|
||||
cols: tab.cols,
|
||||
rows: tab.rows,
|
||||
withHeaderRow: tab.header
|
||||
})
|
||||
|
||||
needFocus = true
|
||||
updateFormattingState()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function tableOptions (event: MouseEvent) {
|
||||
const ops = [
|
||||
{
|
||||
id: '#addColumnBefore',
|
||||
icon: AddColBefore,
|
||||
label: textEditorPlugin.string.AddColumnBefore,
|
||||
action: () => textEditor.addColumnBefore(),
|
||||
category: {
|
||||
label: textEditorPlugin.string.CategoryColumn
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '#addColumnAfter',
|
||||
icon: AddColAfter,
|
||||
label: textEditorPlugin.string.AddColumnAfter,
|
||||
action: () => textEditor.addColumnAfter(),
|
||||
category: {
|
||||
label: textEditorPlugin.string.CategoryColumn
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
id: '#deleteColumn',
|
||||
icon: DeleteCol,
|
||||
label: textEditorPlugin.string.DeleteColumn,
|
||||
action: () => textEditor.deleteColumn(),
|
||||
category: {
|
||||
label: textEditorPlugin.string.CategoryColumn
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '#addRowBefore',
|
||||
icon: AddRowBefore,
|
||||
label: textEditorPlugin.string.AddRowBefore,
|
||||
action: () => textEditor.addRowBefore(),
|
||||
category: {
|
||||
label: textEditorPlugin.string.CategoryRow
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '#addRowAfter',
|
||||
icon: AddRowAfter,
|
||||
label: textEditorPlugin.string.AddRowAfter,
|
||||
action: () => textEditor.addRowAfter(),
|
||||
category: {
|
||||
label: textEditorPlugin.string.CategoryRow
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '#deleteRow',
|
||||
icon: DeleteRow,
|
||||
label: textEditorPlugin.string.DeleteRow,
|
||||
action: () => textEditor.deleteRow(),
|
||||
category: {
|
||||
label: textEditorPlugin.string.CategoryRow
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '#deleteTable',
|
||||
icon: DeleteTable,
|
||||
label: textEditorPlugin.string.DeleteTable,
|
||||
action: () => textEditor.deleteTable(),
|
||||
category: {
|
||||
label: textEditorPlugin.string.Table
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{
|
||||
value: ops
|
||||
},
|
||||
getEventPositionElement(event),
|
||||
(val) => {
|
||||
if (val !== undefined) {
|
||||
const op = ops.find((it) => it.id === val)
|
||||
if (op) {
|
||||
op.action()
|
||||
needFocus = true
|
||||
updateFormattingState()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="ref-container clear-mins">
|
||||
{#if isFormatting}
|
||||
<div class="formatPanel buttons-group xsmall-gap mb-4" class:withoutTopBorder>
|
||||
<StyleButton
|
||||
icon={Header}
|
||||
size={buttonSize}
|
||||
selected={activeModes.has('heading')}
|
||||
showTooltip={{ label: getEmbeddedLabel(`H${headingLevel}`) }}
|
||||
on:click={toggleHeader}
|
||||
/>
|
||||
<StyleButton
|
||||
icon={Bold}
|
||||
size={buttonSize}
|
||||
@@ -217,6 +420,23 @@
|
||||
showTooltip={{ label: textEditorPlugin.string.CodeBlock }}
|
||||
on:click={getToggler(textEditor.toggleCodeBlock)}
|
||||
/>
|
||||
<StyleButton
|
||||
icon={IconTable}
|
||||
iconProps={{ style: 'table' }}
|
||||
size={buttonSize}
|
||||
selected={activeModes.has('table')}
|
||||
on:click={insertTable}
|
||||
showTooltip={{ label: textEditorPlugin.string.InsertTable }}
|
||||
/>
|
||||
{#if activeModes.has('table')}
|
||||
<StyleButton
|
||||
icon={IconTable}
|
||||
iconProps={{ style: 'tableProps' }}
|
||||
size={buttonSize}
|
||||
on:click={tableOptions}
|
||||
showTooltip={{ label: textEditorPlugin.string.TableOptions }}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="textInput" class:focusable>
|
||||
@@ -259,8 +479,8 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if showButtons}
|
||||
<div class="buttons">
|
||||
{#each defActions as a}
|
||||
<div class="buttons-group xsmall-gap my-4">
|
||||
{#each defActions.filter((it) => it.hidden === undefined || it.hidden === true) as a}
|
||||
<StyleButton icon={a.icon} size={buttonSize} on:click={(evt) => handleAction(a, evt)} />
|
||||
{/each}
|
||||
<div class="flex-grow">
|
||||
@@ -315,9 +535,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
import textEditorPlugin from '../plugin'
|
||||
import { FormatMode } from '../types'
|
||||
import { defaultExtensions } from './extensions'
|
||||
import { Level } from '@tiptap/extension-heading'
|
||||
|
||||
export let content: string = ''
|
||||
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
|
||||
@@ -55,8 +56,8 @@
|
||||
export function insertText (text: string): void {
|
||||
editor.commands.insertContent(text as HTMLContent)
|
||||
}
|
||||
export function checkIsActive (formatMode: FormatMode) {
|
||||
return editor.isActive(formatMode)
|
||||
export function checkIsActive (formatMode: FormatMode, attributes?: {} | undefined) {
|
||||
return editor.isActive(formatMode, attributes)
|
||||
}
|
||||
export function toggleBold () {
|
||||
editor.commands.toggleBold()
|
||||
@@ -94,9 +95,41 @@
|
||||
export function toggleCodeBlock () {
|
||||
editor.commands.toggleCodeBlock()
|
||||
}
|
||||
let needFocus = false
|
||||
export function toggleHeading (attributes: { level: Level }) {
|
||||
editor.commands.toggleHeading(attributes)
|
||||
}
|
||||
export function addColumnBefore () {
|
||||
editor.commands.addColumnBefore()
|
||||
}
|
||||
export function addColumnAfter () {
|
||||
editor.commands.addColumnAfter()
|
||||
}
|
||||
export function deleteColumn () {
|
||||
editor.commands.deleteColumn()
|
||||
}
|
||||
export function addRowBefore () {
|
||||
editor.commands.addRowBefore()
|
||||
}
|
||||
export function addRowAfter () {
|
||||
editor.commands.addRowAfter()
|
||||
}
|
||||
export function deleteRow () {
|
||||
editor.commands.deleteRow()
|
||||
}
|
||||
export function deleteTable () {
|
||||
editor.commands.deleteTable()
|
||||
}
|
||||
export function insertTable (options?: { rows?: number; cols?: number; withHeaderRow?: boolean }) {
|
||||
editor.commands.insertTable({
|
||||
cols: options?.cols ?? 2,
|
||||
rows: options?.rows ?? 1,
|
||||
withHeaderRow: options?.withHeaderRow
|
||||
})
|
||||
}
|
||||
|
||||
let needFocus = false
|
||||
let focused = false
|
||||
|
||||
export function focus (): void {
|
||||
needFocus = true
|
||||
}
|
||||
@@ -164,6 +197,7 @@
|
||||
content = editor.getHTML()
|
||||
isEmpty = editor.isEmpty
|
||||
dispatch('value', content)
|
||||
dispatch('update', content)
|
||||
},
|
||||
onSelectionUpdate: () => dispatch('selection-update')
|
||||
})
|
||||
|
||||
@@ -60,3 +60,66 @@ export const defaultExtensions = [
|
||||
...tableExtensions,
|
||||
...taskListExtensions
|
||||
]
|
||||
|
||||
export const mInsertTable = [
|
||||
{
|
||||
label: '2x2',
|
||||
rows: 2,
|
||||
cols: 2,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '3x3',
|
||||
rows: 3,
|
||||
cols: 3,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '2x1',
|
||||
rows: 2,
|
||||
cols: 1,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '5x5',
|
||||
rows: 5,
|
||||
cols: 5,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: '1x2',
|
||||
rows: 1,
|
||||
cols: 2,
|
||||
header: false
|
||||
},
|
||||
{
|
||||
label: 'Headed 2x2',
|
||||
rows: 2,
|
||||
cols: 2,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 3x3',
|
||||
rows: 3,
|
||||
cols: 3,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 2x1',
|
||||
rows: 2,
|
||||
cols: 1,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 5x5',
|
||||
rows: 5,
|
||||
cols: 5,
|
||||
header: true
|
||||
},
|
||||
{
|
||||
label: 'Headed 1x2',
|
||||
rows: 1,
|
||||
cols: 2,
|
||||
header: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -404,6 +404,7 @@ input.search {
|
||||
.mx-3 { margin: 0 .75rem; }
|
||||
.mx-10 { margin: 0 2.5rem; }
|
||||
.mx-auto { margin: 0 auto; }
|
||||
.my-2 { margin: .5rem 0; }
|
||||
.my-4 { margin: 1rem 0; }
|
||||
|
||||
.pl-1 { padding-left: .25rem; }
|
||||
|
||||
@@ -24,6 +24,7 @@ table.proseTable {
|
||||
td,
|
||||
th {
|
||||
min-width: 1rem;
|
||||
height: 2rem;
|
||||
border: 1px solid var(--button-border-hover);
|
||||
padding: .25rem .5rem;
|
||||
vertical-align: top;
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
export let id: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
/** eslint-disable a11y-click-events-have-key-events */
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
{id}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
export let showButtons = false
|
||||
export let buttonSize: IconSize = 'small'
|
||||
export let maxHeight: 'max' | 'card' | 'limited' | string = 'max'
|
||||
export let focusable: boolean = false
|
||||
|
||||
export function attach (): void {
|
||||
inputFile.click()
|
||||
@@ -199,12 +200,15 @@
|
||||
{placeholder}
|
||||
{alwaysEdit}
|
||||
{showButtons}
|
||||
showAttach
|
||||
{buttonSize}
|
||||
{maxHeight}
|
||||
{focusable}
|
||||
on:changeSize
|
||||
on:attach={() => attach()}
|
||||
/>
|
||||
{#if attachments.size}
|
||||
<div class="flex-row-center list scroll-divider-color mt-1">
|
||||
<div class="flex-row-center list scroll-divider-color">
|
||||
{#each Array.from(attachments.values()) as attachment}
|
||||
<div class="item flex">
|
||||
<AttachmentPresenter
|
||||
@@ -223,6 +227,7 @@
|
||||
<style lang="scss">
|
||||
.list {
|
||||
padding: 0.5rem;
|
||||
min-width: 0;
|
||||
color: var(--theme-caption-color);
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
Button,
|
||||
EditBox,
|
||||
getCurrentLocation,
|
||||
IconAttachment,
|
||||
IconEdit,
|
||||
IconMoreH,
|
||||
Label,
|
||||
@@ -296,30 +295,19 @@
|
||||
</div>
|
||||
{/if}
|
||||
<EditBox bind:value={title} placeholder={tracker.string.IssueTitlePlaceholder} kind="large-style" />
|
||||
<div class="flex-between mt-6">
|
||||
{#key description}
|
||||
<div class="flex-grow">
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
objectId={_id}
|
||||
_class={tracker.class.Issue}
|
||||
space={issue.space}
|
||||
alwaysEdit
|
||||
showButtons
|
||||
maxHeight={'card'}
|
||||
bind:content={description}
|
||||
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
{/key}
|
||||
<div
|
||||
class="tool"
|
||||
on:click={() => {
|
||||
descriptionBox.attach()
|
||||
}}
|
||||
>
|
||||
<IconAttachment size={'large'} />
|
||||
</div>
|
||||
<div class="w-full mt-6">
|
||||
<AttachmentStyledBox
|
||||
bind:this={descriptionBox}
|
||||
objectId={_id}
|
||||
_class={tracker.class.Issue}
|
||||
space={issue.space}
|
||||
alwaysEdit
|
||||
showButtons
|
||||
maxHeight={'card'}
|
||||
focusable
|
||||
bind:content={description}
|
||||
placeholder={tracker.string.IssueDescriptionPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Scroller>
|
||||
@@ -422,15 +410,4 @@
|
||||
height: 1px;
|
||||
background-color: var(--divider-color);
|
||||
}
|
||||
|
||||
.tool {
|
||||
align-self: start;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0.3;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user