Skip to content

Commit f56a787

Browse files
Add quiet styling to S2 Picker (#6812)
* add quiet styling * remove spacing * fix spacing * simplify outline style * have label on a single line * long content in quiet picker to truncate in fixed width container * small fixes * offset dropdown when align is start * fix width of quiet picker dropdown * remove unused import * remove another unused import * update offset --------- Co-authored-by: Robert Snow <[email protected]>
1 parent 81e3804 commit f56a787

File tree

3 files changed

+66
-17
lines changed

3 files changed

+66
-17
lines changed

packages/@react-spectrum/s2/src/Field.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ interface FieldLabelProps extends Omit<LabelProps, 'className' | 'style' | 'chil
3535
includeNecessityIndicatorInAccessibilityName?: boolean,
3636
staticColor?: 'white' | 'black',
3737
contextualHelp?: ReactNode,
38+
isQuiet?: boolean,
3839
children?: ReactNode
3940
}
4041

@@ -49,6 +50,7 @@ function FieldLabel(props: FieldLabelProps, ref: DOMRef<HTMLLabelElement>) {
4950
labelPosition,
5051
staticColor,
5152
contextualHelp,
53+
isQuiet,
5254
UNSAFE_style,
5355
UNSAFE_className = '',
5456
...labelProps
@@ -80,9 +82,10 @@ function FieldLabel(props: FieldLabelProps, ref: DOMRef<HTMLLabelElement>) {
8082
contain: {
8183
labelPosition: {
8284
top: 'inline-size'
83-
}
85+
},
86+
isQuiet: 'none'
8487
}
85-
})({labelAlign, labelPosition})}>
88+
})({labelAlign, labelPosition, isQuiet})}>
8689
<Label
8790
{...labelProps}
8891
ref={domRef}

packages/@react-spectrum/s2/src/Picker.tsx

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,12 @@ export interface PickerStyleProps {
6767
*
6868
* @default 'M'
6969
*/
70-
size?: 'S' | 'M' | 'L' | 'XL'
70+
size?: 'S' | 'M' | 'L' | 'XL',
71+
/**
72+
* Whether the picker should be displayed with a quiet style.
73+
* @private
74+
*/
75+
isQuiet?: boolean
7176
}
7277

7378
export interface PickerProps<T extends object> extends
@@ -101,6 +106,9 @@ interface PickerButtonProps extends PickerStyleProps, ButtonRenderProps {}
101106
const inputButton = style<PickerButtonProps | AriaSelectRenderProps>({
102107
...focusRing(),
103108
...fieldInput(),
109+
outlineStyle: {
110+
isQuiet: 'none'
111+
},
104112
position: 'relative',
105113
font: 'control',
106114
display: 'flex',
@@ -110,19 +118,42 @@ const inputButton = style<PickerButtonProps | AriaSelectRenderProps>({
110118
alignItems: 'center',
111119
height: 'control',
112120
transition: 'default',
113-
columnGap: 'text-to-control',
114-
paddingX: 'edge-to-text',
121+
columnGap: {
122+
default: 'text-to-control',
123+
isQuiet: 'text-to-visual'
124+
},
125+
paddingX: {
126+
default: 'edge-to-text',
127+
isQuiet: 0
128+
},
115129
backgroundColor: {
116130
default: baseColor('gray-100'),
117131
isOpen: 'gray-200',
118-
isDisabled: 'disabled'
132+
isDisabled: 'disabled',
133+
isQuiet: 'transparent'
119134
},
120135
color: {
121136
default: 'neutral',
122137
isDisabled: 'disabled'
138+
},
139+
maxWidth: {
140+
isQuiet: 'max'
123141
}
124142
});
125143

144+
const quietFocusLine = style({
145+
width: 'full',
146+
// Use pixels since we are emulating a border.
147+
height: `[2px]`,
148+
position: 'absolute',
149+
bottom: 0,
150+
borderRadius: 'full',
151+
backgroundColor: {
152+
default: 'blue-800',
153+
forcedColors: 'Highlight'
154+
}
155+
})
156+
126157
export let menu = style({
127158
outlineStyle: 'none',
128159
display: 'grid',
@@ -157,7 +188,10 @@ const invalidBorder = style({
157188
});
158189

159190
const valueStyles = style({
160-
flexGrow: 1,
191+
flexGrow: {
192+
default: 1,
193+
isQuiet: 0
194+
},
161195
truncate: true,
162196
display: 'flex',
163197
alignItems: 'center'
@@ -195,7 +229,8 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
195229
necessityIndicator,
196230
UNSAFE_className = '',
197231
UNSAFE_style,
198-
placeholder = 'Select an option...',
232+
placeholder = 'Select...',
233+
isQuiet,
199234
...pickerProps
200235
} = props;
201236

@@ -221,7 +256,7 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
221256
labelPosition,
222257
size
223258
}, props.styles)}>
224-
{({isDisabled, isOpen, isInvalid, isRequired}) => (
259+
{({isDisabled, isOpen, isFocusVisible, isInvalid, isRequired}) => (
225260
<>
226261
<InternalPickerContext.Provider value={{size}}>
227262
<FieldLabel
@@ -230,6 +265,7 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
230265
size={size}
231266
labelPosition={labelPosition}
232267
labelAlign={labelAlign}
268+
isQuiet={isQuiet}
233269
necessityIndicator={necessityIndicator}
234270
contextualHelp={props.contextualHelp}>
235271
{label}
@@ -240,11 +276,12 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
240276
className={renderProps => inputButton({
241277
...renderProps,
242278
size: size,
243-
isOpen
279+
isOpen,
280+
isQuiet
244281
})}>
245282
{(renderProps) => (
246283
<>
247-
<SelectValue className={valueStyles + ' ' + raw('&> * {display: none;}')}>
284+
<SelectValue className={valueStyles({isQuiet}) + ' ' + raw('&> * {display: none;}')}>
248285
{({defaultChildren}) => {
249286
return (
250287
<Provider
@@ -280,7 +317,8 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
280317
<ChevronIcon
281318
size={size}
282319
className={iconStyles} />
283-
{isInvalid && !isDisabled &&
320+
{isFocusVisible && isQuiet && <span className={quietFocusLine} /> }
321+
{isInvalid && !isDisabled && !isQuiet &&
284322
// @ts-ignore known limitation detecting functions from the theme
285323
<div className={invalidBorder({...renderProps, size})} />
286324
}
@@ -300,14 +338,21 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
300338
placement={`${direction} ${align}` as Placement}
301339
shouldFlip={shouldFlip}
302340
UNSAFE_style={{
303-
width: menuWidth ? `${menuWidth}px` : undefined
341+
width: menuWidth && !isQuiet ? `${menuWidth}px` : undefined
304342
}}
305343
styles={style({
344+
marginStart: {
345+
isQuiet: -12
346+
},
306347
minWidth: {
307-
default: '[var(--trigger-width)]'
348+
default: '[var(--trigger-width)]',
349+
isQuiet: 192
308350
},
309-
width: '[var(--trigger-width)]'
310-
})}>
351+
width: {
352+
default: '[var(--trigger-width)]',
353+
isQuiet: '[calc(var(--trigger-width) + (-2 * self(marginStart)))]'
354+
}
355+
})(props)}>
311356
<Provider
312357
values={[
313358
[HeaderContext, {className: sectionHeader({size})}],

packages/@react-spectrum/s2/src/style-utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ export const fieldInput = () => ({
111111
contain: {
112112
// Only apply size containment if contain-intrinsic-width is supported.
113113
// In older browsers, this will fall back to the default browser intrinsic width.
114-
'@supports (contain-intrinsic-width: 1px)': 'inline-size'
114+
'@supports (contain-intrinsic-width: 1px)': 'inline-size',
115+
isQuiet: 'none'
115116
},
116117
'--defaultWidth': {
117118
type: 'width',

0 commit comments

Comments
 (0)