0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-10 21:58:23 -05:00

refactor(console): improve invitation email input field (#5615)

This commit is contained in:
Charles Zhao 2024-04-02 15:26:41 +08:00 committed by GitHub
parent e09318d3e8
commit d1c41a2fa7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 96 additions and 74 deletions

View file

@ -4,8 +4,8 @@
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
justify-content: space-between; justify-content: space-between;
min-height: 96px; min-height: 102px;
padding: 0 _.unit(2) 0 _.unit(3); padding: _.unit(1.5) _.unit(3);
background: var(--color-layer-1); background: var(--color-layer-1);
border: 1px solid var(--color-border); border: 1px solid var(--color-border);
border-radius: 8px; border-radius: 8px;
@ -17,11 +17,12 @@
cursor: pointer; cursor: pointer;
position: relative; position: relative;
&.multiple { .wrapper {
display: flex;
align-items: center;
justify-content: flex-start; justify-content: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
gap: _.unit(2); gap: _.unit(2);
padding: _.unit(1.5) _.unit(3);
cursor: text; cursor: text;
.tag { .tag {
@ -58,8 +59,7 @@
color: var(--color-text); color: var(--color-text);
font: var(--font-body-2); font: var(--font-body-2);
background: transparent; background: transparent;
flex-grow: 1; flex: 1;
padding: _.unit(0.5);
&::placeholder { &::placeholder {
color: var(--color-placeholder); color: var(--color-placeholder);
@ -81,6 +81,10 @@
} }
} }
canvas {
display: none;
}
.errorMessage { .errorMessage {
font: var(--font-body-2); font: var(--font-body-2);
color: var(--color-error); color: var(--color-error);

View file

@ -2,7 +2,7 @@ import { emailRegEx } from '@logto/core-kit';
import { generateStandardShortId } from '@logto/shared/universal'; import { generateStandardShortId } from '@logto/shared/universal';
import { conditional, type Nullable } from '@silverhand/essentials'; import { conditional, type Nullable } from '@silverhand/essentials';
import classNames from 'classnames'; import classNames from 'classnames';
import { useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import Close from '@/assets/icons/close.svg'; import Close from '@/assets/icons/close.svg';
@ -23,6 +23,13 @@ type Props = {
placeholder?: string; placeholder?: string;
}; };
/**
* The body-2 font declared in @logto/core-kit/scss/fonts. It is referenced here to calculate
* the width of the input text, which determines the minimum width of the input field.
*/
const fontBody2 =
'400 14px / 20px -apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji';
function InviteEmailsInput({ function InviteEmailsInput({
className, className,
values, values,
@ -35,6 +42,18 @@ function InviteEmailsInput({
const [currentValue, setCurrentValue] = useState(''); const [currentValue, setCurrentValue] = useState('');
const { setError, clearErrors } = useFormContext<InviteMemberForm>(); const { setError, clearErrors } = useFormContext<InviteMemberForm>();
const { parseEmailOptions } = useEmailInputUtils(); const { parseEmailOptions } = useEmailInputUtils();
const [minInputWidth, setMinInputWidth] = useState<number>(0);
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
// Render placeholder text in canvas to calculate its width in CSS pixels.
const ctx = canvasRef.current?.getContext('2d');
if (!ctx) {
return;
}
ctx.font = fontBody2;
setMinInputWidth(ctx.measureText(currentValue).width);
}, [currentValue]);
const onChange = (values: InviteeEmailItem[]) => { const onChange = (values: InviteeEmailItem[]) => {
const { values: parsedValues, errorMessage } = parseEmailOptions(values); const { values: parsedValues, errorMessage } = parseEmailOptions(values);
@ -67,12 +86,7 @@ function InviteEmailsInput({
return ( return (
<> <>
<div <div
className={classNames( className={classNames(styles.input, Boolean(error) && styles.error, className)}
styles.input,
styles.multiple,
Boolean(error) && styles.error,
className
)}
role="button" role="button"
tabIndex={0} tabIndex={0}
onKeyDown={onKeyDownHandler(() => { onKeyDown={onKeyDownHandler(() => {
@ -82,6 +96,7 @@ function InviteEmailsInput({
ref.current?.focus(); ref.current?.focus();
}} }}
> >
<div className={styles.wrapper}>
{values.map((option) => ( {values.map((option) => (
<Tag <Tag
key={option.id} key={option.id}
@ -114,6 +129,7 @@ function InviteEmailsInput({
ref={ref} ref={ref}
placeholder={conditional(values.length === 0 && placeholder)} placeholder={conditional(values.length === 0 && placeholder)}
value={currentValue} value={currentValue}
style={{ minWidth: `${minInputWidth}px` }}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === 'Backspace' && currentValue === '') { if (event.key === 'Backspace' && currentValue === '') {
if (focusedValueId) { if (focusedValueId) {
@ -148,9 +164,11 @@ function InviteEmailsInput({
}} }}
/> />
</div> </div>
</div>
{Boolean(error) && typeof error === 'string' && ( {Boolean(error) && typeof error === 'string' && (
<div className={styles.errorMessage}>{error}</div> <div className={styles.errorMessage}>{error}</div>
)} )}
<canvas ref={canvasRef} />
</> </>
); );
} }