Fix AccordionEditor and UI (#2436)

This commit is contained in:
Alexander Platov 2022-12-14 07:53:37 +03:00 committed by GitHub
parent 2a4661748d
commit 12c244ddf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 122 additions and 65 deletions

View File

@ -150,11 +150,13 @@
{getName(selected)}
{/if}
{:else}
<div class="flex-row-center">
<div class="flex-presenter">
{#if icon}
<Icon {icon} size={kind === 'link' ? 'small' : size} />
<div class="icon" class:small-gap={size === 'inline' || size === 'small'}>
<Icon {icon} size={kind === 'link' ? 'small' : size} />
</div>
{/if}
<div class="ml-2">
<div class="label no-underline">
<Label {label} />
</div>
</div>

View File

@ -25,9 +25,10 @@
export let icon: Asset | AnySvelteComponent | undefined = undefined
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="flex-row-center" on:click>
<Avatar avatar={value.avatar} {size} {icon} />
<div class="flex-col ml-2 min-w-0">
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}">
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
<div class="content-accent-color overflow-label text-left">{formatName(value.name)}</div>
</div>

View File

@ -22,7 +22,7 @@
export let emphasized: boolean = false
export let alwaysEdit: boolean = false
export let showButtons: boolean = true
export let showAttach: boolean = false
export let hideAttachments: boolean = false
export let buttonSize: IconSize = 'small'
export let hideExtraButtons: boolean = false
export let maxHeight: 'max' | 'card' | 'limited' | string = 'max'
@ -113,7 +113,7 @@
<StyledTextEditor
{placeholder}
{showButtons}
{showAttach}
{hideAttachments}
{buttonSize}
{maxHeight}
{focusable}
@ -126,6 +126,7 @@
}}
on:blur={() => {
focused = false
dispatch('blur', rawValue)
if (alwaysEdit) {
dispatch('value', rawValue)
content = rawValue

View File

@ -60,7 +60,7 @@
export let content: string = ''
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
export let showButtons: boolean = true
export let showAttach: boolean = false
export let hideAttachments: boolean = false
export let buttonSize: IconSize = 'large'
export let isScrollable: boolean = true
export let focusable: boolean = false
@ -118,7 +118,7 @@
dispatch('attach')
},
order: 1000,
hidden: showAttach
hidden: hideAttachments
},
{
label: textEditorPlugin.string.TextStyle,
@ -496,7 +496,7 @@
{#if $$slots.right}
<div class="flex-between">
<div class="buttons-group xsmall-gap mt-4">
{#each defActions.filter((it) => it.hidden === undefined || it.hidden === true) as a}
{#each defActions.filter((it) => it.hidden === undefined || it.hidden === false) as a}
<StyleButton icon={a.icon} size={buttonSize} on:click={(evt) => handleAction(a, evt)} />
{/each}
<slot />
@ -507,7 +507,7 @@
</div>
{:else}
<div class="buttons-group xsmall-gap mt-4">
{#each defActions.filter((it) => it.hidden === undefined || it.hidden === true) as a}
{#each defActions.filter((it) => it.hidden === undefined || it.hidden === false) as a}
<StyleButton icon={a.icon} size={buttonSize} on:click={(evt) => handleAction(a, evt)} />
{/each}
<slot />

View File

@ -241,7 +241,6 @@ input.search {
cursor: pointer;
.icon {
margin-right: .5rem;
color: var(--dark-color);
&.circle {
@ -249,6 +248,8 @@ input.search {
background-color: var(--avatar-bg-color);
border-radius: 50%;
}
&:not(.small-gap) { margin-right: .5rem; }
&.small-gap { margin-right: .25rem; }
}
.label {
min-width: 0;
@ -275,10 +276,11 @@ input.search {
margin-left: .75rem;
}
&:hover {
.icon { color: var(--theme-caption-color); }
.icon { color: var(--caption-color); }
.label {
text-decoration: underline;
color: var(--theme-caption-color);
color: var(--caption-color);
&:not(.no-underline) { text-decoration: underline; }
}
.action { visibility: visible; }
}
@ -356,17 +358,17 @@ input.search {
flex-wrap: nowrap;
white-space: nowrap;
width: fit-content;
color: var(--theme-caption-color);
color: var(--caption-color);
cursor: pointer;
.icon {
margin-right: .25rem;
color: var(--theme-content-dark-color);
color: var(--dark-color);
&.small-size {
width: 1.5rem;
height: 1.5rem;
}
}
&:hover .icon { color: var(--theme-caption-color); }
&:hover .icon { color: var(--caption-color); }
}
/* Margins & Paddings */

View File

@ -414,6 +414,9 @@
max-height: 1.5rem;
font-weight: 400;
transition: opacity .15s var(--timing-main);
&::-webkit-scrollbar:vertical { width: 0; }
&::-webkit-scrollbar:horizontal { height: 0; }
}
.rotated-icon {
transform-origin: center;
@ -426,6 +429,7 @@
&.opened {
.caption .value { opacity: 0; }
.expand-collapse .expand-collapse,
.expand-collapse {
visibility: visible;
max-height: max-content;
@ -436,13 +440,19 @@
margin-bottom: -.5rem;
.value { opacity: 1; }
&.hasAttachments { margin-bottom: 0; }
}
.expand-collapse {
.expand-collapse .expand-collapse,
.expand-collapse:not(.hasAttachments) {
overflow: hidden;
visibility: hidden;
max-height: 0;
}
&:hover .caption { margin-bottom: 0rem; }
&:hover .caption {
margin-bottom: 0rem;
&.hasAttachments { margin-bottom: .5rem; }
}
}
&:first-child {

View File

@ -214,8 +214,8 @@
height: 100%;
min-width: 0;
background-color: var(--board-card-bg-color);
border: 1px solid var(--divider-color);
border-top: 1px solid var(--board-card-bg-color);
border-left: 1px solid var(--divider-color);
border-bottom-right-radius: .45rem;
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
transition: box-shadow 150ms ease 0s, transform 150ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
transform: translateX(0);
@ -241,10 +241,10 @@
}
}
&.asideShown .popupPanel-body__main {
border: 1px solid var(--divider-color);
border-radius: 0 0 .5rem .5rem;
}
// &.asideShown .popupPanel-body__main {
// border: 1px solid var(--divider-color);
// border-radius: 0 0 .5rem .5rem;
// }
}
}

View File

@ -76,7 +76,7 @@
}
}}
>
<span slot="content" class="overflow-label disabled" class:dark-color={selectedItem === undefined}>
<span slot="content" class="overflow-label disabled" class:content-accent-color={selectedItem === undefined}>
{#if selectedItem}{selectedItem.label}{:else}<Label label={label ?? ui.string.NotSelected} />{/if}
</span>
</Button>

View File

@ -16,11 +16,12 @@
export let column: number = 2
export let rowGap: number = 2.5
export let columnGap: number = 1.5
export let topGap: boolean = false
$: style = `grid-template-columns: repeat(${column}, 1fr); row-gap: ${rowGap}rem; column-gap: ${columnGap}rem;`
</script>
<div class="grid" {style}>
<div class="grid" {style} style:margin-top={topGap ? `${rowGap}rem` : 0}>
<slot />
</div>

View File

@ -21,18 +21,20 @@
import AttachmentStyledBox from './AttachmentStyledBox.svelte'
export let items: AccordionItem[]
export let objectId: Ref<Doc>
export let space: Ref<Space>
export let _class: Ref<Class<Doc>>
export let objectId: Ref<Doc> | undefined = undefined
export let space: Ref<Space> | undefined = undefined
export let _class: Ref<Class<Doc>> | undefined = undefined
export let withoutAttach: boolean = false
export function createAttachments (): void {
attachments.forEach((at) => at.createAttachments())
attachments[attachments.length - 1].createAttachments()
}
const dispatch = createEventDispatcher()
const attachments: AttachmentStyledBox[] = []
const edits: TextEditor[] = []
let hasAttachments: boolean = false
const flip = (index: number, ev?: MouseEvent): void => {
ev?.stopPropagation()
@ -41,7 +43,7 @@
case 'opened':
attachments[index].setEditable(false)
items[index].state = 'closed'
setTimeout(() => edits[index].focus('end'), 0)
setTimeout(() => edits[index].focus(), 0)
break
case 'closed':
items[index].state = 'opened'
@ -59,6 +61,7 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="caption"
class:hasAttachments={hasAttachments && i === items.length - 1}
use:tooltip={{ label: item.tooltip }}
tabindex="-1"
on:click={() => {
@ -80,6 +83,7 @@
dispatch('update', { item, value: ev.detail })
flip(i)
}}
on:blur={() => dispatch('blur', item)}
/>
{/if}
</div>
@ -91,17 +95,26 @@
</svelte:fragment>
</Button>
</div>
<div class="expand-collapse">
<div class="expand-collapse" class:hasAttachments={hasAttachments && i === items.length - 1}>
<AttachmentStyledBox
bind:this={attachments[i]}
alwaysEdit
showButtons
fakeAttach={withoutAttach ? 'hidden' : i < items.length - 1 ? 'fake' : 'normal'}
bind:content={item.content}
placeholder={textEditorPlugin.string.EditorPlaceholder}
{objectId}
{_class}
{space}
on:changeContent={(ev) => dispatch('update', { item, value: ev.detail })}
on:attach={(ev) => {
if (ev && ev.detail.action === 'drop') attachments[attachments.length - 1].fileDrop(ev.detail.event)
else if (ev.detail.action === 'add') attachments[attachments.length - 1].attach()
else if (ev.detail.action === 'saved') {
if (ev.detail.value !== hasAttachments) hasAttachments = ev.detail.value
}
}}
on:blur={() => dispatch('blur', item)}
/>
</div>
</div>

View File

@ -19,14 +19,14 @@
import { createQuery, getClient } from '@hcengineering/presentation'
import { StyledTextBox } from '@hcengineering/text-editor'
import { IconSize } from '@hcengineering/ui'
import { onDestroy } from 'svelte'
import { createEventDispatcher, onDestroy } from 'svelte'
import attachment from '../plugin'
import { deleteFile, uploadFile } from '../utils'
import AttachmentPresenter from './AttachmentPresenter.svelte'
export let objectId: Ref<Doc>
export let space: Ref<Space>
export let _class: Ref<Class<Doc>>
export let objectId: Ref<Doc> | undefined = undefined
export let space: Ref<Space> | undefined = undefined
export let _class: Ref<Class<Doc>> | undefined = undefined
export let content: string = ''
export let placeholder: IntlString | undefined = undefined
export let alwaysEdit = false
@ -35,8 +35,11 @@
export let buttonSize: IconSize = 'small'
export let maxHeight: 'max' | 'card' | 'limited' | string = 'max'
export let focusable: boolean = false
export let fakeAttach: 'fake' | 'hidden' | 'normal' = 'normal'
export let refContainer: HTMLElement | undefined = undefined
const dispatch = createEventDispatcher()
export function focus (): void {
refInput.focus()
}
@ -81,6 +84,7 @@
)
async function createAttachment (file: File) {
if (space === undefined || objectId === undefined || _class === undefined) return
try {
const uuid = await uploadFile(file, { space, attachedTo: objectId })
const _id: Ref<Attachment> = generateId()
@ -107,6 +111,7 @@
}
async function saveAttachment (doc: Attachment): Promise<void> {
if (space === undefined || objectId === undefined || _class === undefined) return
await client.addCollection(attachment.class.Attachment, space, objectId, _class, 'attachments', doc, doc._id)
}
@ -120,12 +125,13 @@
inputFile.value = ''
}
function fileDrop (e: DragEvent) {
export function fileDrop (e: DragEvent) {
const list = e.dataTransfer?.files
if (list === undefined || list.length === 0) return
for (let index = 0; index < list.length; index++) {
const file = list.item(index)
if (file !== null) createAttachment(file)
if (list !== undefined && list.length !== 0) {
for (let index = 0; index < list.length; index++) {
const file = list.item(index)
if (file !== null) createAttachment(file)
}
}
}
@ -176,6 +182,10 @@
return Promise.all(promises).then()
}
$: if (attachments.size || newAttachments.size || removedAttachments.size) {
dispatch('attach', { action: 'saved', value: attachments.size })
}
function isAllowedPaste (evt: ClipboardEvent) {
let t: HTMLElement | null = evt.target as HTMLElement
@ -193,7 +203,7 @@
return false
}
function pasteAction (evt: ClipboardEvent): void {
export function pasteAction (evt: ClipboardEvent): void {
if (!isAllowedPaste(evt)) {
return
}
@ -211,7 +221,7 @@
}
</script>
<svelte:window on:paste={pasteAction} />
<svelte:window on:paste={(ev) => (fakeAttach === 'normal' ? pasteAction(ev) : undefined)} />
<input
bind:this={inputFile}
@ -227,24 +237,33 @@
class="flex-col clear-mins"
on:dragover|preventDefault={() => {}}
on:dragleave={() => {}}
on:drop|preventDefault|stopPropagation={fileDrop}
on:drop|preventDefault|stopPropagation={(ev) => {
if (fakeAttach === 'fake') dispatch('attach', { action: 'drop', event: ev })
else if (fakeAttach === 'normal') fileDrop(ev)
}}
>
<StyledTextBox
bind:this={refInput}
bind:content
{placeholder}
{alwaysEdit}
{showButtons}
showAttach
{buttonSize}
{maxHeight}
{focusable}
{emphasized}
on:changeSize
on:changeContent
on:attach={() => attach()}
/>
{#if attachments.size}
<div class="expand-collapse">
<StyledTextBox
bind:this={refInput}
bind:content
{placeholder}
{alwaysEdit}
{showButtons}
hideAttachments={fakeAttach === 'hidden'}
{buttonSize}
{maxHeight}
{focusable}
{emphasized}
on:changeSize
on:changeContent
on:blur
on:attach={() => {
if (fakeAttach === 'fake') dispatch('attach', { action: 'add' })
else if (fakeAttach === 'normal') attach()
}}
/>
</div>
{#if attachments.size && fakeAttach === 'normal'}
<div class="flex-row-center list scroll-divider-color">
{#each Array.from(attachments.values()) as attachment}
<div class="item flex">

View File

@ -87,10 +87,12 @@
}
)
}
$: smallgap = size === 'inline' || size === 'small'
</script>
{#if value && statuses}
{#if kind === 'list' || kind === 'list-header'}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="flex-row-center flex-no-shrink" class:cursor-pointer={isEditable} on:click={handleStatusEditorOpened}>
<div class="flex-center flex-no-shrink square-4">
{#if selectedStatus}<IssueStatusIcon value={selectedStatus} size={kind === 'list' ? 'inline' : 'medium'} />{/if}
@ -98,8 +100,8 @@
{#if selectedStatusLabel}
<span
class="{kind === 'list'
? 'ml-2 text-md'
: 'ml-3 text-base'} overflow-label disabled fs-bold content-accent-color"
? 'ml-1 text-md'
: 'ml-2 text-base'} overflow-label disabled fs-bold content-accent-color"
>
{selectedStatusLabel}
</span>
@ -115,12 +117,18 @@
{width}
on:click={handleStatusEditorOpened}
>
<span slot="content" class="inline-flex pointer-events-none">
<span slot="content" class="flex-row-center pointer-events-none">
{#if selectedStatus}
<IssueStatusIcon value={selectedStatus} size="inline" />
{/if}
{#if selectedStatusLabel}
<span class="overflow-label disabled" class:ml-2={selectedStatus}>{selectedStatusLabel}</span>
<span
class="overflow-label disabled"
class:ml-1={selectedStatus && smallgap}
class:ml-2={selectedStatus && !smallgap}
>
{selectedStatusLabel}
</span>
{/if}
</span>
</Button>