@@ -12,9 +12,14 @@ export type ResponsivePropObject<T = string> = {
1212
1313export type ResponsiveProp < T = string > = T | ResponsivePropObject < T > ;
1414
15- export type ResponsiveValue < T = string > = undefined | ResponsiveProp < T > ;
15+ export type PolarisCSSCustomPropertyName = `${`--p-` | `--pc-`} ${string } `;
16+ export type PolarisCSSVar = `var(${PolarisCSSCustomPropertyName } )`;
1617
17- type ResponsiveVariables < T > = {
18+ type ResponsiveCSSCustomProperties = {
19+ [ Breakpoint in `${string } -${BreakpointsAlias } `] ?: PolarisCSSVar ;
20+ } ;
21+
22+ type ResponsiveValues < T > = {
1823 [ Breakpoint in `${string } -${BreakpointsAlias } `] ?: T ;
1924} ;
2025
@@ -36,6 +41,41 @@ export function sanitizeCustomProperties(
3641 return nonNullValues . length ? Object . fromEntries ( nonNullValues ) : undefined ;
3742}
3843
44+ export function createPolarisCSSVar < T extends string | number = string > (
45+ tokenSubgroup : string ,
46+ tokenValue : T ,
47+ ) : PolarisCSSVar {
48+ // For backwards compatibility with `Grid` and `Grid.Cell`, accept already
49+ // formed var()'s using either polaris or polaris component custom properties.
50+ if ( typeof tokenValue === 'string' && tokenValue . startsWith ( 'var(' ) ) {
51+ if (
52+ ! tokenValue . startsWith ( `var(--p-${ tokenSubgroup } -` ) &&
53+ ! tokenValue . startsWith ( `var(--pc-${ tokenSubgroup } -` )
54+ ) {
55+ throw new Error (
56+ `"${ tokenValue } " is not from the ${ tokenSubgroup } token group.` ,
57+ ) ;
58+ }
59+
60+ return tokenValue as PolarisCSSVar ;
61+ }
62+
63+ // NOTE: All our token values today are either strings or numbers, so
64+ // stringifying them here works. But if we ever have anything more complex
65+ // (such as an object) this may generate invalid token names.
66+ return `var(--p-${ tokenSubgroup } -${ tokenValue } )` ;
67+ }
68+
69+ export function createPolarisCSSCustomProperty (
70+ componentName : string ,
71+ componentProp : string ,
72+ breakpointAlias ?: BreakpointsAlias ,
73+ ) : PolarisCSSCustomPropertyName {
74+ return `--pc-${ componentName } -${ componentProp } ${
75+ breakpointAlias ? `-${ breakpointAlias } ` : ''
76+ } `;
77+ }
78+
3979/**
4080 * Given params like so:
4181 * (
@@ -53,59 +93,88 @@ export function sanitizeCustomProperties(
5393 * '--pc-button-padding-lg': 'var(--p-spacing-6)'
5494 * }
5595 *
96+ * NOTE: Also supports legacy / deprecated values which are a complete CSS
97+ * variable declaration (primarily for `Grid`):
98+ * (
99+ * 'grid',
100+ * 'gap',
101+ * 'spacing',
102+ * {
103+ * sm: "var(--p-spacing-4)",
104+ * lg: "var(--p-spacing-6)"
105+ * }
106+ * )
107+ *
56108 */
57- export function getResponsiveProps < T = string > (
109+ export function getResponsiveProps < T extends string | number = string > (
58110 componentName : string ,
59111 componentProp : string ,
60112 tokenSubgroup : string ,
61113 responsiveProp ?: ResponsiveProp < T > ,
62- ) : ResponsiveVariables < T > {
63- if ( ! responsiveProp ) return { } ;
64-
65- let result : ResponsivePropConfig ;
66-
67- if ( ! isObject ( responsiveProp ) ) {
68- result = {
69- [ breakpointsAliases [ 0 ] ] : `var(--p-${ tokenSubgroup } -${ responsiveProp } )` ,
70- } ;
71- } else {
72- result = Object . fromEntries (
73- Object . entries ( responsiveProp ) . map ( ( [ breakpointAlias , aliasOrScale ] ) => [
74- breakpointAlias ,
75- `var(--p-${ tokenSubgroup } -${ aliasOrScale } )` ,
114+ ) : ResponsiveCSSCustomProperties {
115+ // "falsey" values are valid except `null` or `undefined`
116+ if ( responsiveProp == null ) return { } ;
117+
118+ if ( isObject ( responsiveProp ) ) {
119+ return Object . fromEntries (
120+ (
121+ Object . entries ( responsiveProp ) . filter (
122+ ( [ , aliasOrScale ] ) => aliasOrScale != null ,
123+ // Use 'Required' here because .filter() doesn't type narrow
124+ ) as Entries < Required < typeof responsiveProp > >
125+ ) . map ( ( [ breakpointAlias , aliasOrScale ] ) => [
126+ createPolarisCSSCustomProperty (
127+ componentName ,
128+ componentProp ,
129+ breakpointAlias ,
130+ ) ,
131+ createPolarisCSSVar ( tokenSubgroup , aliasOrScale ) ,
76132 ] ) ,
77133 ) ;
78134 }
79135
80- // Prefix each responsive key with the correct token name
81- return Object . fromEntries (
82- Object . entries ( result ) . map ( ( [ breakpointAlias , value ] ) => [
83- `--pc- ${ componentName } - ${ componentProp } - ${ breakpointAlias } ` ,
84- value ,
85- ] ) ,
86- ) as unknown as ResponsiveVariables < T > ;
136+ return {
137+ [ createPolarisCSSCustomProperty (
138+ componentName ,
139+ componentProp ,
140+ breakpointsAliases [ 0 ] ,
141+ ) ] : createPolarisCSSVar ( tokenSubgroup , responsiveProp as T ) ,
142+ } ;
87143}
88144
89- export function getResponsiveValue < T = string > (
145+ export function getResponsiveValue < T extends string | number = string > (
90146 componentName : string ,
91147 componentProp : string ,
92- responsiveProp ?: ResponsiveValue < T > ,
93- ) : ResponsiveVariables < T > {
94- if ( ! responsiveProp ) return { } ;
95-
96- if ( ! isObject ( responsiveProp ) ) {
97- return {
98- [ `--pc-${ componentName } -${ componentProp } -${ breakpointsAliases [ 0 ] } ` ] :
99- responsiveProp ,
100- } as ResponsiveVariables < T > ;
148+ responsiveProp ?: ResponsiveProp < T > ,
149+ ) : ResponsiveValues < T > {
150+ // "falsey" values are valid except `null` or `undefined`
151+ if ( responsiveProp == null ) return { } ;
152+
153+ if ( isObject ( responsiveProp ) ) {
154+ return Object . fromEntries (
155+ (
156+ Object . entries ( responsiveProp ) . filter (
157+ ( [ , responsiveValue ] ) => responsiveValue != null ,
158+ // Use 'Required' here because .filter() doesn't type narrow
159+ ) as Entries < Required < typeof responsiveProp > >
160+ ) . map ( ( [ breakpointAlias , responsiveValue ] ) => [
161+ createPolarisCSSCustomProperty (
162+ componentName ,
163+ componentProp ,
164+ breakpointAlias ,
165+ ) ,
166+ responsiveValue ,
167+ ] ) ,
168+ ) ;
101169 }
102170
103- return Object . fromEntries (
104- Object . entries ( responsiveProp ) . map ( ( [ breakpointAlias , responsiveValue ] ) => [
105- `--pc-${ componentName } -${ componentProp } -${ breakpointAlias } ` ,
106- responsiveValue ,
107- ] ) ,
108- ) ;
171+ return {
172+ [ createPolarisCSSCustomProperty (
173+ componentName ,
174+ componentProp ,
175+ breakpointsAliases [ 0 ] ,
176+ ) ] : responsiveProp as T ,
177+ } ;
109178}
110179
111180export function mapResponsivePropValues < Input , Output > (
0 commit comments