Redesign input and empty state components: improve search input interactions with clear button and icons, update compatibility questions empty state styling, and adjust profile form input widths and button properties for consistency and usability.

This commit is contained in:
MartinBraquet
2026-05-10 11:21:17 +02:00
parent 47e898b0c6
commit 857c9eb65b
3 changed files with 51 additions and 13 deletions

View File

@@ -313,7 +313,14 @@ export function CompatibilityQuestionsDisplay(props: {
)
})}
{shownAnswers.length === 0 && (
<div className="text-ink-500">{t('answers.display.none', 'None')}</div>
<Col className="items-center py-8 text-center">
<div className="text-ink-600 mb-2">
{t('answers.display.no_results', 'No questions match your search')}
</div>
<div className="text-sm text-ink-500">
{t('answers.display.try_different', 'Try adjusting your search or filters')}
</div>
</Col>
)}
</>
)}

View File

@@ -387,6 +387,7 @@ export const OptionalProfileUserForm = (props: {
<label className={clsx(labelClassName)}>{t('profile.optional.age', 'Age')}</label>
<Input
type="number"
className={'!w-24'}
placeholder={t('profile.optional.age', 'Age')}
value={profile['age'] ?? undefined}
min={18}
@@ -422,7 +423,7 @@ export const OptionalProfileUserForm = (props: {
const heightInInches = Number(e.target.value || 0) * 12 + (heightInches ?? 0)
setProfile('height_in_inches', heightInInches)
}}
className={'w-20'}
className={'!w-20'}
value={typeof heightFeet === 'number' && heightFeet ? Math.floor(heightFeet) : ''}
min={0}
step={1}
@@ -437,7 +438,7 @@ export const OptionalProfileUserForm = (props: {
const heightInInches = Number(e.target.value || 0) + 12 * (heightFeet ?? 0)
setProfile('height_in_inches', heightInInches)
}}
className={'w-20'}
className={'!w-20'}
value={
typeof heightInches === 'number' && heightInches ? Math.floor(heightInches) : ''
}
@@ -462,7 +463,7 @@ export const OptionalProfileUserForm = (props: {
setProfile('height_in_inches', totalInches)
}
}}
className={'w-24'}
className={'!w-24'}
value={
heightFeet !== undefined && profile['height_in_inches']
? Math.round(profile['height_in_inches'] * 2.54)
@@ -1187,7 +1188,8 @@ export const OptionalProfileUserForm = (props: {
disabled={isSubmitting || uploadingImages}
loading={isSubmitting}
onClick={handleSubmit}
color={'gray'}
size={'xl'}
color={'primary'}
>
{buttonLabel ?? t('common.next', 'Next')}
</Button>

View File

@@ -1,5 +1,6 @@
import clsx from 'clsx'
import {ComponentPropsWithoutRef, forwardRef, Ref} from 'react'
import {Search, X} from 'lucide-react'
import {ComponentPropsWithoutRef, forwardRef, Ref, useState} from 'react'
import {Row} from 'web/components/layout/row'
/** Text input. Wraps html `<input>` */
@@ -11,21 +12,40 @@ export const Input = forwardRef(
} & ComponentPropsWithoutRef<'input'>,
ref: Ref<HTMLInputElement>,
) => {
const {error, searchIcon, className, ...rest} = props
const {error, searchIcon, className, value, onChange, ...rest} = props
const [hasValue, setHasValue] = useState(!!value)
const rowClassName = clsx(
'bg-canvas-50 h-12 rounded-xl border border-canvas-200 px-4 shadow-sm transition-colors items-center gap-2',
className,
)
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setHasValue(!!e.target.value)
onChange?.(e)
}
const handleClear = () => {
setHasValue(false)
// Trigger onChange with empty value
const syntheticEvent = {
target: {value: ''},
} as React.ChangeEvent<HTMLInputElement>
onChange?.(syntheticEvent)
}
const rowClassName =
'bg-canvas-50 h-12 rounded-xl border border-canvas-200 px-4 shadow-sm transition-colors items-center gap-2'
const elem = (
<input
ref={ref}
step={0.001} // default to 3 decimal places
value={value}
onChange={handleChange}
className={clsx(
'bg-canvas-50 invalid:border-error invalid:text-error invalid:placeholder-rose-700 focus:outline-none focus:ring-1 disabled:cursor-not-allowed md:text-sm [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:m-0 [&::-webkit-outer-spin-button]:m-0',
'w-full bg-canvas-50 invalid:border-error invalid:text-error invalid:placeholder-rose-700 focus:outline-none focus:ring-1 disabled:cursor-not-allowed md:text-sm [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:m-0 [&::-webkit-outer-spin-button]:m-0',
error
? 'border-error text-error focus:border-error focus:ring-error placeholder-rose-700' // matches invalid: styles
: 'focus:border-canvas-200 focus:ring-transparent',
!searchIcon && rowClassName,
className,
)}
{...rest}
/>
@@ -33,9 +53,18 @@ export const Input = forwardRef(
if (searchIcon)
return (
<Row className={clsx(rowClassName)}>
{searchIcon && <span className="search-icon">🔍</span>}
<Row className={rowClassName}>
<Search className="w-4 h-4 text-canvas-400" />
{elem}
{hasValue && (
<button
type="button"
onClick={handleClear}
className="text-canvas-400 hover:text-primary-600 transition-colors"
>
<X className="w-4 h-4" />
</button>
)}
</Row>
)