Adjust reference input styles (#3816)

Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
Anna No 2023-10-10 23:34:39 +07:00 committed by GitHub
parent 4c80eaf569
commit 84d81417fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 206 additions and 245 deletions

View File

@ -17,14 +17,13 @@
import { getClient } from '@hcengineering/presentation'
import {
AnySvelteComponent,
Button,
ButtonKind,
EmojiPopup,
Icon,
IconEmoji,
Spinner,
handler,
registerFocus,
showPopup,
tooltip,
deviceOptionsStore as deviceInfo,
checkAdaptiveMatching
} from '@hcengineering/ui'
@ -40,18 +39,22 @@
import RIMention from './icons/RIMention.svelte'
import Send from './icons/Send.svelte'
const dispatch = createEventDispatcher()
export let content: string = ''
export let showHeader = false
export let showActions = true
export let showSend = true
export let iconSend: Asset | AnySvelteComponent | undefined = undefined
export let labelSend: IntlString | undefined = undefined
export let kindSend: ButtonKind = 'ghost'
export let haveAttachment = false
export let withoutTopBorder = false
export let placeholder: IntlString | undefined = undefined
export let extraActions: RefAction[] | undefined = undefined
export let loading: boolean = false
export let focusable: boolean = false
const client = getClient()
const dispatch = createEventDispatcher()
const buttonSize = 'medium'
let textEditor: TextEditor
@ -156,195 +159,131 @@
})
</script>
<div class="ref-container">
<div class="textInput" class:withoutTopBorder>
<div class="inputMsg">
<TextEditor
bind:content
bind:this={textEditor}
on:content={(ev) => {
if (!isEmpty || haveAttachment) {
dispatch('message', ev.detail)
content = ''
textEditor.clear()
}
}}
on:blur={() => {
focused = false
dispatch('blur', focused)
}}
on:focus={() => {
focused = true
updateFocus()
dispatch('focus', focused)
}}
extensions={[
completionPlugin,
EmojiExtension.configure(),
IsEmptyContentExtension.configure({ onChange: (value) => (isEmpty = value) })
]}
on:update
placeholder={placeholder ?? textEditorPlugin.string.EditorPlaceholder}
textFormatCategories={[
TextFormatCategory.TextDecoration,
TextFormatCategory.Link,
TextFormatCategory.List,
TextFormatCategory.Quote,
TextFormatCategory.Code
]}
/>
<div class="ref-container" class:focusable>
{#if showHeader && $$slots.header}
<div class="header">
<slot name="header" />
</div>
{#if showSend}
<button
class="sendButton"
on:click={submit}
use:tooltip={{ label: labelSend ?? textEditorPlugin.string.Send }}
disabled={(isEmpty && !haveAttachment) || loading}
>
<div class="icon">
{#if loading}
<div class="pointer-events-none spinner">
<Spinner size={'medium'} />
</div>
{:else}
<Icon icon={iconSend ?? Send} size={'medium'} />
{/if}
</div>
</button>
{/if}
{/if}
<div class="text-input">
<TextEditor
bind:content
bind:this={textEditor}
on:content={(ev) => {
if (!isEmpty || haveAttachment) {
dispatch('message', ev.detail)
content = ''
textEditor.clear()
}
}}
on:blur={() => {
focused = false
dispatch('blur')
}}
on:focus={() => {
focused = true
updateFocus()
dispatch('focus')
}}
extensions={[
completionPlugin,
EmojiExtension.configure(),
IsEmptyContentExtension.configure({ onChange: (value) => (isEmpty = value) })
]}
on:update
placeholder={placeholder ?? textEditorPlugin.string.EditorPlaceholder}
textFormatCategories={[
TextFormatCategory.TextDecoration,
TextFormatCategory.Link,
TextFormatCategory.List,
TextFormatCategory.Quote,
TextFormatCategory.Code
]}
/>
</div>
<div class="flex-between clear-mins" style:margin={'.75rem .75rem 0'}>
<div class="buttons-group medium-gap">
{#each actions as a}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="icon-button"
use:tooltip={{ label: a.label }}
on:click={handler(a, (a, evt) => handleAction(a, evt))}
>
<Icon icon={a.icon} size={'medium'} />
{#if showActions || showSend}
<div class="buttons-panel flex-between clear-mins">
{#if showActions}
<div class="buttons-group xsmall-gap">
{#each actions as a}
<Button
icon={a.icon}
iconProps={{ size: buttonSize }}
kind="ghost"
showTooltip={{ label: a.label }}
size={buttonSize}
on:click={handler(a, (a, evt) => handleAction(a, evt))}
/>
{#if a.order % 10 === 1}
<div class="buttons-divider" />
{/if}
{/each}
</div>
{#if a.order % 10 === 1}
<div class="buttons-divider" />
{/if}
{/each}
</div>
{#if extraActions && extraActions.length > 0}
<div class="buttons-group {shrinkButtons ? 'medium-gap' : 'large-gap'}">
{#each extraActions as a}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="icon-button"
class:disabled={a.disabled}
use:tooltip={{ label: a.label }}
on:click={handler(a, (a, evt) => {
if (!a.disabled) {
handleAction(a, evt)
}
})}
>
<Icon icon={a.icon} size={'medium'} fill={a.fill} />
{#if extraActions && extraActions.length > 0}
<div class="buttons-group {shrinkButtons ? 'xsmall-gap' : 'small-gap'}">
{#each extraActions as a}
<Button
disabled={a.disabled}
icon={a.icon}
iconProps={{ size: buttonSize }}
kind="ghost"
showTooltip={{ label: a.label }}
size={buttonSize}
on:click={handler(a, (a, evt) => {
if (!a.disabled) {
handleAction(a, evt)
}
})}
/>
{/each}
</div>
{/each}
</div>
{/if}
</div>
{/if}
{/if}
{#if showSend}
<Button
{loading}
disabled={(isEmpty && !haveAttachment) || loading}
icon={iconSend ?? Send}
iconProps={{ size: buttonSize }}
kind={kindSend}
size={buttonSize}
showTooltip={{
label: labelSend ?? textEditorPlugin.string.Send
}}
on:click={submit}
/>
{/if}
</div>
{/if}
</div>
<style lang="scss">
.icon-button {
display: flex;
justify-content: center;
align-items: center;
width: 1.25rem;
height: 1.25rem;
color: var(--theme-darker-color);
cursor: pointer;
&:hover {
color: var(--theme-content-color);
}
&.disabled {
color: var(--theme-trans-color);
&:hover {
color: var(--theme-trans-color);
cursor: not-allowed;
}
}
}
.ref-container {
display: flex;
flex-direction: column;
min-height: 4.5rem;
border: 0.0625rem solid var(--theme-refinput-border);
border-radius: 0.375rem;
.textInput {
display: flex;
justify-content: space-between;
align-items: flex-end;
min-height: 2.75rem;
padding: 0.75rem 1rem;
background-color: var(--theme-refinput-color);
border: 1px solid var(--theme-refinput-border);
border-radius: 0.25rem;
&.withoutTopBorder {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.inputMsg {
display: flex;
align-self: center;
align-items: center;
width: calc(100% - 1.75rem);
height: 100%;
color: var(--theme-content-color);
background-color: transparent;
border: none;
outline: none;
}
.sendButton {
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
margin-left: 0.5rem;
padding: 0;
width: 1.25rem;
height: 1.25rem;
background-color: transparent;
border: 1px solid transparent;
border-radius: 0.25rem;
outline: none;
cursor: pointer;
.icon {
width: 1.25rem;
height: 1.25rem;
color: var(--theme-content-color);
cursor: pointer;
&:hover {
color: var(--theme-caption-color);
}
}
&:focus {
box-shadow: 0 0 0 2px var(--accented-button-outline);
& > .icon {
color: var(--theme-caption-color);
}
}
&:disabled {
pointer-events: none;
.icon {
color: var(--theme-trans-color);
cursor: not-allowed;
}
}
&.focusable {
&:focus-within {
border-color: var(--primary-edit-border-color);
}
}
}
.header {
padding: 0.325rem 0.75rem;
border-bottom: 0.0625rem solid var(--theme-refinput-border);
}
.text-input {
min-height: 2.75rem;
padding: 0.625rem 0.75rem;
}
.buttons-panel {
padding: 0.325rem 0.75rem;
}
</style>

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { IntlString } from '@hcengineering/platform'
import { IconSize, Label } from '@hcengineering/ui'
import { ButtonSize, Label } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import textEditorPlugin from '../plugin'
import StyledTextEditor from './StyledTextEditor.svelte'
@ -10,7 +10,7 @@
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
export let showButtons = true
export let buttonSize: IconSize = 'small'
export let buttonSize: ButtonSize = 'small'
export let focus = false
export let kind: 'normal' | 'emphasized' | 'indented' = 'normal'
export let isScrollable: boolean = false

View File

@ -3,6 +3,7 @@
import presentation, { MessageViewer } from '@hcengineering/presentation'
import {
ActionIcon,
ButtonSize,
IconCheck,
IconClose,
IconEdit,
@ -33,7 +34,7 @@
export let alwaysEdit: boolean = false
export let showButtons: boolean = true
export let hideAttachments: boolean = false
export let buttonSize: IconSize = 'medium'
export let buttonSize: ButtonSize = 'medium'
export let formatButtonSize: IconSize = 'small'
export let hideExtraButtons: boolean = false
export let maxHeight: 'max' | 'card' | 'limited' | string = 'max'

View File

@ -13,17 +13,26 @@
// limitations under the License.
-->
<script lang="ts">
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 { getClient } from '@hcengineering/presentation'
import { AnySvelteComponent, EmojiPopup, IconEmoji, IconSize, Scroller, showPopup } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import {
AnySvelteComponent,
Button,
ButtonSize,
EmojiPopup,
IconEmoji,
IconSize,
Scroller,
showPopup
} from '@hcengineering/ui'
import textEditorPlugin from '../plugin'
import { RefInputAction, RefInputActionItem, TextEditorHandler, TextFormatCategory } from '../types'
import Attach from './icons/Attach.svelte'
import { AnyExtension, mergeAttributes } from '@tiptap/core'
import StyleButton from './StyleButton.svelte'
import TextEditor from './TextEditor.svelte'
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
const dispatch = createEventDispatcher()
@ -31,7 +40,7 @@
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
export let showButtons: boolean = true
export let hideAttachments: boolean = false
export let buttonSize: IconSize = 'medium'
export let buttonSize: ButtonSize = 'medium'
export let formatButtonSize: IconSize = 'small'
export let isScrollable: boolean = true
export let focusable: boolean = false
@ -173,7 +182,7 @@
const buttonsGap = 'small-gap'
$: buttonsHeight =
buttonSize === 'large' || buttonSize === 'x-large' || buttonSize === 'full'
buttonSize === 'large' || buttonSize === 'x-large'
? 'h-6 max-h-6'
: buttonSize === 'medium'
? 'h-5 max-h-5'
@ -191,10 +200,11 @@
<div
class="ref-container clear-mins"
class:h-full={full}
class:focusable
tabindex="-1"
on:click|preventDefault|stopPropagation={() => (needFocus = true)}
>
<div class="textInput" class:focusable>
<div class="textInput">
<div
bind:clientHeight={contentHeight}
class="inputMsg"
@ -247,7 +257,14 @@
<div class="flex-between">
<div class="buttons-group {buttonsGap} mt-3">
{#each actions.filter((it) => it.hidden !== true) as a}
<StyleButton icon={a.icon} size={buttonSize} on:click={(evt) => handleAction(a, evt)} />
<Button
icon={a.icon}
iconProps={{ size: buttonSize }}
kind="ghost"
showTooltip={{ label: a.label }}
size={buttonSize}
on:click={(evt) => handleAction(a, evt)}
/>
{#if a.order % 10 === 1}
<div class="buttons-divider {buttonsHeight}" />
{/if}
@ -268,7 +285,6 @@
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: 1.25rem;
.textInput {
flex-grow: 1;
@ -282,7 +298,6 @@
align-self: stretch;
width: 100%;
min-height: 0;
color: var(--theme-caption-color);
background-color: transparent;
&.scrollable {
@ -300,16 +315,16 @@
}
}
}
}
&.focusable {
margin: -0.25rem -0.5rem;
padding: 0.25rem 0.5rem;
border: 1px solid transparent;
border-radius: 0.25rem;
&.focusable {
border: 0.0625rem solid transparent;
border-radius: 0.375rem;
margin: -0.25rem -0.5rem;
padding: 0.25rem 0.5rem;
&:focus-within {
border-color: var(--primary-edit-border-color);
}
&:focus-within {
border-color: var(--primary-edit-border-color);
}
}
}

View File

@ -3,8 +3,12 @@
const fill: string = 'currentColor'
</script>
<svg class="svg-{size}" {fill} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path
d="M18,2.8C18,2.8,18,2.8,18,2.8c0-0.1-0.1-0.2-0.1-0.3c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c-0.1,0-0.1-0.1-0.2-0.1c0,0,0,0-0.1,0c-0.1,0-0.1,0-0.2,0c0,0,0,0,0,0l-14.8,5C2.4,7.5,2.2,7.7,2.2,7.9c0,0.2,0.1,0.4,0.3,0.5l6.3,3.4l4.6,6.1c0.1,0.1,0.2,0.2,0.4,0.2c0,0,0.1,0,0.1,0c0.2,0,0.3-0.2,0.4-0.4L18,3c0,0,0,0,0,0C18,2.9,18,2.9,18,2.8z M15.6,4.1L9,10.7L4,8L15.6,4.1z M13.5,16.4l-3.8-5l6.8-6.8L13.5,16.4z"
/>
<svg class="svg-{size}" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Send">
<path
id="Vector"
d="M17.1564 9.44394L3.40636 2.56894C3.29859 2.51504 3.17754 2.49345 3.05778 2.50675C2.93803 2.52006 2.82467 2.5677 2.73136 2.64394C2.64225 2.71862 2.57574 2.81668 2.5393 2.92709C2.50287 3.0375 2.49795 3.15589 2.52511 3.26894L4.37511 10.0002L2.50011 16.7127C2.47463 16.8071 2.47165 16.9062 2.49143 17.0019C2.5112 17.0977 2.55317 17.1875 2.61396 17.2641C2.67475 17.3407 2.75267 17.402 2.84144 17.443C2.93022 17.484 3.02738 17.5036 3.12511 17.5002C3.22295 17.4996 3.31928 17.476 3.40636 17.4314L17.1564 10.5564C17.2587 10.504 17.3447 10.4243 17.4046 10.3262C17.4646 10.228 17.4964 10.1152 17.4964 10.0002C17.4964 9.88516 17.4646 9.77236 17.4046 9.67421C17.3447 9.57607 17.2587 9.49639 17.1564 9.44394ZM4.09386 15.6939L5.47511 10.6252H10.6251C10.9703 10.6252 11.2501 10.3454 11.2501 10.0002C11.2501 9.65501 10.9703 9.37519 10.6251 9.37519H5.47511L4.09386 4.30644L15.4751 10.0002L4.09386 15.6939Z"
{fill}
/>
</g>
</svg>

View File

@ -87,6 +87,9 @@
/* Dark Theme */
.theme-dark {
--theme-text-primary-color: rgba(255, 255, 255, .8);
--theme-text-placeholder-color: rgba(255, 255, 255, .4);
--accented-button-disabled: rgba(255, 255, 255, .12);
--accented-button-disabled-color: rgba(255, 255, 255, .4);
--brand-button-disabled: rgba(255, 255, 255, .12);
@ -112,9 +115,8 @@
--theme-button-contrast-disabled-color: rgba(0, 0, 0, .5);
--theme-button-contrast-border: rgba(255, 255, 255, .2);
--theme-refinput-color: rgba(255, 255, 255, .03);
--theme-refinput-divider: rgba(255, 255, 255, .07);
--theme-refinput-border: rgba(255, 255, 255, .03);
--theme-refinput-border: rgba(255, 255, 255, .1);
// Be aware to update defineAlpha() function in colors.ts
--theme-bg-color: #1A1A28;
@ -291,6 +293,9 @@
/* Light Theme */
.theme-light {
--theme-text-primary-color: rgba(0, 0, 0, .8);
--theme-text-placeholder-color: rgba(0, 0, 0, .4);
--accented-button-disabled: rgba(0, 0, 0, .12);
--accented-button-disabled-color: rgba(0, 0, 0, .4);
--brand-button-disabled: rgba(0, 0, 0, .12);
@ -316,9 +321,8 @@
--theme-button-contrast-disabled-color: rgba(255, 255, 255, .5);
--theme-button-contrast-border: rgba(0, 0, 0, .2);
--theme-refinput-color: rgba(0, 0, 0, .03);
--theme-refinput-divider: rgba(0, 0, 0, .07);
--theme-refinput-border: rgba(0, 0, 0, .03);
--theme-refinput-border: rgba(0, 0, 0, .1);
// Be aware to update defineAlpha() function in colors.ts
--theme-bg-color: #F1F1F4;

View File

@ -9,7 +9,7 @@
.text-editor-view {
overflow-y: auto;
color: var(--theme-caption-color);
color: var(--theme-text-primary-color);
p:not(:last-child) {
margin-block-end: 1em;
@ -23,7 +23,7 @@
p.is-editor-empty:first-child::before {
content: attr(data-placeholder);
float: left;
color: var(--theme-halfcontent-color);
color: var(--theme-text-placeholder-color);
pointer-events: none;
height: 0;
}

View File

@ -276,26 +276,6 @@
on:dragleave={() => {}}
on:drop|preventDefault|stopPropagation={fileDrop}
>
{#if attachments.size || progress}
<div class="flex-row-center list scroll-divider-color">
{#if progress}
<div class="flex p-3">
<Loading />
</div>
{/if}
{#each Array.from(attachments.values()) as attachment}
<div class="item flex">
<AttachmentPresenter
value={attachment}
removable
on:remove={(result) => {
if (result !== undefined) removeAttachment(attachment)
}}
/>
</div>
{/each}
</div>
{/if}
<ReferenceInput
{focusIndex}
bind:this={refInput}
@ -303,12 +283,12 @@
{iconSend}
{labelSend}
{showSend}
showHeader={attachments.size > 0 || progress}
{loading}
on:focus
on:blur
on:message={onMessage}
haveAttachment={attachments.size > 0}
withoutTopBorder={attachments.size > 0}
on:attach={() => {
dispatch('focus')
inputFile.click()
@ -316,20 +296,38 @@
on:update={onUpdate}
{placeholder}
{extraActions}
/>
>
<div slot="header">
{#if attachments.size || progress}
<div class="flex-row-center list scroll-divider-color">
{#if progress}
<div class="flex p-3">
<Loading />
</div>
{/if}
{#each Array.from(attachments.values()) as attachment}
<div class="item flex">
<AttachmentPresenter
value={attachment}
removable
on:remove={(result) => {
if (result !== undefined) removeAttachment(attachment)
}}
/>
</div>
{/each}
</div>
{/if}
</div>
</ReferenceInput>
</div>
</div>
<style lang="scss">
.list {
padding: 0.5rem;
color: var(--theme-caption-color);
overflow-x: auto;
overflow-y: hidden;
background-color: var(--theme-refinput-color);
border: 1px solid var(--theme-divider-color);
border-radius: 0.5rem 0.5rem 0 0;
border-bottom: none;
.item + .item {
padding-left: 1rem;

View File

@ -18,7 +18,7 @@
import { IntlString, setPlatformStatus, unknownError } from '@hcengineering/platform'
import { createQuery, DraftController, draftsStore, getClient } from '@hcengineering/presentation'
import { StyledTextBox } from '@hcengineering/text-editor'
import { IconSize, updatePopup } from '@hcengineering/ui'
import { ButtonSize, IconSize, updatePopup } from '@hcengineering/ui'
import { createEventDispatcher, onDestroy } from 'svelte'
import attachment from '../plugin'
import { deleteFile, uploadFile } from '../utils'
@ -35,7 +35,7 @@
export let alwaysEdit = false
export let showButtons = false
export let kind: 'normal' | 'emphasized' | 'indented' = 'normal'
export let buttonSize: IconSize = 'medium'
export let buttonSize: ButtonSize = 'medium'
export let formatButtonSize: IconSize = 'small'
export let maxHeight: 'max' | 'card' | 'limited' | string = 'max'
export let focusable: boolean = false

View File

@ -90,7 +90,7 @@
<div class="max-w-120 input">
<CommentInput
{object}
on:focus={(evt) => {
on:focus={() => {
commentMode = true
}}
/>

View File

@ -43,8 +43,8 @@ test.describe('workbench tests', () => {
await page.click('text=general')
// Click .textInput
await expect(page.locator('.textInput')).toBeVisible()
// Click .text-input
await expect(page.locator('.text-input')).toBeVisible()
await page.click('[id="app-contact\\:string\\:Contacts"]')
await page.click('.antiNav-element:has-text("Employee")')