Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/@react-spectrum/s2/src/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export interface ComboboxStyleProps {
size?: 'S' | 'M' | 'L' | 'XL'
}
export interface ComboBoxProps<T extends object> extends
Omit<AriaComboBoxProps<T>, 'children' | 'style' | 'className' | 'defaultFilter' | 'allowsEmptyCollection' | keyof GlobalDOMAttributes>,
Omit<AriaComboBoxProps<T>, 'children' | 'style' | 'className' | 'defaultFilter' | 'allowsEmptyCollection' | 'isTriggerPressedWhenOpen' | keyof GlobalDOMAttributes>,
ComboboxStyleProps,
StyleProps,
SpectrumLabelableProps,
Expand Down Expand Up @@ -353,6 +353,7 @@ export const ComboBox = /*#__PURE__*/ (forwardRef as forwardRefType)(function Co
return (
<AriaComboBox
{...comboBoxProps}
isTriggerPressedWhenOpen={false}
allowsEmptyCollection
style={UNSAFE_style}
className={UNSAFE_className + style(field(), getAllowedOverrides())({
Expand Down Expand Up @@ -642,9 +643,6 @@ const ComboboxInner = forwardRef(function ComboboxInner(props: ComboBoxProps<any
)}
<Button
ref={buttonRef}
// Prevent press scale from sticking while ComboBox is open.
// @ts-ignore
isPressed={false}
style={renderProps => pressScale(buttonRef)(renderProps)}
className={renderProps => inputButton({
...renderProps,
Expand Down
6 changes: 2 additions & 4 deletions packages/@react-spectrum/s2/src/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import {useSpectrumContextProps} from './useSpectrumContextProps';


export interface DatePickerProps<T extends DateValue> extends
Omit<AriaDatePickerProps<T>, 'children' | 'className' | 'style' | keyof GlobalDOMAttributes>,
Omit<AriaDatePickerProps<T>, 'children' | 'className' | 'style' | 'isTriggerPressedWhenOpen' | keyof GlobalDOMAttributes>,
Pick<CalendarProps<T>, 'createCalendar' | 'pageBehavior' | 'firstDayOfWeek' | 'isDateUnavailable'>,
StyleProps,
SpectrumLabelableProps,
Expand Down Expand Up @@ -153,6 +153,7 @@ export const DatePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(function
ref={ref}
isRequired={isRequired}
{...dateFieldProps}
isTriggerPressedWhenOpen={false}
style={UNSAFE_style}
className={(UNSAFE_className || '') + style(field(), getAllowedOverrides())({
isInForm: !!formContext,
Expand Down Expand Up @@ -274,9 +275,6 @@ export function CalendarButton(props: {isOpen: boolean, size: 'S' | 'M' | 'L' |
return (
<Button
ref={buttonRef}
// Prevent press scale from sticking while DatePicker is open.
// @ts-ignore
isPressed={false}
onFocusChange={setButtonHasFocus}
style={pressScale(buttonRef)}
className={renderProps => inputButton({
Expand Down
11 changes: 2 additions & 9 deletions packages/@react-spectrum/s2/src/DialogTrigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
*/

import {DialogTrigger as AriaDialogTrigger, DialogTriggerProps as AriaDialogTriggerProps} from 'react-aria-components';
import {PressResponder} from '@react-aria/interactions';
import {ReactNode} from 'react';

export interface DialogTriggerProps extends AriaDialogTriggerProps {}
export type DialogTriggerProps = Omit<AriaDialogTriggerProps, 'isPressedWhenOpen'>;

/**
* DialogTrigger serves as a wrapper around a Dialog and its associated trigger, linking the Dialog's
Expand All @@ -23,12 +22,6 @@ export interface DialogTriggerProps extends AriaDialogTriggerProps {}
*/
export function DialogTrigger(props: DialogTriggerProps): ReactNode {
return (
<AriaDialogTrigger {...props}>
{/* RAC sets isPressed via PressResponder when the dialog is open.
We don't want press scaling to appear to get "stuck", so override this. */}
<PressResponder isPressed={false}>
{props.children}
</PressResponder>
</AriaDialogTrigger>
<AriaDialogTrigger {...props} isPressedWhenOpen={false} />
);
}
4 changes: 1 addition & 3 deletions packages/@react-spectrum/s2/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import {useSpectrumContextProps} from './useSpectrumContextProps';
// viewbox on LinkOut is super weird just because i copied the icon from designs...
// need to strip id's from icons

export interface MenuTriggerProps extends AriaMenuTriggerProps {
export interface MenuTriggerProps extends Omit<AriaMenuTriggerProps, 'isPressedWhenOpen'> {
/**
* Alignment of the menu relative to the trigger.
*
Expand Down Expand Up @@ -530,8 +530,6 @@ export function MenuItem(props: MenuItemProps): ReactNode {
* linking the Menu's open state with the trigger's press state.
*/
function MenuTrigger(props: MenuTriggerProps): ReactNode {
// RAC sets isPressed via PressResponder when the menu is open.
// We don't want press scaling to appear to get "stuck", so override this.
// For mouse interactions, menus open on press start. When the popover underlay appears
// it covers the trigger button, causing onPressEnd to fire immediately and no press scaling
// to occur. We override this by listening for pointerup on the document ourselves.
Expand Down
6 changes: 2 additions & 4 deletions packages/@react-spectrum/s2/src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export interface PickerStyleProps {

type SelectionMode = 'single' | 'multiple';
export interface PickerProps<T extends object, M extends SelectionMode = 'single'> extends
Omit<AriaSelectProps<T, M>, 'children' | 'style' | 'className' | keyof GlobalDOMAttributes>,
Omit<AriaSelectProps<T, M>, 'children' | 'style' | 'className' | 'isTriggerPressedWhenOpen' | keyof GlobalDOMAttributes>,
PickerStyleProps,
StyleProps,
SpectrumLabelableProps,
Expand Down Expand Up @@ -351,6 +351,7 @@ export const Picker = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pick
return (
<AriaSelect
{...pickerProps}
isTriggerPressedWhenOpen={false}
aria-describedby={spinnerId}
placeholder={placeholder}
style={UNSAFE_style}
Expand Down Expand Up @@ -521,9 +522,6 @@ const PickerButton = createHideableComponent(function PickerButton<T extends obj
<Button
ref={buttonRef}
style={renderProps => pressScale(buttonRef)(renderProps)}
// Prevent press scale from sticking while Picker is open.
// @ts-ignore
isPressed={false}
className={renderProps => inputButton({
...renderProps,
size: size,
Expand Down
129 changes: 73 additions & 56 deletions packages/@react-spectrum/s2/src/TabsPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,24 @@ import {controlFont, fieldInput, StyleProps} from './style-utils' with {type: 'm
import {
FieldLabel
} from './Field';
import {FocusableRef, FocusableRefValue, SpectrumLabelableProps} from '@react-types/shared';
import {FocusableRef, FocusableRefValue, PressEvent, SpectrumLabelableProps} from '@react-types/shared';
import {forwardRefType} from './types';
import {HeaderContext, HeadingContext, Text, TextContext} from './Content';
import {IconContext} from './Icon';
import {Placement, useLocale} from 'react-aria';
import {Popover} from './Popover';
import {PressResponder} from '@react-aria/interactions';
import {pressScale} from './pressScale';
import {raw} from '../style/style-macro' with {type: 'macro'};
import React, {createContext, forwardRef, ReactNode, useContext, useRef} from 'react';
import React, {createContext, forwardRef, ReactNode, useContext, useRef, useState} from 'react';
import {useFocusableRef} from '@react-spectrum/utils';
import {useFormProps} from './Form';
import {useGlobalListeners} from '@react-aria/utils';
import {useSpectrumContextProps} from './useSpectrumContextProps';
export interface PickerStyleProps {
}
export interface PickerProps<T extends object> extends
Omit<AriaSelectProps<T>, 'children' | 'style' | 'className' | 'placeholder'>,
Omit<AriaSelectProps<T>, 'children' | 'style' | 'className' | 'placeholder' | 'isTriggerPressedWhenOpen'>,
PickerStyleProps,
StyleProps,
SpectrumLabelableProps,
Expand Down Expand Up @@ -181,69 +183,84 @@ function Picker<T extends object>(props: PickerProps<T>, ref: FocusableRef<HTMLB
let {direction: dir} = useLocale();
let RTLFlipOffset = dir === 'rtl' ? -1 : 1;

// For mouse interactions, pickers open on press start. When the popover underlay appears
// it covers the trigger button, causing onPressEnd to fire immediately and no press scaling
// to occur. We override this by listening for pointerup on the document ourselves.
let [isPressed, setPressed] = useState(false);
let {addGlobalListener} = useGlobalListeners();
let onPressStart = (e: PressEvent) => {
if (e.pointerType !== 'mouse') {
return;
}
setPressed(true);
addGlobalListener(document, 'pointerup', () => {
setPressed(false);
}, {once: true, capture: true});
};

return (
<div>
<AriaSelect
{...pickerProps}
isTriggerPressedWhenOpen={false}
aria-labelledby={`${labelBehavior === 'hide' ? valueId : ''} ${ariaLabelledby}`}>
{({isOpen}) => (
<>
<FieldLabel isQuiet={isQuiet} />
<Button
ref={domRef}
style={renderProps => pressScale(domRef)(renderProps)}
// Prevent press scale from sticking while Picker is open.
// @ts-ignore
isPressed={false}
className={renderProps => inputButton({
...renderProps,
size: 'M',
isOpen,
isQuiet,
density
})}>
<SelectValue className={valueStyles + ' ' + raw('&> * {display: none;}')}>
{({defaultChildren}) => {
return (
<Provider
values={[
[IconContext, {
slots: {
icon: {
render: centerBaseline({slot: 'icon', styles: iconCenterWrapper({labelBehavior})}),
styles: icon
<PressResponder onPressStart={onPressStart} isPressed={isPressed}>
<Button
ref={domRef}
style={renderProps => pressScale(domRef)(renderProps)}
className={renderProps => inputButton({
...renderProps,
size: 'M',
isOpen,
isQuiet,
density
})}>
<SelectValue className={valueStyles + ' ' + raw('&> * {display: none;}')}>
{({defaultChildren}) => {
return (
<Provider
values={[
[IconContext, {
slots: {
icon: {
render: centerBaseline({slot: 'icon', styles: iconCenterWrapper({labelBehavior})}),
styles: icon
}
}
}
}],
[TextContext, {
slots: {
// Default slot is useful when converting other collections to PickerItems.
[DEFAULT_SLOT]: {
id: valueId,
styles: style({
display: {
default: 'block',
labelBehavior: {
hide: 'none'
}
},
flexGrow: 1,
truncate: true
})({labelBehavior})
}],
[TextContext, {
slots: {
// Default slot is useful when converting other collections to PickerItems.
[DEFAULT_SLOT]: {
id: valueId,
styles: style({
display: {
default: 'block',
labelBehavior: {
hide: 'none'
}
},
flexGrow: 1,
truncate: true
})({labelBehavior})
}
}
}
}],
[InsideSelectValueContext, true]
]}>
{defaultChildren}
</Provider>
);
}}
</SelectValue>
<ChevronIcon
size={size}
className={iconStyles} />
</Button>
}],
[InsideSelectValueContext, true]
]}>
{defaultChildren}
</Provider>
);
}}
</SelectValue>
<ChevronIcon
size={size}
className={iconStyles} />
</Button>
</PressResponder>
<Popover
hideArrow
offset={menuOffset}
Expand Down
12 changes: 9 additions & 3 deletions packages/react-aria-components/src/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ export interface ComboBoxProps<T extends object> extends Omit<AriaComboBoxProps<
*/
formValue?: 'text' | 'key',
/** Whether the combo box allows the menu to be open when the collection is empty. */
allowsEmptyCollection?: boolean
allowsEmptyCollection?: boolean,
/**
* Whether the trigger remains pressed when the overlay is open.
* @default true
*/
isTriggerPressedWhenOpen?: boolean
}

export const ComboBoxContext = createContext<ContextValue<ComboBoxProps<any>, HTMLDivElement>>(null);
Expand Down Expand Up @@ -123,7 +128,8 @@ function ComboBoxInner<T extends object>({props, collection, comboBoxRef: ref}:
let {
name,
formValue = 'key',
allowsCustomValue
allowsCustomValue,
isTriggerPressedWhenOpen = true
} = props;
if (allowsCustomValue) {
formValue = 'text';
Expand Down Expand Up @@ -207,7 +213,7 @@ function ComboBoxInner<T extends object>({props, collection, comboBoxRef: ref}:
values={[
[ComboBoxStateContext, state],
[LabelContext, {...labelProps, ref: labelRef}],
[ButtonContext, {...buttonProps, ref: buttonRef, isPressed: state.isOpen}],
[ButtonContext, {...buttonProps, ref: buttonRef, isPressed: isTriggerPressedWhenOpen && state.isOpen}],
[InputContext, {...inputProps, ref: inputRef}],
[OverlayTriggerStateContext, state],
[PopoverContext, {
Expand Down
20 changes: 16 additions & 4 deletions packages/react-aria-components/src/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,24 @@ export interface DatePickerProps<T extends DateValue> extends Omit<AriaDatePicke
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state.
* @default 'react-aria-DatePicker'
*/
className?: ClassNameOrFunction<DatePickerRenderProps>
className?: ClassNameOrFunction<DatePickerRenderProps>,
/**
* Whether the trigger remains pressed when the overlay is open.
* @default true
*/
isTriggerPressedWhenOpen?: boolean
}
export interface DateRangePickerProps<T extends DateValue> extends Omit<AriaDateRangePickerProps<T>, 'label' | 'description' | 'errorMessage' | 'validationState' | 'validationBehavior'>, Pick<DateRangePickerStateOptions<T>, 'shouldCloseOnSelect'>, RACValidation, RenderProps<DateRangePickerRenderProps>, SlotProps, GlobalDOMAttributes<HTMLDivElement> {
/**
* The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state.
* @default 'react-aria-DateRangePicker'
*/
className?: ClassNameOrFunction<DateRangePickerRenderProps>
className?: ClassNameOrFunction<DateRangePickerRenderProps>,
/**
* Whether the trigger remains pressed when the overlay is open.
* @default true
*/
isTriggerPressedWhenOpen?: boolean
}

export const DatePickerContext = createContext<ContextValue<DatePickerProps<any>, HTMLDivElement>>(null);
Expand All @@ -112,6 +122,7 @@ export const DatePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(function
[props, ref] = useContextProps(props, ref, DatePickerContext);
let {validationBehavior: formValidationBehavior} = useSlottedContext(FormContext) || {};
let validationBehavior = props.validationBehavior ?? formValidationBehavior ?? 'native';
let {isTriggerPressedWhenOpen = true} = props;
let state = useDatePickerState({
...props,
validationBehavior
Expand Down Expand Up @@ -174,7 +185,7 @@ export const DatePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(function
[DatePickerStateContext, state],
[GroupContext, {...groupProps, ref: groupRef, isInvalid: state.isInvalid}],
[DateFieldContext, fieldProps],
[ButtonContext, {...buttonProps, isPressed: state.isOpen}],
[ButtonContext, {...buttonProps, isPressed: isTriggerPressedWhenOpen && state.isOpen}],
[LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
[CalendarContext, calendarProps],
[OverlayTriggerStateContext, state],
Expand Down Expand Up @@ -221,6 +232,7 @@ export const DateRangePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(func
[props, ref] = useContextProps(props, ref, DateRangePickerContext);
let {validationBehavior: formValidationBehavior} = useSlottedContext(FormContext) || {};
let validationBehavior = props.validationBehavior ?? formValidationBehavior ?? 'native';
let {isTriggerPressedWhenOpen = true} = props;
let state = useDateRangePickerState({
...props,
validationBehavior
Expand Down Expand Up @@ -283,7 +295,7 @@ export const DateRangePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(func
values={[
[DateRangePickerStateContext, state],
[GroupContext, {...groupProps, ref: groupRef, isInvalid: state.isInvalid}],
[ButtonContext, {...buttonProps, isPressed: state.isOpen}],
[ButtonContext, {...buttonProps, isPressed: isTriggerPressedWhenOpen && state.isOpen}],
[LabelContext, {...labelProps, ref: labelRef, elementType: 'span'}],
[RangeCalendarContext, calendarProps],
[OverlayTriggerStateContext, state],
Expand Down
Loading