Skip to content

Commit

Permalink
Add slot contexts to all S2 components (adobe#6856)
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett authored Aug 14, 2024
1 parent c4a783e commit f30983a
Show file tree
Hide file tree
Showing 53 changed files with 636 additions and 392 deletions.
14 changes: 9 additions & 5 deletions packages/@react-spectrum/s2/src/ActionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@
*/

import {baseColor, fontRelative, style} from '../style/spectrum-theme' with { type: 'macro' };
import {ButtonProps, ButtonRenderProps, OverlayTriggerStateContext, Provider, Button as RACButton, Text} from 'react-aria-components';
import {ButtonProps, ButtonRenderProps, ContextValue, OverlayTriggerStateContext, Provider, Button as RACButton, Text} from 'react-aria-components';
import {centerBaseline} from './CenterBaseline';
import {FocusableRef} from '@react-types/shared';
import {createContext, forwardRef, ReactNode, useContext} from 'react';
import {FocusableRef, FocusableRefValue} from '@react-types/shared';
import {focusRing, getAllowedOverrides, StyleProps} from './style-utils' with { type: 'macro' };
import {forwardRef, ReactNode, useContext} from 'react';
import {IconContext} from './Icon';
import {pressScale} from './pressScale';
import {TextContext} from './Content';
import {useFocusableRef} from '@react-spectrum/utils';
import {useSpectrumContextProps} from './useSpectrumContextProps';

export interface ActionButtonStyleProps {
/**
Expand Down Expand Up @@ -170,7 +171,10 @@ export const btnStyles = style<ButtonRenderProps & ActionButtonStyleProps & Togg
disableTapHighlight: true
}, getAllowedOverrides());

export const ActionButtonContext = createContext<ContextValue<ActionButtonProps, FocusableRefValue<HTMLButtonElement>>>(null);

function ActionButton(props: ActionButtonProps, ref: FocusableRef<HTMLButtonElement>) {
[props, ref] = useSpectrumContextProps(props, ref, ActionButtonContext);
let domRef = useFocusableRef(ref);
let overlayTriggerState = useContext(OverlayTriggerStateContext);

Expand All @@ -189,9 +193,9 @@ function ActionButton(props: ActionButtonProps, ref: FocusableRef<HTMLButtonElem
}, props.styles)}>
<Provider
values={[
[TextContext, {className: style({paddingY: '--labelPadding', order: 1, truncate: true})}],
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1, truncate: true})}],
[IconContext, {
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
}]
]}>
Expand Down
9 changes: 7 additions & 2 deletions packages/@react-spectrum/s2/src/ActionMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
*/

import {ActionButton, ActionButtonProps} from './ActionButton';
import {AriaLabelingProps, DOMProps, FocusableRef} from '@react-types/shared';
import {AriaLabelingProps, DOMProps, FocusableRef, FocusableRefValue} from '@react-types/shared';
import {ContextValue} from 'react-aria-components';
import {createContext, forwardRef} from 'react';
import {filterDOMProps} from '@react-aria/utils';
import {forwardRef} from 'react';
import {forwardRefType} from './types';
import {Menu, MenuProps, MenuTrigger, MenuTriggerProps} from './Menu';
import MoreIcon from '../s2wf-icons/S2_Icon_More_20_N.svg';
import {StyleProps} from './style-utils' with { type: 'macro' };
import {useSpectrumContextProps} from './useSpectrumContextProps';

export interface ActionMenuProps<T> extends
Pick<MenuTriggerProps, 'isOpen' | 'defaultOpen' | 'onOpenChange' | 'align' | 'direction' | 'shouldFlip'>,
Expand All @@ -26,7 +28,10 @@ export interface ActionMenuProps<T> extends
StyleProps, DOMProps, AriaLabelingProps {
}

export const ActionMenuContext = createContext<ContextValue<ActionMenuProps<any>, FocusableRefValue<HTMLButtonElement>>>(null);

function ActionMenu<T extends object>(props: ActionMenuProps<T>, ref: FocusableRef<HTMLButtonElement>) {
[props, ref] = useSpectrumContextProps(props, ref, ActionMenuContext);
let buttonProps = filterDOMProps(props, {labelable: true});

// size independently controlled?
Expand Down
9 changes: 5 additions & 4 deletions packages/@react-spectrum/s2/src/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
* governing permissions and limitations under the License.
*/

import {ContextValue, useContextProps} from 'react-aria-components';
import {ContextValue} from 'react-aria-components';
import {createContext, forwardRef} from 'react';
import {DOMProps, DOMRef} from '@react-types/shared';
import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared';
import {filterDOMProps} from '@react-aria/utils';
import {getAllowedOverrides, StyleProps, StylesPropWithHeight, UnsafeStyles} from './style-utils' with {type: 'macro'};
import {style} from '../style/spectrum-theme' with { type: 'macro' };
import {useDOMRef} from '@react-spectrum/utils';
import {useSpectrumContextProps} from './useSpectrumContextProps';

export interface AvatarProps extends StyleProps, DOMProps {
/** Text description of the avatar. */
Expand All @@ -40,11 +41,11 @@ const imageStyles = style({
disableTapHighlight: true
}, getAllowedOverrides({height: true}));

export const AvatarContext = createContext<ContextValue<AvatarContextProps, HTMLImageElement>>({});
export const AvatarContext = createContext<ContextValue<AvatarContextProps, DOMRefValue<HTMLImageElement>>>(null);

function Avatar(props: AvatarProps, ref: DOMRef<HTMLImageElement>) {
[props, ref] = useSpectrumContextProps(props, ref, AvatarContext);
let domRef = useDOMRef(ref);
[props, domRef] = useContextProps(props, domRef, AvatarContext);
let {
alt = '',
src,
Expand Down
16 changes: 10 additions & 6 deletions packages/@react-spectrum/s2/src/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@
* governing permissions and limitations under the License.
*/

import {AriaLabelingProps, DOMProps, DOMRef} from '@react-types/shared';
import {AriaLabelingProps, DOMProps, DOMRef, DOMRefValue} from '@react-types/shared';
import {centerBaseline} from './CenterBaseline';
import {centerPadding, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
import {ContextValue, Provider, SlotProps} from 'react-aria-components';
import {filterDOMProps} from '@react-aria/utils';
import {fontRelative, style} from '../style/spectrum-theme' with {type: 'macro'};
import {IconContext} from './Icon';
import {Provider} from 'react-aria-components';
import React, {forwardRef, ReactNode} from 'react';
import React, {createContext, forwardRef, ReactNode} from 'react';
import {Text, TextContext} from './Content';
import {useDOMRef} from '@react-spectrum/utils';
import {useSpectrumContextProps} from './useSpectrumContextProps';

export interface BadgeStyleProps {
/**
Expand All @@ -36,13 +37,15 @@ export interface BadgeStyleProps {
variant: 'accent' | 'informative' | 'neutral' | 'positive' | 'notice' | 'negative' | 'gray' | 'red' | 'orange' | 'yellow' | 'charteuse' | 'celery' | 'green' | 'seafoam' | 'cyan' | 'blue' | 'indigo' | 'purple' | 'fuchsia' | 'magenta' | 'pink' | 'turquoise' | 'brown' | 'cinnamon' | 'silver'
}

export interface BadgeProps extends DOMProps, AriaLabelingProps, StyleProps, BadgeStyleProps{
export interface BadgeProps extends DOMProps, AriaLabelingProps, StyleProps, BadgeStyleProps, SlotProps {
/**
* The content to display in the badge.
*/
children: ReactNode
}

export const BadgeContext = createContext<ContextValue<Partial<BadgeProps>, DOMRefValue<HTMLDivElement>>>(null);

const badge = style<BadgeStyleProps>({
display: 'flex',
font: 'control',
Expand Down Expand Up @@ -115,6 +118,7 @@ const badge = style<BadgeStyleProps>({
}, getAllowedOverrides());

function Badge(props: BadgeProps, ref: DOMRef<HTMLDivElement>) {
[props, ref] = useSpectrumContextProps(props, ref, BadgeContext);
let {
children,
variant = 'neutral',
Expand All @@ -127,9 +131,9 @@ function Badge(props: BadgeProps, ref: DOMRef<HTMLDivElement>) {
return (
<Provider
values={[
[TextContext, {className: style({paddingY: '--labelPadding', order: 1})}],
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1})}],
[IconContext, {
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
}]
]}>
Expand Down
101 changes: 46 additions & 55 deletions packages/@react-spectrum/s2/src/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
* governing permissions and limitations under the License.
*/

import {Breadcrumb as AriaBreadcrumb, BreadcrumbsProps as AriaBreadcrumbsProps, HeadingContext, Link, Provider, Breadcrumbs as RACBreadcrumbs} from 'react-aria-components';
import {Breadcrumb as AriaBreadcrumb, BreadcrumbsProps as AriaBreadcrumbsProps, ContextValue, HeadingContext, Link, Provider, Breadcrumbs as RACBreadcrumbs, useSlottedContext} from 'react-aria-components';
import {AriaBreadcrumbItemProps} from 'react-aria';
import ChevronIcon from '../ui-icons/Chevron';
import {Children, cloneElement, createContext, forwardRef, isValidElement, ReactElement, ReactNode, useContext, useRef} from 'react';
import {createContext, forwardRef, ReactNode, useRef} from 'react';
import {DOMRefValue, LinkDOMProps} from '@react-types/shared';
import {focusRing, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
import {forwardRefType} from './types';
import {LinkDOMProps} from '@react-types/shared';
import {size, style} from '../style/spectrum-theme' with { type: 'macro' };

interface BreadcrumbsStyleProps {
Expand All @@ -41,6 +41,8 @@ export interface BreadcrumbsProps<T> extends Omit<AriaBreadcrumbsProps<T>, 'chil
children?: ReactNode
}

export const BreadcrumbsContext = createContext<ContextValue<BreadcrumbsProps<any>, DOMRefValue<HTMLDivElement>>>(null);

const wrapper = style<BreadcrumbsStyleProps>({
display: 'flex',
justifyContent: 'start',
Expand All @@ -67,38 +69,27 @@ const wrapper = style<BreadcrumbsStyleProps>({
}
}, getAllowedOverrides());

const BreadcrumbsInternalContext = createContext<BreadcrumbsProps<any> & {length: number}>({length: 0});

function Breadcrumbs<T extends object>({
function Breadcrumbs<T extends object>(props: BreadcrumbsProps<T>) {
let {
UNSAFE_className = '',
UNSAFE_style,
styles,
...props
}: BreadcrumbsProps<T>) {
let {size = 'M', isDisabled} = props;
size = 'M',
children,
...otherProps
} = props;
let ref = useRef(null);
// TODO: Remove when https://github.com/adobe/react-spectrum/pull/6440 is released
let childArray: ReactElement[] = [];
Children.forEach(props.children, (child, index) => {
if (isValidElement<{index: number}>(child)) {
child = cloneElement(child, {key: index, index});
childArray.push(child);
}
});
return (
<RACBreadcrumbs
{...props}
{...otherProps}
ref={ref}
style={UNSAFE_style}
className={UNSAFE_className + wrapper({
size
}, styles)}>
<Provider
values={[
[BreadcrumbsInternalContext, {size, isDisabled, length: childArray.length}]
]}>
{childArray}
</Provider>
<BreadcrumbsContext.Provider value={props}>
{children}
</BreadcrumbsContext.Provider>
</RACBreadcrumbs>
);
}
Expand Down Expand Up @@ -192,43 +183,43 @@ export interface BreadcrumbProps extends Omit<AriaBreadcrumbItemProps, 'children

export function Breadcrumb({children, ...props}: BreadcrumbProps) {
let {href, target, rel, download, ping, referrerPolicy, ...other} = props;
let {size = 'M', length, isDisabled} = useContext(BreadcrumbsInternalContext);
let {size = 'M', isDisabled} = useSlottedContext(BreadcrumbsContext)!;
let ref = useRef(null);
// TODO: use isCurrent render prop when https://github.com/adobe/react-spectrum/pull/6440 is released
let isCurrent = (props as BreadcrumbProps & {index: number}).index === length - 1;
return (
<AriaBreadcrumb
{...other}
ref={ref}
className={breadcrumbStyles({size, isCurrent})} >
{isCurrent ?
<span
className={currentStyles({size})}>
<Provider
values={[
[HeadingContext, {className: heading}]
]}>
{children}
</Provider>
</span>
: (
<>
<Link
style={({isFocusVisible}) => ({clipPath: isFocusVisible ? 'none' : 'margin-box'})}
href={href}
target={target}
rel={rel}
download={download}
ping={ping}
referrerPolicy={referrerPolicy}
isDisabled={isDisabled || isCurrent}
className={({isFocused, isFocusVisible, isHovered, isDisabled, isPressed}) => linkStyles({isFocused, isFocusVisible, isHovered, isDisabled, size, isCurrent, isPressed})}>
className={({isCurrent}) => breadcrumbStyles({size, isCurrent})}>
{({isCurrent}) => (
isCurrent ?
<span
className={currentStyles({size})}>
<Provider
values={[
[HeadingContext, {className: heading}]
]}>
{children}
</Link>
<ChevronIcon
size="M"
className={chevronStyles} />
</>
</Provider>
</span>
: (
<>
<Link
style={({isFocusVisible}) => ({clipPath: isFocusVisible ? 'none' : 'margin-box'})}
href={href}
target={target}
rel={rel}
download={download}
ping={ping}
referrerPolicy={referrerPolicy}
isDisabled={isDisabled || isCurrent}
className={({isFocused, isFocusVisible, isHovered, isDisabled, isPressed}) => linkStyles({isFocused, isFocusVisible, isHovered, isDisabled, size, isCurrent, isPressed})}>
{children}
</Link>
<ChevronIcon
size="M"
className={chevronStyles} />
</>
)
)}
</AriaBreadcrumb>
);
Expand Down
28 changes: 11 additions & 17 deletions packages/@react-spectrum/s2/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@
*/

import {baseColor, fontRelative, style} from '../style/spectrum-theme' with {type: 'macro'};
import {ButtonRenderProps, Link, LinkProps, OverlayTriggerStateContext, Provider, Button as RACButton, ButtonProps as RACButtonProps} from 'react-aria-components';
import {ButtonRenderProps, ContextValue, Link, LinkProps, OverlayTriggerStateContext, Provider, Button as RACButton, ButtonProps as RACButtonProps} from 'react-aria-components';
import {centerBaseline} from './CenterBaseline';
import {centerPadding, focusRing, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
import {createContext, forwardRef, ReactNode, useContext} from 'react';
import {FocusableRef} from '@react-types/shared';
import {FocusableRef, FocusableRefValue} from '@react-types/shared';
import {IconContext} from './Icon';
import {mergeProps} from 'react-aria';
import {pressScale} from './pressScale';
import {Text, TextContext} from './Content';
import {useFocusableRef} from '@react-spectrum/utils';
import {useSpectrumContextProps} from './useSpectrumContextProps';

interface ButtonStyleProps {
/**
Expand Down Expand Up @@ -55,12 +55,8 @@ export interface LinkButtonProps extends Omit<LinkProps, 'className' | 'style' |
children?: ReactNode
}

interface ButtonContextValue extends ButtonStyleProps, StyleProps {
/** Whether the Button is disabled. */
isDisabled?: boolean
}

export const ButtonContext = createContext<ButtonContextValue>({});
export const ButtonContext = createContext<ContextValue<ButtonProps, FocusableRefValue<HTMLButtonElement>>>(null);
export const LinkButtonContext = createContext<ContextValue<ButtonProps, FocusableRefValue<HTMLAnchorElement>>>(null);

const button = style<ButtonRenderProps & ButtonStyleProps>({
...focusRing(),
Expand Down Expand Up @@ -275,9 +271,8 @@ const button = style<ButtonRenderProps & ButtonStyleProps>({
}, getAllowedOverrides());

function Button(props: ButtonProps, ref: FocusableRef<HTMLButtonElement>) {
[props, ref] = useSpectrumContextProps(props, ref, ButtonContext);
let domRef = useFocusableRef(ref);
let ctx = useContext(ButtonContext);
props = mergeProps(ctx, props);
let overlayTriggerState = useContext(OverlayTriggerStateContext);

return (
Expand All @@ -296,9 +291,9 @@ function Button(props: ButtonProps, ref: FocusableRef<HTMLButtonElement>) {
}, props.styles)}>
<Provider
values={[
[TextContext, {className: style({paddingY: '--labelPadding', order: 1})}],
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1})}],
[IconContext, {
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
}]
]}>
Expand All @@ -317,9 +312,8 @@ let _Button = forwardRef(Button);
export {_Button as Button};

function LinkButton(props: LinkButtonProps, ref: FocusableRef<HTMLAnchorElement>) {
[props, ref] = useSpectrumContextProps(props, ref, LinkButtonContext);
let domRef = useFocusableRef(ref);
let ctx = useContext(ButtonContext);
props = mergeProps(ctx, props);
let overlayTriggerState = useContext(OverlayTriggerStateContext);

return (
Expand All @@ -338,9 +332,9 @@ function LinkButton(props: LinkButtonProps, ref: FocusableRef<HTMLAnchorElement>
}, props.styles)}>
<Provider
values={[
[TextContext, {className: style({paddingY: '--labelPadding', order: 1})}],
[TextContext, {styles: style({paddingY: '--labelPadding', order: 1})}],
[IconContext, {
render: centerBaseline({slot: 'icon', className: style({order: 0})}),
render: centerBaseline({slot: 'icon', styles: style({order: 0})}),
styles: style({size: fontRelative(20), marginStart: '--iconMargin', flexShrink: 0})
}]
]}>
Expand Down
Loading

0 comments on commit f30983a

Please sign in to comment.