Cleaning the Editbox (#5012)

Signed-off-by: Alexander Platov <alexander.platov@hardcoreeng.com>
Signed-off-by: Alex Velichko <alex@hardcoreeng.com>
Co-authored-by: Alex Velichko <alex@hardcoreeng.com>
This commit is contained in:
Alexander Platov 2024-03-19 18:38:32 +03:00 committed by GitHub
parent 5b1fd6a9b7
commit 23a3c8e94f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 60 additions and 121 deletions

View File

@ -27,14 +27,20 @@
&:focus { box-shadow: 0 0 0 1px var(--theme-editbox-focus-border); } &:focus { box-shadow: 0 0 0 1px var(--theme-editbox-focus-border); }
} }
} }
.focusable:not(.default, .default-large, .ghost, .ghost-large, .modern-ghost-large) { .focusable:not(.underline, .default, .default-large, .ghost, .ghost-large, .modern-ghost-large) {
margin: 0 -0.75rem; position: relative;
padding: 0.625rem 0.75rem; padding: 0.625rem 0;
width: calc(100% + 1.5rem);
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out;
&:focus-within { box-shadow: 0 0 0 1px var(--theme-editbox-focus-border); } &::after {
position: absolute;
top: 0;
left: -0.75rem;
width: calc(100% + 1.5rem);
height: 100%;
border: 1px solid var(--theme-editbox-focus-border);
border-radius: 0.25rem;
}
&:focus-within::after { content: ''; }
} }
input { input {
@ -128,4 +134,9 @@
} }
} }
} }
.required::after {
content: ' *';
color: var(--theme-error-color);
}
} }

View File

@ -13,21 +13,18 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import type { Asset, IntlString } from '@hcengineering/platform' import type { IntlString } from '@hcengineering/platform'
import { translate } from '@hcengineering/platform' import { translate } from '@hcengineering/platform'
import { afterUpdate, createEventDispatcher, onMount } from 'svelte' import { createEventDispatcher, onMount } from 'svelte'
import { registerFocus } from '../focus' import { registerFocus } from '../focus'
import plugin from '../plugin' import plugin from '../plugin'
import type { AnySvelteComponent, EditStyle } from '../types' import type { EditStyle } from '../types'
import Icon from './Icon.svelte'
import Label from './Label.svelte' import Label from './Label.svelte'
import { resizeObserver } from '../resize'
import { floorFractionDigits } from '../utils' import { floorFractionDigits } from '../utils'
import { themeStore } from '@hcengineering/theme' import { themeStore } from '@hcengineering/theme'
export let label: IntlString | undefined = undefined export let label: IntlString | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined export let maxWidth: string = '100%'
export let maxWidth: string | undefined = undefined
export let value: string | number | undefined = undefined export let value: string | number | undefined = undefined
export let placeholder: IntlString = plugin.string.EditBoxPlaceholder export let placeholder: IntlString = plugin.string.EditBoxPlaceholder
export let placeholderParam: any | undefined = undefined export let placeholderParam: any | undefined = undefined
@ -45,11 +42,8 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let text: HTMLElement
let input: HTMLInputElement let input: HTMLInputElement
let style: string
let phTraslate: string = '' let phTraslate: string = ''
let parentWidth: number | undefined
$: { $: {
if ( if (
@ -61,38 +55,11 @@
value = floorFractionDigits(Number(value), maxDigitsAfterPoint) value = floorFractionDigits(Number(value), maxDigitsAfterPoint)
} }
} }
$: style = `max-width: ${
maxWidth || (parentWidth ? (icon ? `calc(${parentWidth}px - 1.25rem)` : `${parentWidth}px`) : 'max-content')
};`
$: void translate(placeholder, placeholderParam ?? {}, $themeStore.language).then((res) => { $: void translate(placeholder, placeholderParam ?? {}, $themeStore.language).then((res) => {
phTraslate = res phTraslate = res
}) })
function computeSize (t: HTMLInputElement | EventTarget | null): void { function handleInput (): void {
if (t == null) {
return
}
const target = t as HTMLInputElement
const value = target.value
text.innerHTML = (value === '' ? phTraslate : value)
.replaceAll(' ', '&nbsp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
if (format === 'number') {
target.style.width = maxWidth ?? '5rem'
} else if (kind === 'underline') {
target.style.width = `calc(${text.clientWidth}px + 1.125rem)`
} else {
target.style.width = Math.max(text.clientWidth, 50) + 'px'
}
}
function handleInput (ev: Event): void {
const t: HTMLInputElement | EventTarget | null = ev.target
if (t !== null && t !== undefined) {
computeSize(t)
}
dispatch('input') dispatch('input')
dispatch('value', value) dispatch('value', value)
} }
@ -106,11 +73,6 @@
input.select() input.select()
select = false select = false
} }
computeSize(input)
})
afterUpdate(() => {
computeSize(input)
}) })
export function focusInput (): void { export function focusInput (): void {
@ -148,6 +110,7 @@
class:flex-grow={fullSize} class:flex-grow={fullSize}
class:w-full={focusable || fullSize} class:w-full={focusable || fullSize}
class:uppercase class:uppercase
style:width={maxWidth}
on:click={(event) => { on:click={(event) => {
if (!propagateClick) { if (!propagateClick) {
event.stopPropagation() event.stopPropagation()
@ -155,33 +118,29 @@
input.focus() input.focus()
}} }}
use:resizeObserver={(element) => {
parentWidth = element.parentElement?.getBoundingClientRect().width
}}
> >
<!-- {focusIndex} --> <!-- {focusIndex} -->
<div class="hidden-text {kind}" bind:this={text} />
{#if label} {#if label}
<div class="mb-1 text-sm font-medium caption-color select-text"> <div class="mb-1 text-sm font-medium caption-color select-text" class:required>
<Label {label} /> <Label {label} />
{#if required}<span class="error-color">&ast;</span>{/if}
</div> </div>
{/if} {/if}
<div class="{kind} flex-row-center clear-mins" class:focusable class:disabled class:w-full={fullSize}> <div
{#if icon} class="{kind} flex-row-center clear-mins"
<div class="dark-color mr-1"> class:focusable
<Icon {icon} size={'small'} /> class:disabled
</div> class:w-full={fullSize}
{/if} style:width={maxWidth}
>
{#if format === 'password'} {#if format === 'password'}
<input <input
{disabled} {disabled}
style:width={maxWidth}
id="userPassword" id="userPassword"
bind:this={input} bind:this={input}
type="Password" type="Password"
bind:value bind:value
placeholder={phTraslate} placeholder={phTraslate}
{style}
on:input={handleInput} on:input={handleInput}
on:change on:change
on:keydown on:keydown
@ -193,12 +152,12 @@
{:else if format === 'number'} {:else if format === 'number'}
<input <input
{disabled} {disabled}
style:width={maxWidth}
bind:this={input} bind:this={input}
type="number" type="number"
class="number" class="number"
bind:value bind:value
placeholder={phTraslate} placeholder={phTraslate}
{style}
on:input={handleInput} on:input={handleInput}
on:change on:change
on:keydown on:keydown
@ -210,11 +169,11 @@
{:else} {:else}
<input <input
{disabled} {disabled}
style:width={maxWidth}
bind:this={input} bind:this={input}
type="text" type="text"
bind:value bind:value
placeholder={phTraslate} placeholder={phTraslate}
{style}
on:input={handleInput} on:input={handleInput}
on:change on:change
on:keydown on:keydown

View File

@ -17,7 +17,7 @@
import { Ref } from '@hcengineering/core' import { Ref } from '@hcengineering/core'
import { getClient, SpaceCreateCard } from '@hcengineering/presentation' import { getClient, SpaceCreateCard } from '@hcengineering/presentation'
import task, { ProjectType } from '@hcengineering/task' import task, { ProjectType } from '@hcengineering/task'
import { Component, EditBox, Grid, IconFolder } from '@hcengineering/ui' import { Component, EditBox, Grid } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import board from '../plugin' import board from '../plugin'
import { createBoard } from '../utils/BoardUtils' import { createBoard } from '../utils/BoardUtils'
@ -51,13 +51,7 @@
}} }}
> >
<Grid column={1} rowGap={1.5}> <Grid column={1} rowGap={1.5}>
<EditBox <EditBox label={board.string.BoardName} bind:value={name} placeholder={board.string.Board} autoFocus />
label={board.string.BoardName}
icon={IconFolder}
bind:value={name}
placeholder={board.string.Board}
autoFocus
/>
<!-- <ToggleWithLabel label={board.string.MakePrivate} description={board.string.MakePrivateDescription} /> --> <!-- <ToggleWithLabel label={board.string.MakePrivate} description={board.string.MakePrivateDescription} /> -->
<Component <Component

View File

@ -110,12 +110,6 @@
</svelte:fragment> </svelte:fragment>
<StatusControl slot="error" {status} /> <StatusControl slot="error" {status} />
<Grid column={1} rowGap={1.5}> <Grid column={1} rowGap={1.5}>
<EditBox <EditBox label={board.string.CardName} bind:value={title} placeholder={board.string.CardPlaceholder} autoFocus />
label={board.string.CardName}
bind:value={title}
icon={board.icon.Card}
placeholder={board.string.CardPlaceholder}
autoFocus
/>
</Grid> </Grid>
</Card> </Card>

View File

@ -19,7 +19,7 @@
import { DirectMessage } from '@hcengineering/chunter' import { DirectMessage } from '@hcengineering/chunter'
import workbench from '@hcengineering/workbench' import workbench from '@hcengineering/workbench'
import { getResource } from '@hcengineering/platform' import { getResource } from '@hcengineering/platform'
import { EditBox, IconFolder } from '@hcengineering/ui' import { EditBox } from '@hcengineering/ui'
import chunter from '../plugin' import chunter from '../plugin'
@ -63,7 +63,6 @@
> >
<EditBox <EditBox
label={chunter.string.ChannelName} label={chunter.string.ChannelName}
icon={IconFolder}
bind:value={name} bind:value={name}
placeholder={chunter.string.ChannelNamePlaceholder} placeholder={chunter.string.ChannelNamePlaceholder}
autoFocus autoFocus

View File

@ -15,7 +15,7 @@
<script lang="ts"> <script lang="ts">
import { Ref } from '@hcengineering/core' import { Ref } from '@hcengineering/core'
import { Department } from '@hcengineering/hr' import { Department } from '@hcengineering/hr'
import { Scroller, Separator } from '@hcengineering/ui' import { Scroller, Separator, EditBox } from '@hcengineering/ui'
import { TreeNode } from '@hcengineering/view-resources' import { TreeNode } from '@hcengineering/view-resources'
import { NavFooter, NavHeader } from '@hcengineering/workbench-resources' import { NavFooter, NavHeader } from '@hcengineering/workbench-resources'

View File

@ -19,7 +19,7 @@
import { Funnel } from '@hcengineering/lead' import { Funnel } from '@hcengineering/lead'
import presentation, { getClient, SpaceCreateCard } from '@hcengineering/presentation' import presentation, { getClient, SpaceCreateCard } from '@hcengineering/presentation'
import task, { ProjectType } from '@hcengineering/task' import task, { ProjectType } from '@hcengineering/task'
import ui, { Component, EditBox, Grid, IconFolder, Label, ToggleWithLabel } from '@hcengineering/ui' import ui, { Component, EditBox, Grid, Label, ToggleWithLabel } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte' import { createEventDispatcher } from 'svelte'
import lead from '../plugin' import lead from '../plugin'
@ -73,13 +73,7 @@
}} }}
> >
<Grid column={1} rowGap={1.5}> <Grid column={1} rowGap={1.5}>
<EditBox <EditBox label={lead.string.FunnelName} bind:value={name} placeholder={lead.string.FunnelName} autoFocus />
label={lead.string.FunnelName}
icon={IconFolder}
bind:value={name}
placeholder={lead.string.FunnelName}
autoFocus
/>
<ToggleWithLabel <ToggleWithLabel
label={presentation.string.MakePrivate} label={presentation.string.MakePrivate}
description={presentation.string.MakePrivateDescription} description={presentation.string.MakePrivateDescription}

View File

@ -241,20 +241,18 @@
}} }}
on:changeContent on:changeContent
> >
{typeType?.name} <span>{typeType?.name}</span>
<div class="flex-row-center clear-mins"> <div class="flex-row-center clear-mins">
<div class="mr-3"> <div class="mr-3">
<Button focusIndex={1} icon={Vacancy} size={'medium'} kind={'link-bordered'} noFocus /> <Button focusIndex={1} icon={Vacancy} size={'medium'} kind={'link-bordered'} noFocus />
</div> </div>
<div class="clear-mins flex-grow"> <EditBox
<EditBox focusIndex={2}
focusIndex={2} bind:value={name}
bind:value={name} placeholder={recruit.string.VacancyPlaceholder}
placeholder={recruit.string.VacancyPlaceholder} kind={'large-style'}
kind={'large-style'} autoFocus
autoFocus />
/>
</div>
</div> </div>
{#key appliedTemplateId} {#key appliedTemplateId}
<AttachmentStyledBox <AttachmentStyledBox

View File

@ -141,16 +141,14 @@
{/if} {/if}
</svelte:fragment> </svelte:fragment>
<span class="fs-title flex-grow"> <EditBox
<EditBox bind:value={rawName}
bind:value={rawName} placeholder={recruit.string.VacancyPlaceholder}
placeholder={recruit.string.VacancyPlaceholder} kind={'large-style'}
kind={'large-style'} focusable
focusable autoFocus={!embedded}
autoFocus={!embedded} on:blur={save}
on:blur={save} />
/>
</span>
<svelte:fragment slot="pre-utils"> <svelte:fragment slot="pre-utils">
{#if saved} {#if saved}

View File

@ -69,12 +69,6 @@
okLabel={recruit.string.OpinionSave} okLabel={recruit.string.OpinionSave}
on:changeContent on:changeContent
> >
<EditBox <EditBox label={recruit.string.OpinionValue} bind:value placeholder={recruit.string.OpinionValue} autoFocus />
label={recruit.string.OpinionValue}
bind:value
icon={recruit.icon.Application}
placeholder={recruit.string.OpinionValue}
autoFocus
/>
<StyledTextArea placeholder={recruit.string.Description} bind:content={description} kind={'emphasized'} /> <StyledTextArea placeholder={recruit.string.Description} bind:content={description} kind={'emphasized'} />
</Card> </Card>

View File

@ -54,7 +54,6 @@
<Grid column={1} rowGap={1.5}> <Grid column={1} rowGap={1.5}>
<EditBox <EditBox
label={templates.string.TemplateCategory} label={templates.string.TemplateCategory}
icon={IconFolder}
bind:value={name} bind:value={name}
placeholder={templates.string.TemplateCategory} placeholder={templates.string.TemplateCategory}
autoFocus autoFocus

View File

@ -71,7 +71,6 @@
{#if space} {#if space}
<EditBox <EditBox
label={clazz.label} label={clazz.label}
icon={clazz.icon}
bind:value={space.name} bind:value={space.name}
placeholder={clazz.label} placeholder={clazz.label}
autoFocus autoFocus

View File

@ -178,7 +178,7 @@ export async function checkIssue (page: Page, props: IssueProps): Promise<void>
} }
export async function checkIssueDraft (page: Page, props: IssueProps): Promise<void> { export async function checkIssueDraft (page: Page, props: IssueProps): Promise<void> {
await expect(page.locator('#issue-name')).toHaveText(props.name) await expect(page.locator('#issue-name input')).toHaveValue(props.name)
if (props.description !== undefined) { if (props.description !== undefined) {
await expect(page.locator('#issue-description')).toHaveText(props.description) await expect(page.locator('#issue-description')).toHaveText(props.description)