From e77730587574f1a6b193f55a6170efa1abdf3523 Mon Sep 17 00:00:00 2001 From: Jess Telford Date: Thu, 5 Oct 2023 18:36:36 +1100 Subject: [PATCH] Support responsive syntax for all of 's style props. --- .changeset/cuddly-ants-reflect.md | 5 + polaris-react/src/components/Box/Box.scss | 133 ++------- polaris-react/src/components/Box/Box.tsx | 277 ++++++++---------- .../src/components/Modal/tests/Modal.test.tsx | 2 +- polaris-react/src/utilities/css.ts | 58 +++- 5 files changed, 218 insertions(+), 257 deletions(-) create mode 100644 .changeset/cuddly-ants-reflect.md diff --git a/.changeset/cuddly-ants-reflect.md b/.changeset/cuddly-ants-reflect.md new file mode 100644 index 00000000000..a82fb9e7a71 --- /dev/null +++ b/.changeset/cuddly-ants-reflect.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': minor +--- + +Support responsive syntax for all of 's style props. diff --git a/polaris-react/src/components/Box/Box.scss b/polaris-react/src/components/Box/Box.scss index a5ade896376..a3236e5d059 100644 --- a/polaris-react/src/components/Box/Box.scss +++ b/polaris-react/src/components/Box/Box.scss @@ -8,110 +8,41 @@ padding-inline-start: 0; } +/* prettier-ignore */ .Box { + @include responsive-props('box', 'background', 'background-color'); + @include responsive-props('box', 'border-block-end-width', 'border-block-end-width', 0); + @include responsive-props('box', 'border-block-start-width', 'border-block-start-width', 0); + @include responsive-props('box', 'border-color', 'border-color'); + @include responsive-props('box', 'border-end-end-radius', 'border-end-end-radius'); + @include responsive-props('box', 'border-end-start-radius', 'border-end-start-radius'); + @include responsive-props('box', 'border-inline-end-width', 'border-inline-end-width', 0); + @include responsive-props('box', 'border-inline-start-width', 'border-inline-start-width', 0); + @include responsive-props('box', 'border-start-end-radius', 'border-start-end-radius'); + @include responsive-props('box', 'border-start-start-radius', 'border-start-start-radius'); + @include responsive-props('box', 'border-style', 'border-style'); + @include responsive-props('box', 'color', 'color'); + @include responsive-props('box', 'inset-block-end', 'inset-block-end'); + @include responsive-props('box', 'inset-block-start', 'inset-block-start'); + @include responsive-props('box', 'inset-inline-end', 'inset-inline-end'); + @include responsive-props('box', 'inset-inline-start', 'inset-inline-start'); + @include responsive-props('box', 'max-width', 'max-width'); + @include responsive-props('box', 'min-height', 'min-height'); + @include responsive-props('box', 'min-width', 'min-width'); + @include responsive-props('box', 'outline-color', 'outline-color'); + @include responsive-props('box', 'outline-style', 'outline-style'); + @include responsive-props('box', 'outline-width', 'outline-width'); + @include responsive-props('box', 'overflow-x', 'overflow-x'); + @include responsive-props('box', 'overflow-y', 'overflow-y'); @include responsive-props('box', 'padding-block-end', 'padding-block-end'); - @include responsive-props( - 'box', - 'padding-block-start', - 'padding-block-start' - ); - @include responsive-props( - 'box', - 'padding-inline-start', - 'padding-inline-start' - ); + @include responsive-props('box', 'padding-block-start', 'padding-block-start'); @include responsive-props('box', 'padding-inline-end', 'padding-inline-end'); - - // stylelint-disable -- Polaris component custom properties - --pc-box-shadow: initial; - --pc-box-background: initial; - --pc-box-border-radius: initial; - --pc-box-border-end-start-radius: var(--pc-box-border-radius); - --pc-box-border-end-end-radius: var(--pc-box-border-radius); - --pc-box-border-start-start-radius: var(--pc-box-border-radius); - --pc-box-border-start-end-radius: var(--pc-box-border-radius); - --pc-box-color: initial; - --pc-box-min-height: initial; - --pc-box-min-width: initial; - --pc-box-max-width: initial; - --pc-box-outline-color: initial; - --pc-box-outline-style: initial; - --pc-box-outline-width: initial; - --pc-box-overflow-x: initial; - --pc-box-overflow-y: initial; - --pc-box-width: initial; - --pc-box-border-style: initial; - --pc-box-border-color: initial; - --pc-box-border-width: 0; - --pc-box-border-block-start-width: var(--pc-box-border-width); - --pc-box-border-block-end-width: var(--pc-box-border-width); - --pc-box-border-inline-start-width: var(--pc-box-border-width); - --pc-box-border-inline-end-width: var(--pc-box-border-width); - --pc-box-inset-block-start: initial; - --pc-box-inset-block-end: initial; - --pc-box-inset-inline-start: initial; - --pc-box-inset-inline-end: initial; - // stylelint-enable - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - inset-block-start: var(--pc-box-inset-block-start); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - inset-block-end: var(--pc-box-inset-block-end); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - inset-inline-start: var(--pc-box-inset-inline-start); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - inset-inline-end: var(--pc-box-inset-inline-end); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - background-color: var(--pc-box-background); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - box-shadow: var(--pc-box-shadow); - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-end-start-radius: var(--pc-box-border-end-start-radius); - // stylelint-enable - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-end-end-radius: var(--pc-box-border-end-end-radius); - // stylelint-enable - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-start-start-radius: var(--pc-box-border-start-start-radius); - // stylelint-enable - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-start-end-radius: var(--pc-box-border-start-end-radius); - // stylelint-enable - // stylelint-disable-next-line -- component custom property that maps to Polaris tokens - border-color: var(--pc-box-border-color); - // stylelint-disable-next-line -- component custom property that maps to Polaris tokens - border-style: var(--pc-box-border-style); - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-block-start-width: var(--pc-box-border-block-start-width); - // stylelint-enable - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-block-end-width: var(--pc-box-border-block-end-width); - // stylelint-enable - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-inline-start-width: var(--pc-box-border-inline-start-width); - // stylelint-enable - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - border-inline-end-width: var(--pc-box-border-inline-end-width); - // stylelint-enable - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - color: var(--pc-box-color); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - min-height: var(--pc-box-min-height); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - min-width: var(--pc-box-min-width); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - max-width: var(--pc-box-max-width); - // stylelint-disable-next-line -- component custom property that maps to Polaris tokens - outline-color: var(--pc-box-outline-color); - // stylelint-disable-next-line -- component custom property that maps to Polaris tokens - outline-style: var(--pc-box-outline-style); - // stylelint-disable-next-line -- component custom property that maps to Polaris tokens - outline-width: var(--pc-box-outline-width); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - overflow-x: var(--pc-box-overflow-x); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - overflow-y: var(--pc-box-overflow-y); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - width: var(--pc-box-width); + @include responsive-props('box', 'padding-inline-start', 'padding-inline-start'); + @include responsive-props('box', 'shadow', 'box-shadow'); + @include responsive-props('box', 'width', 'width'); + @include responsive-props('box', 'position', 'position'); + @include responsive-props('box', 'z-index', 'z-index'); + @include responsive-props('box', 'opacity', 'opacity'); -webkit-overflow-scrolling: touch; } diff --git a/polaris-react/src/components/Box/Box.tsx b/polaris-react/src/components/Box/Box.tsx index bda39bfdca6..983e776815e 100644 --- a/polaris-react/src/components/Box/Box.tsx +++ b/polaris-react/src/components/Box/Box.tsx @@ -11,10 +11,13 @@ import type { import { getResponsiveProps, + getResponsiveValue, + mapResponsivePropValues, + createPolarisCSSVar, classNames, sanitizeCustomProperties, } from '../../utilities/css'; -import type {ResponsiveProp} from '../../utilities/css'; +import type {ResponsiveProp, UnwrapResponsiveProp} from '../../utilities/css'; import styles from './Box.scss'; @@ -24,8 +27,6 @@ type LineStyles = 'solid' | 'dashed'; type Overflow = 'hidden' | 'scroll'; type Position = 'relative' | 'absolute' | 'fixed' | 'sticky'; -type Spacing = ResponsiveProp; - export interface BoxProps extends React.AriaAttributes { children?: React.ReactNode; /** HTML Element type @@ -33,111 +34,111 @@ export interface BoxProps extends React.AriaAttributes { */ as?: Element; /** Background color */ - background?: ColorBackgroundAlias; + background?: ResponsiveProp; /** Border color */ - borderColor?: ColorBorderAlias | 'transparent'; + borderColor?: ResponsiveProp; /** Border style */ - borderStyle?: LineStyles; + borderStyle?: ResponsiveProp; /** Border radius */ - borderRadius?: BorderRadiusAliasOrScale; + borderRadius?: ResponsiveProp; /** Vertical end horizontal start border radius */ - borderEndStartRadius?: BorderRadiusAliasOrScale; + borderEndStartRadius?: ResponsiveProp; /** Vertical end horizontal end border radius */ - borderEndEndRadius?: BorderRadiusAliasOrScale; + borderEndEndRadius?: ResponsiveProp; /** Vertical start horizontal start border radius */ - borderStartStartRadius?: BorderRadiusAliasOrScale; + borderStartStartRadius?: ResponsiveProp; /** Vertical start horizontal end border radius */ - borderStartEndRadius?: BorderRadiusAliasOrScale; + borderStartEndRadius?: ResponsiveProp; /** Border width */ - borderWidth?: BorderWidthScale; + borderWidth?: ResponsiveProp; /** Vertical start border width */ - borderBlockStartWidth?: BorderWidthScale; + borderBlockStartWidth?: ResponsiveProp; /** Vertical end border width */ - borderBlockEndWidth?: BorderWidthScale; + borderBlockEndWidth?: ResponsiveProp; /** Horizontal start border width */ - borderInlineStartWidth?: BorderWidthScale; + borderInlineStartWidth?: ResponsiveProp; /** Horizontal end border width */ - borderInlineEndWidth?: BorderWidthScale; + borderInlineEndWidth?: ResponsiveProp; /** Color of children */ - color?: ColorTextAlias; + color?: ResponsiveProp; /** HTML id attribute */ id?: string; /** Minimum height of container */ - minHeight?: string; + minHeight?: ResponsiveProp; /** Minimum width of container */ - minWidth?: string; + minWidth?: ResponsiveProp; /** Maximum width of container */ - maxWidth?: string; + maxWidth?: ResponsiveProp; /** Clip horizontal content of children */ - overflowX?: Overflow; + overflowX?: ResponsiveProp; /** Clip vertical content of children */ - overflowY?: Overflow; - /** Spacing around children. Accepts a spacing token or an object of spacing tokens for different screen sizes. + overflowY?: ResponsiveProp; + /** ResponsiveProp around children. Accepts a spacing token or an object of spacing tokens for different screen sizes. * @example * padding='400' * padding={{xs: '200', sm: '300', md: '400', lg: '500', xl: '600'}} */ - padding?: Spacing; + padding?: ResponsiveProp; /** Vertical start spacing around children. Accepts a spacing token or an object of spacing tokens for different screen sizes. * @example * paddingBlockStart='400' * paddingBlockStart={{xs: '200', sm: '300', md: '400', lg: '500', xl: '600'}} */ - paddingBlockStart?: Spacing; + paddingBlockStart?: ResponsiveProp; /** Vertical end spacing around children. Accepts a spacing token or an object of spacing tokens for different screen sizes. * @example * paddingBlockEnd='400' * paddingBlockEnd={{xs: '200', sm: '300', md: '400', lg: '500', xl: '600'}} */ - paddingBlockEnd?: Spacing; + paddingBlockEnd?: ResponsiveProp; /** Horizontal start spacing around children. Accepts a spacing token or an object of spacing tokens for different screen sizes. * @example * paddingInlineStart='400' * paddingInlineStart={{xs: '200', sm: '300', md: '400', lg: '500', xl: '600'}} */ - paddingInlineStart?: Spacing; + paddingInlineStart?: ResponsiveProp; /** Horizontal end spacing around children. Accepts a spacing token or an object of spacing tokens for different screen sizes. * @example * paddingInlineEnd='400' * paddingInlineEnd={{xs: '200', sm: '300', md: '400', lg: '500', xl: '600'}} */ - paddingInlineEnd?: Spacing; + paddingInlineEnd?: ResponsiveProp; /** Aria role */ role?: Extract< React.AriaRole, 'status' | 'presentation' | 'menu' | 'listbox' | 'combobox' >; /** Shadow on box */ - shadow?: ShadowAliasOrScale; + shadow?: ResponsiveProp; /** Set tab order */ tabIndex?: Extract['tabIndex'], number>; /** Width of container */ - width?: string; + width?: ResponsiveProp; // These could be moved to new layout component(s) in the future /** Position of box */ - position?: Position; + position?: ResponsiveProp; /** Top position of box */ - insetBlockStart?: Spacing; + insetBlockStart?: ResponsiveProp; /** Bottom position of box */ - insetBlockEnd?: Spacing; + insetBlockEnd?: ResponsiveProp; /** Left position of box */ - insetInlineStart?: Spacing; + insetInlineStart?: ResponsiveProp; /** Right position of box */ - insetInlineEnd?: Spacing; + insetInlineEnd?: ResponsiveProp; /** Opacity of box */ - opacity?: string; + opacity?: ResponsiveProp; /** Outline color */ - outlineColor?: ColorBorderAlias; + outlineColor?: ResponsiveProp; /** Outline style */ - outlineStyle?: LineStyles; + outlineStyle?: ResponsiveProp; /** Outline width */ - outlineWidth?: BorderWidthScale; + outlineWidth?: ResponsiveProp; /** Visually hide the contents during print */ printHidden?: boolean; /** Visually hide the contents (still announced by screenreader) */ visuallyHidden?: boolean; /** z-index of box */ - zIndex?: string; + zIndex?: ResponsiveProp; } export const Box = forwardRef( @@ -190,120 +191,100 @@ export const Box = forwardRef( }, ref, ) => { - // eslint-disable-next-line no-nested-ternary - const borderStyleValue = borderStyle - ? borderStyle - : borderColor || - borderWidth || - borderBlockStartWidth || - borderBlockEndWidth || - borderInlineStartWidth || - borderInlineEndWidth - ? 'solid' - : undefined; + const mapBorderStyle = ( + value: UnwrapResponsiveProp, + ) => { + // ignore null & undefined + if (value == null) { + return value; + } + // TODO: Make this responsive-aware so it only sets a value if borders are set for the current breakpoint + // eslint-disable-next-line no-nested-ternary + return borderStyle + ? borderStyle + : borderColor || + borderWidth || + borderBlockStartWidth || + borderBlockEndWidth || + borderInlineStartWidth || + borderInlineEndWidth + ? 'solid' + : undefined; + }; - // eslint-disable-next-line no-nested-ternary - const outlineStyleValue = outlineStyle - ? outlineStyle - : outlineColor || outlineWidth - ? 'solid' - : undefined; + const mapOutlineStyle = ( + value: UnwrapResponsiveProp, + ) => { + // ignore null & undefined + if (value == null) { + return value; + } - const style = { - '--pc-box-color': color ? `var(--p-color-${color})` : undefined, - '--pc-box-background': background - ? `var(--p-color-${background})` - : undefined, + // TODO: Make this responsive-aware so it only sets a value if borders are set for the current breakpoint // eslint-disable-next-line no-nested-ternary - '--pc-box-border-color': borderColor - ? borderColor === 'transparent' - ? 'transparent' - : `var(--p-color-${borderColor})` - : undefined, - '--pc-box-border-style': borderStyleValue, - '--pc-box-border-radius': borderRadius - ? `var(--p-border-radius-${borderRadius})` - : undefined, - '--pc-box-border-end-start-radius': borderEndStartRadius - ? `var(--p-border-radius-${borderEndStartRadius})` - : undefined, - '--pc-box-border-end-end-radius': borderEndEndRadius - ? `var(--p-border-radius-${borderEndEndRadius})` - : undefined, - '--pc-box-border-start-start-radius': borderStartStartRadius - ? `var(--p-border-radius-${borderStartStartRadius})` - : undefined, - '--pc-box-border-start-end-radius': borderStartEndRadius - ? `var(--p-border-radius-${borderStartEndRadius})` - : undefined, - '--pc-box-border-width': borderWidth - ? `var(--p-border-width-${borderWidth})` - : undefined, - '--pc-box-border-block-start-width': borderBlockStartWidth - ? `var(--p-border-width-${borderBlockStartWidth})` - : undefined, - '--pc-box-border-block-end-width': borderBlockEndWidth - ? `var(--p-border-width-${borderBlockEndWidth})` - : undefined, - '--pc-box-border-inline-start-width': borderInlineStartWidth - ? `var(--p-border-width-${borderInlineStartWidth})` - : undefined, - '--pc-box-border-inline-end-width': borderInlineEndWidth - ? `var(--p-border-width-${borderInlineEndWidth})` - : undefined, - '--pc-box-min-height': minHeight, - '--pc-box-min-width': minWidth, - '--pc-box-max-width': maxWidth, - '--pc-box-outline-color': outlineColor - ? `var(--p-color-${outlineColor})` - : undefined, - '--pc-box-outline-style': outlineStyleValue, - '--pc-box-outline-width': outlineWidth - ? `var(--p-border-width-${outlineWidth})` - : undefined, - '--pc-box-overflow-x': overflowX, - '--pc-box-overflow-y': overflowY, - ...getResponsiveProps( - 'box', - 'padding-block-end', - 'space', - paddingBlockEnd || padding, - ), - ...getResponsiveProps( - 'box', - 'padding-block-start', - 'space', - paddingBlockStart || padding, + return outlineStyle + ? outlineStyle + : outlineColor || outlineWidth + ? 'solid' + : undefined; + }; + + const mapBorderColor = ( + value: UnwrapResponsiveProp, + ) => { + // ignore null & undefined + if (value == null) { + return value; + } + return value === 'transparent' + ? 'transparent' + : createPolarisCSSVar('color', value); + }; + + // prettier-ignore + const style = { + ...getResponsiveProps('box', 'background', 'color', background), + ...getResponsiveProps('box', 'border-block-end-width', 'border-width', borderBlockEndWidth || borderWidth,), + ...getResponsiveProps('box', 'border-block-start-width', 'border-width', borderBlockStartWidth || borderWidth,), + ...mapResponsivePropValues( + getResponsiveValue('box', 'border-color', borderColor), + mapBorderColor, ), - ...getResponsiveProps( - 'box', - 'padding-inline-start', - 'space', - paddingInlineStart || padding, + ...getResponsiveProps('box', 'border-end-end-radius', 'border-radius', borderEndEndRadius || borderRadius,), + ...getResponsiveProps('box', 'border-end-start-radius', 'border-radius', borderEndStartRadius || borderRadius,), + ...getResponsiveProps('box', 'border-inline-end-width', 'border-width', borderInlineEndWidth || borderWidth,), + ...getResponsiveProps('box', 'border-inline-start-width', 'border-width', borderInlineStartWidth || borderWidth,), + ...getResponsiveProps('box', 'border-start-end-radius', 'border-radius', borderStartEndRadius || borderRadius,), + ...getResponsiveProps('box', 'border-start-start-radius', 'border-radius', borderStartStartRadius || borderRadius,), + ...mapResponsivePropValues( + getResponsiveValue('box', 'border-style', borderStyle), + mapBorderStyle, ), - ...getResponsiveProps( - 'box', - 'padding-inline-end', - 'space', - paddingInlineEnd || padding, + ...getResponsiveProps('box', 'color', 'color', color), + ...getResponsiveProps('box', 'inset-block-end', 'space', insetBlockEnd), + ...getResponsiveProps('box', 'inset-block-start', 'space', insetBlockStart,), + ...getResponsiveProps('box', 'inset-inline-end', 'space', insetInlineEnd), + ...getResponsiveProps('box', 'inset-inline-start', 'space', insetInlineStart,), + ...getResponsiveValue('box', 'max-width', maxWidth), + ...getResponsiveValue('box', 'min-height', minHeight), + ...getResponsiveValue('box', 'min-width', minWidth), + ...getResponsiveProps('box', 'outline-color', 'color', outlineColor), + ...mapResponsivePropValues( + getResponsiveValue('box', 'outline-style', outlineStyle), + mapOutlineStyle, ), - '--pc-box-shadow': shadow ? `var(--p-shadow-${shadow})` : undefined, - '--pc-box-width': width, - position, - '--pc-box-inset-block-start': insetBlockStart - ? `var(--p-space-${insetBlockStart})` - : undefined, - '--pc-box-inset-block-end': insetBlockEnd - ? `var(--p-space-${insetBlockEnd})` - : undefined, - '--pc-box-inset-inline-start': insetInlineStart - ? `var(--p-space-${insetInlineStart})` - : undefined, - '--pc-box-inset-inline-end': insetInlineEnd - ? `var(--p-space-${insetInlineEnd})` - : undefined, - zIndex, - opacity, + ...getResponsiveProps('box', 'outline-width', 'border-width', outlineWidth,), + ...getResponsiveValue('box', 'overflow-x', overflowX), + ...getResponsiveValue('box', 'overflow-y', overflowY), + ...getResponsiveProps('box', 'padding-block-end', 'space', paddingBlockEnd || padding,), + ...getResponsiveProps('box', 'padding-block-start', 'space', paddingBlockStart || padding,), + ...getResponsiveProps('box', 'padding-inline-end', 'space', paddingInlineEnd || padding,), + ...getResponsiveProps('box', 'padding-inline-start', 'space', paddingInlineStart || padding,), + ...getResponsiveProps('box', 'shadow', 'shadow', shadow), + ...getResponsiveValue('box', 'width', width), + ...getResponsiveValue('box', 'position', position), + ...getResponsiveValue('box', 'z-index', zIndex), + ...getResponsiveValue('box', 'opacity', opacity), } as React.CSSProperties; const className = classNames( diff --git a/polaris-react/src/components/Modal/tests/Modal.test.tsx b/polaris-react/src/components/Modal/tests/Modal.test.tsx index 3559e5ff69e..c3b2d6d2046 100644 --- a/polaris-react/src/components/Modal/tests/Modal.test.tsx +++ b/polaris-react/src/components/Modal/tests/Modal.test.tsx @@ -314,7 +314,7 @@ describe('', () => { expect(modal.find(Header)).toContainReactComponent('div', { style: expect.objectContaining({ - position: 'absolute', + '--pc-box-position-xs': 'absolute', }) as React.CSSProperties, }); expect(modal.find(Header)).not.toContainReactComponent(Text, { diff --git a/polaris-react/src/utilities/css.ts b/polaris-react/src/utilities/css.ts index 131a8731568..75b4df07784 100644 --- a/polaris-react/src/utilities/css.ts +++ b/polaris-react/src/utilities/css.ts @@ -11,6 +11,8 @@ export type ResponsivePropObject = { }; export type ResponsiveProp = T | ResponsivePropObject; +export type UnwrapResponsiveProp> = + C extends ResponsiveProp ? T : unknown; export type PolarisCSSCustomPropertyName = `${`--p-` | `--pc-`}${string}`; export type PolarisCSSVar = `var(${PolarisCSSCustomPropertyName})`; @@ -19,9 +21,11 @@ type ResponsiveCSSCustomProperties = { [Breakpoint in `${string}-${BreakpointsAlias}`]?: PolarisCSSVar; }; -type ResponsiveValues = { +export type ResponsiveValues = { [Breakpoint in `${string}-${BreakpointsAlias}`]?: T; }; +export type UnwrapResponsiveValues> = + C extends ResponsiveValues ? T : unknown; export function classNames(...classes: (string | Falsy)[]) { return classes.filter(Boolean).join(' '); @@ -170,17 +174,57 @@ export function getResponsiveValue( }; } -export function mapResponsivePropValues( - responsiveProp: ResponsiveProp | undefined, - fn: (value?: Input) => Output | undefined, -): ResponsiveProp | undefined { +/* +export function mapResponsivePropValues( + responsiveProp: Input | undefined, + fn: ( + value?: Input extends ResponsiveValues + ? V + : Input extends ResponsiveProp + ? P + : unknown, + ) => Output | undefined, +): Output extends ResponsiveValues + ? ResponsiveValues + : Output extends ResponsiveProp + ? ResponsiveProp + : unknown | undefined { + */ + +export function mapResponsivePropValues< + Input extends ResponsiveValues, + Output = unknown, +>( + responsiveProp: Input, + fn: (value?: UnwrapResponsiveValues) => Output | undefined, +): ResponsiveValues | undefined; +export function mapResponsivePropValues< + Input extends ResponsiveProp, + Output = unknown, +>( + responsiveProp: Input, + fn: (value?: UnwrapResponsiveProp) => Output | undefined, +): ResponsiveProp | undefined; +export function mapResponsivePropValues<_, Output>( + responsiveProp: unknown, + fn: (value?: unknown) => Output | undefined, +): unknown | undefined { if (isObject(responsiveProp)) { return Object.fromEntries( (Object.entries(responsiveProp) as Entries).map( ([breakpointAlias, value]) => [breakpointAlias, fn(value)], ), ); + } else { + /* + return fn( + responsiveProp as Input extends ResponsiveValues + ? V + : Input extends ResponsiveProp + ? P + : unknown, + ); + */ + return fn(responsiveProp); } - - return fn(responsiveProp as Input); }