Match ranked 'Add values…' affordance to select/multiselect

The ranked values cell now mirrors the select/multiselect CreatableSelect
cell for consistency across field types:

- Tab commits a pending value (keeping focus in the input for the next
  one) when it's non-empty and not a duplicate, matching the select cell;
  a blank/duplicate input lets Tab move focus away normally.
- The empty-state placeholder uses the shared 'Add values… (required)'
  text and matches react-select's placeholder color (full-opacity
  neutral50) and 10px content inset, so size, color, and left alignment
  line up with the select cell.
- The placeholder is hidden once values exist, mirroring react-select.
- The chips well gets the same hover/focus background tint and text
  cursor as the select control.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
David Krauser
2026-06-11 17:34:52 -04:00
parent d2c0965674
commit 3c12f30008
2 changed files with 44 additions and 8 deletions
@@ -8,8 +8,21 @@
min-height: 40px;
flex-wrap: wrap;
align-items: center;
padding: 8px 12px;
// 10px matches react-select's content inset on the select/multiselect
// cell (valueContainer's 8px padding + the 2px margin on each chip and on
// the placeholder), so chips and the "Add values…" input line up across
// field types.
padding: 8px 10px;
cursor: text;
gap: 6px;
// Match the select/multiselect values cell's hover/focus affordance so the
// empty state (and the populated cell) read identically across field types.
&:hover,
&:focus-within {
background: rgba(var(--button-bg-rgb), 0.08);
}
}
// The chip is a pill split into three flush regions: the rank badge fills the
@@ -82,15 +95,28 @@
}
}
// Match the select/multiselect CreatableSelect's "Add values…" input exactly:
// react-select sets `fontSize: inherit` on its input (so it follows the cell
// size rather than the larger UA default a bare <input> would use). No left
// padding here — the chips container's 10px inset already aligns this with the
// react-select placeholder/value position.
.user-property-rank-values__add-input {
min-width: 120px;
height: 24px;
flex: 1;
padding: 0 4px;
padding: 0;
border: none;
background: transparent;
color: var(--center-channel-color);
font-size: 12px;
font-size: inherit;
// react-select renders its placeholder as a plain <div> at full opacity in
// theme.colors.neutral50; a native <input>'s ::placeholder is dimmed by the
// browser's default opacity, so reset it to 1 to match the grey exactly.
&::placeholder {
color: hsl(0, 0%, 50%);
opacity: 1;
}
&:focus {
outline: none;
@@ -107,11 +107,18 @@ const UserPropertyRankValues = ({field, updateField, autoFocus}: Props) => {
}, [trimmedQuery, isDuplicate, options, setOptions]);
const handleQueryKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {
// Commit the pending value on Enter, and on Tab when there's a valid value
// to add — keeping focus in the input for the next value, mirroring the
// select/multiselect values cell. A blank or duplicate input lets Tab move
// focus away normally.
if (event.key === 'Enter') {
event.preventDefault();
addValue();
} else if (event.key === 'Tab' && trimmedQuery && !isDuplicate) {
event.preventDefault();
addValue();
}
}, [addValue]);
}, [addValue, trimmedQuery, isDuplicate]);
return (
<div className='user-property-rank-values'>
@@ -136,10 +143,13 @@ const UserPropertyRankValues = ({field, updateField, autoFocus}: Props) => {
className='user-property-rank-values__add-input'
value={query}
maxLength={Constants.MAX_CUSTOM_ATTRIBUTE_LENGTH}
placeholder={formatMessage({
id: 'admin.system_properties.user_properties.rank_values.add_placeholder',
defaultMessage: 'Add value…',
})}
// Mirror react-select: the placeholder only shows in the
// empty state, not once values have been added.
placeholder={ascOptions.length === 0 ? formatMessage({
id: 'admin.system_properties.user_properties.table.values.placeholder',
defaultMessage: 'Add values… (required)',
}) : undefined}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleQueryKeyDown}
onBlur={addValue}