diff --git a/change/@griffel-core-40e0578a-53de-40b8-9ed2-958d4fe70437.json b/change/@griffel-core-40e0578a-53de-40b8-9ed2-958d4fe70437.json new file mode 100644 index 000000000..ba5a2fe52 --- /dev/null +++ b/change/@griffel-core-40e0578a-53de-40b8-9ed2-958d4fe70437.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: add makeResetStyles to @griffel/core", + "packageName": "@griffel/core", + "email": "olfedias@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/e2e/typescript/src/assets/fixture-reset.ts b/e2e/typescript/src/assets/fixture-reset.ts new file mode 100644 index 000000000..1ba44ff00 --- /dev/null +++ b/e2e/typescript/src/assets/fixture-reset.ts @@ -0,0 +1,326 @@ +import { GriffelResetStyle } from '@griffel/core'; + +function assertType(style: GriffelResetStyle): GriffelResetStyle { + return style; +} + +// Animation +assertType({ animationName: 'foo' }); + +// Basic styles +// + +assertType({ flex: 0 }); +assertType({ flex: 1 }); +assertType({ zIndex: 0 }); +assertType({ zIndex: 1 }); + +assertType({ padding: '5px' }); +assertType({ paddingLeft: '5px' }); +assertType({ color: 'beige' }); + +// CSS variables +// + +assertType({ fontWeight: 'var(--foo)' }); +assertType({ flexShrink: 'var(--bar)' }); +assertType({ opacity: 'var(--baz)' }); +assertType({ zIndex: 'var(--qux)' }); + +assertType({ '--color': 'red' }); + +// Mixins +// + +assertType({ + ...{ padding: '5px' }, + ...{ paddingLeft: '5px' }, + ...{ color: 'red' }, +}); + +// Strict selector defined via "CSS.Pseudos" +// + +assertType({ + ':hover': { flexShrink: 0 }, + ':focus': { flexShrink: 'initial' }, + ':active': { zIndex: 0 }, + ':visited': { zIndex: 1 }, +}); + +assertType({ + ':hover': { fontWeight: 'var(--foo)' }, + ':focus': { flexShrink: 'var(--bar)' }, + ':active': { opacity: 'var(--bar)' }, + ':visited': { zIndex: 'var(--qux)' }, +}); + +assertType({ + ':hover': { color: 'beige' }, + ':active': { padding: '5px' }, + ':focus': { paddingLeft: '5px' }, +}); + +assertType({ + ':hover': { '--color': 'red' }, +}); + +// Custom selectors +// + +assertType({ + ':hover:focus': { flexShrink: 0 }, + ':hover:active': { flexShrink: 'initial' }, + ':hover:visited': { zIndex: 0 }, + ':hover:focus-visible': { zIndex: 1 }, +}); + +assertType({ + ':link:hover': { fontWeight: 'var(--foo)' }, + ':link:focus': { flexShrink: 'var(--bar)' }, + ':link:active': { opacity: 'var(--bar)' }, + ':link:visited': { zIndex: 'var(--qux)' }, +}); + +assertType({ + '.bar': { color: 'beige' }, + '.foo': { padding: '5px' }, + '.qux': { paddingLeft: '5px' }, +}); + +assertType({ + '.bar': { '--color': 'red' }, +}); + +// Nested custom selectors +// + +assertType({ + '.foo': { + '.bar': { flexShrink: 0 }, + '.baz': { flexShrink: 'initial' }, + '.qux': { opacity: 0 }, + '.fred': { zIndex: 0 }, + '.thud': { zIndex: 1 }, + }, + '.bar': { + '.baz': { color: 'beige' }, + '.qux': { paddingLeft: '5px' }, + }, + '.baz': { + '.qux': { + '--color': 'red', + }, + }, + '.qux': { + '.bar': { flexShrink: 'var(--bar)' }, + '.baz': { opacity: 'var(--baz)' }, + }, +}); + +// Invalid values +// + +assertType({ + // @ts-expect-error "outline-box" is an invalid value for "box-sizing" + boxSizing: 'outline-box', +}); +assertType({ + // @ts-expect-error "1" is invalid value for "overflowX" + overflowX: '1', +}); +assertType({ + // @ts-expect-error "paddingLeft" cannot be numeric value + paddingLeft: 5, +}); +assertType({ + // @ts-expect-error "0" is invalid value for "color" + color: 0, +}); + +assertType({ + // @ts-expect-error type check still fails on outline-box, not on any other line + boxSizing: 'outline-box', + zIndex: 1, +}); +assertType({ + zIndex: 1, + // @ts-expect-error type check still fails on outline-box, not on any other line + boxSizing: 'outline-box', +}); + +assertType({ + // @ts-expect-error Object is not assignable to CSS property + zIndex: { color: 'red' }, + // @ts-expect-error Object is not assignable to CSS property + opacity: { color: 'red' }, +}); + +// Mixins with invalid values +// + +// @ts-expect-error Object in "zIndex" is not assignable to CSS property +assertType({ + ...{ zIndex: { color: 'red' } }, + ...{ color: 'red' }, +}); +// @ts-expect-error "outline-box" in "boxSizing" is an invalid value for "box-sizing" +assertType({ + ...{ boxSizing: 'outline-box' }, + ...{ color: 'red' }, +}); + +// Just a type check, deep objects are not expected to be used as style mixins? +const typedMixin: GriffelResetStyle = { + marginLeft: '5px', + ':hover': { + marginLeft: '6px', + '--customColor': 'blue', + }, + '--customColor': 'silver', +}; + +assertType({ + ...typedMixin, + color: 'var(--customColor)', +}); + +assertType({ + '@media screen and (max-width: 992px)': { + ...typedMixin, + }, +}); + +// Strict selectors +// + +assertType({ + ':hover': { + // @ts-expect-error "1" is invalid value for "overflowX" + overflowX: '1', + // @ts-expect-error "paddingLeft" cannot be numeric value + paddingLeft: 5, + }, +}); +assertType({ + ':hover': { + // @ts-expect-error outline-box is an invalid value for box-sizing + boxSizing: 'outline-box', + zIndex: 1, + }, +}); + +// Custom selectors +// + +assertType({ + // @ts-expect-error "1" is invalid value for "overflowX" + ':hover:focus': { + overflowX: '1', + }, +}); + +assertType({ + // @ts-expect-error "paddingLeft" cannot be numeric value + ':hover:focus': { + paddingLeft: 5, + }, +}); + +assertType({ + // @ts-expect-error "1" is invalid value for "overflowX", padding is banned, paddingLeft cannot be a numeric value + ':hover:focus': { + overflowX: 'scroll', + padding: 0, + paddingLeft: 5, + }, +}); + +assertType({ + ':hover:focus': { + // @ts-expect-error Object is not assignable to CSS property + zIndex: { color: 'red' }, + opacity: { color: 'red' }, // < no error here, TS only reports the first error in this case + }, + ':hover:active': { + // @ts-expect-error Object is not assignable to CSS property + opacity: { color: 'red' }, + zIndex: { color: 'red' }, // < no error here, TS only reports the first error in this case + }, +}); + +// Nested custom selectors +// + +assertType({ + // @ts-expect-error "1" is invalid value for "overflowX", padding is banned, paddingLeft cannot be a numeric value + '.foo': { + '.baz': { + overflowX: '1', + padding: 0, + paddingLeft: 5, + }, + }, +}); +assertType({ + // @ts-expect-error outline-box is an invalid value for box-sizing + '.foo': { + boxSizing: 'outline-box', + + '.bar': { + boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object + }, + }, +}); +assertType({ + // @ts-expect-error outline-box is an invalid value for box-sizing + '.foo': { + boxSizing: 'outline-box', + zIndex: 1, + + '.bar': { + boxSizing: 'outline-box', // < no error here, TS only reports the error for the whole object + zIndex: 1, + }, + }, +}); +assertType({ + '.foo': { + // @ts-expect-error Object is not assignable to CSS property + zIndex: { color: 'red' }, + opacity: { color: 'red' }, // < no error here, TS only reports the first error in this case + + '.bar': { + zIndex: { color: 'red' }, // < no error here, TS only reports the first error in this case + opacity: { color: 'red' }, // < no error here, TS only reports the first error in this case + }, + }, +}); + +// Fallback values +assertType({ + color: ['red', 'blue'], + padding: [0, '2px'], + paddingLeft: [0, '2px'], + zIndex: [10, 20], + ':hover': { + color: ['red', 'blue'], + }, + ':hover:active': { + zIndex: [2], + color: ['red', 'blue'], + paddingLeft: [0, '2px'], + }, +}); + +assertType({ + paddingLeft: [ + // @ts-expect-error "paddingLeft" cannot be numeric value + 2, + '2px', + ], + // @ts-expect-error "paddingLeft" cannot be numeric value + ':hover:active': { + paddingLeft: ['2px', 2], + }, +}); diff --git a/e2e/typescript/src/test.mjs b/e2e/typescript/src/test.mjs index dc0769151..4bc793356 100644 --- a/e2e/typescript/src/test.mjs +++ b/e2e/typescript/src/test.mjs @@ -77,6 +77,7 @@ async function performTest(tsVersion) { console.log(logSymbols.success, 'Package files were packed'); await fs.promises.copyFile(path.resolve(assetsPath, 'fixture.ts'), path.join(tempDir, 'fixture.ts')); + await fs.promises.copyFile(path.resolve(assetsPath, 'fixture-reset.ts'), path.join(tempDir, 'fixture-reset.ts')); await fs.promises.copyFile(path.resolve(assetsPath, 'tsconfig.fixture.json'), path.join(tempDir, 'tsconfig.json')); // Reuse the same cache directory to speed up install and avoid network requests diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 21414f56e..77f71ea3c 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -6,6 +6,9 @@ export const DATA_BUCKET_ATTR = 'data-make-styles-bucket'; /** @internal */ export const HASH_PREFIX = 'f'; +/** @internal */ +export const RESET_HASH_PREFIX = 'r'; + /** @internal */ export const SEQUENCE_HASH_LENGTH = 7; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index aa817dbf9..447328f51 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -47,19 +47,23 @@ export type { CreateDOMRendererOptions } from './renderer/createDOMRenderer'; export { rehydrateRendererCache } from './renderer/rehydrateRendererCache'; export { mergeClasses } from './mergeClasses'; -export { makeStaticStyles } from './makeStaticStyles'; export { makeStyles } from './makeStyles'; +export { makeStaticStyles } from './makeStaticStyles'; +export { makeResetStyles } from './makeResetStyles'; + export { resolveStyleRulesForSlots } from './resolveStyleRulesForSlots'; // Private exports, are used by build time transforms or other tools export { __css } from './__css'; +export { __styles } from './__styles'; + export { normalizeCSSBucketEntry } from './runtime/utils/normalizeCSSBucketEntry'; export { styleBucketOrdering } from './renderer/getStyleSheetForBucket'; export { defaultCompareMediaQueries } from './renderer/createDOMRenderer'; export { getStyleBucketName } from './runtime/getStyleBucketName'; export { reduceToClassNameForSlots } from './runtime/reduceToClassNameForSlots'; export { resolveStyleRules } from './runtime/resolveStyleRules'; -export { __styles } from './__styles'; +export { resolveResetStyleRules } from './runtime/resolveResetStyleRules'; export * from './constants'; export type { @@ -69,6 +73,8 @@ export type { // Styles GriffelAnimation, GriffelStyle, + // Reset styles + GriffelResetStyle, // Internal types CSSClasses, CSSClassesMapBySlot, diff --git a/packages/core/src/makeResetStyles.test.ts b/packages/core/src/makeResetStyles.test.ts new file mode 100644 index 000000000..65c463d28 --- /dev/null +++ b/packages/core/src/makeResetStyles.test.ts @@ -0,0 +1,36 @@ +import { griffelRendererSerializer } from './common/snapshotSerializers'; +import { createDOMRenderer } from './renderer/createDOMRenderer'; +import { makeResetStyles } from './makeResetStyles'; +import { GriffelRenderer } from './types'; + +expect.addSnapshotSerializer(griffelRendererSerializer); + +describe('makeResetStyles', () => { + let renderer: GriffelRenderer; + + beforeEach(() => { + process.env.NODE_ENV = 'production'; + renderer = createDOMRenderer(document); + }); + + afterEach(() => { + document.head.innerHTML = ''; + }); + + it('returns a single classname for a single style', () => { + const computeClassName = makeResetStyles({ + color: 'red', + flexDirection: 'row', + }); + + expect(computeClassName({ dir: 'ltr', renderer })).toEqual('r7lmmpp'); + expect(renderer).toMatchInlineSnapshot(` + .r7lmmpp { + color: red; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + } + `); + }); +}); diff --git a/packages/core/src/makeResetStyles.ts b/packages/core/src/makeResetStyles.ts new file mode 100644 index 000000000..717b28f35 --- /dev/null +++ b/packages/core/src/makeResetStyles.ts @@ -0,0 +1,35 @@ +import { resolveResetStyleRules } from './runtime/resolveResetStyleRules'; +import type { GriffelResetStyle, MakeStylesOptions } from './types'; + +/** + * @internal + */ +export function makeResetStyles(styles: GriffelResetStyle) { + const insertionCache: Record = {}; + + let ltrClassName: string | null = null; + let rtlClassName: string | null = null; + + let cssRules: string[] | null = null; + + function computeClassName(options: MakeStylesOptions): string { + const { dir, renderer } = options; + + if (ltrClassName === null) { + [ltrClassName, rtlClassName, cssRules] = resolveResetStyleRules(styles); + } + + const isLTR = dir === 'ltr'; + // As RTL classes are different they should have a different cache key for insertion + const rendererId = isLTR ? renderer.id : renderer.id + 'r'; + + if (insertionCache[rendererId] === undefined) { + renderer.insertCSSRules({ r: cssRules! }); + insertionCache[rendererId] = true; + } + + return isLTR ? ltrClassName : rtlClassName || ltrClassName; + } + + return computeClassName; +} diff --git a/packages/core/src/renderer/getStyleSheetForBucket.test.ts b/packages/core/src/renderer/getStyleSheetForBucket.test.ts index 187611189..d71a14dc5 100644 --- a/packages/core/src/renderer/getStyleSheetForBucket.test.ts +++ b/packages/core/src/renderer/getStyleSheetForBucket.test.ts @@ -14,6 +14,7 @@ describe('getStyleSheetForBucket', () => { const target = createFakeDocument(); const renderer = createDOMRenderer(); + getStyleSheetForBucket('r', target, renderer); getStyleSheetForBucket('l', target, renderer); getStyleSheetForBucket('d', target, renderer); getStyleSheetForBucket('v', target, renderer); diff --git a/packages/core/src/renderer/getStyleSheetForBucket.ts b/packages/core/src/renderer/getStyleSheetForBucket.ts index 3abc69c8a..d93789c9d 100644 --- a/packages/core/src/renderer/getStyleSheetForBucket.ts +++ b/packages/core/src/renderer/getStyleSheetForBucket.ts @@ -8,6 +8,8 @@ import { createIsomorphicStyleSheet } from './createIsomorphicStyleSheet'; * @internal */ export const styleBucketOrdering: StyleBucketName[] = [ + // reset styles + 'r', // catch-all 'd', // link diff --git a/packages/core/src/runtime/resolveResetStyleRules.test.ts b/packages/core/src/runtime/resolveResetStyleRules.test.ts new file mode 100644 index 000000000..8d313c302 --- /dev/null +++ b/packages/core/src/runtime/resolveResetStyleRules.test.ts @@ -0,0 +1,210 @@ +import { resolveResetStyleRules } from './resolveResetStyleRules'; + +describe('resolveResetStyleRules', () => { + it('handles base rules', () => { + const result = resolveResetStyleRules({ + color: 'red', + overflowX: 'hidden', + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "r11y0rml", + null, + Array [ + ".r11y0rml{color:red;overflow-x:hidden;}", + ], + ] + `); + }); + + it('handles RTL', () => { + const result = resolveResetStyleRules({ marginLeft: '15px' }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "rovwgyn", + "rj5b9iu", + Array [ + ".rovwgyn{margin-left:15px;}", + ".rj5b9iu{margin-right:15px;}", + ], + ] + `); + }); + + it('handles fallback values', () => { + const result = resolveResetStyleRules({ + color: ['red', 'blue'], + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "rj1urkn", + null, + Array [ + ".rj1urkn{color:red;color:blue;}", + ], + ] + `); + }); + + it('handles :global() selector', () => { + expect( + resolveResetStyleRules({ + color: 'red', + ':global(body)': { color: 'magenta' }, + }), + ).toMatchInlineSnapshot(` + Array [ + "rzlpwqs", + null, + Array [ + ".rzlpwqs{color:red;}", + "body .rzlpwqs{color:magenta;}", + ], + ] + `); + + expect( + resolveResetStyleRules({ + ':global(body)': { + color: 'magenta', + ':focus': { color: 'pink' }, + }, + }), + ).toMatchInlineSnapshot(` + Array [ + "r1i1zh9k", + null, + Array [ + "body .r1i1zh9k{color:magenta;}", + "body .r1i1zh9k:focus{color:pink;}", + ], + ] + `); + + expect( + resolveResetStyleRules({ + ':global(.fui-FluentProvider)': { + '& .foo': { color: 'orange' }, + }, + }), + ).toMatchInlineSnapshot(` + Array [ + "rmi35r5", + null, + Array [ + ".fui-FluentProvider .rmi35r5 .foo{color:orange;}", + ], + ] + `); + }); + + it('handles media queries', () => { + const result = resolveResetStyleRules({ + color: 'red', + '@media (forced-colors: active)': { + color: 'orange', + ':focus': { + color: 'yellow', + }, + }, + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "rpycl1b", + null, + Array [ + ".rpycl1b{color:red;}", + "@media (forced-colors: active){.rpycl1b{color:orange;}.rpycl1b:focus{color:yellow;}}", + ], + ] + `); + }); + + it('handles layer queries', () => { + const result = resolveResetStyleRules({ + '@layer utilities': { + color: 'orange', + ':focus': { + color: 'yellow', + }, + }, + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "rvhnavh", + null, + Array [ + "@layer utilities{color:orange;:focus{color:yellow;}}", + ], + ] + `); + }); + + it('handles support queries', () => { + const result = resolveResetStyleRules({ + '@supports (display: flex)': { + color: 'orange', + ':focus': { + color: 'yellow', + }, + }, + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "rxf8lon", + null, + Array [ + "@supports (display: flex){.rxf8lon{color:orange;}.rxf8lon:focus{color:yellow;}}", + ], + ] + `); + }); + + it('handles nested queries queries', () => { + const result = resolveResetStyleRules({ + '@supports (display: flex)': { + color: 'pink', + + '@media (forced-colors: active)': { + color: 'orange', + }, + }, + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "rhd25ja", + null, + Array [ + "@supports (display: flex){.rhd25ja{color:pink;}@media (forced-colors: active){.rhd25ja{color:orange;}}}", + ], + ] + `); + }); + + it('handles nested selectors', () => { + const result = resolveResetStyleRules({ + ':hover': { color: 'red' }, + '& :focus': { color: 'red' }, + '&.foo': { color: 'red' }, + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + "r1s1f2pl", + null, + Array [ + ".r1s1f2pl:hover{color:red;}", + ".r1s1f2pl :focus{color:red;}", + ".r1s1f2pl.foo{color:red;}", + ], + ] + `); + }); +}); diff --git a/packages/core/src/runtime/resolveResetStyleRules.ts b/packages/core/src/runtime/resolveResetStyleRules.ts new file mode 100644 index 000000000..ee3173c42 --- /dev/null +++ b/packages/core/src/runtime/resolveResetStyleRules.ts @@ -0,0 +1,122 @@ +import hashString from '@emotion/hash'; +import { convertProperty } from 'rtl-css-js/core'; + +import { RESET_HASH_PREFIX } from '../constants'; +import { GriffelStyle, GriffelResetStyle } from '../types'; +import { compileCSSRules, normalizePseudoSelector } from './compileCSS'; +import { isMediaQuerySelector } from './utils/isMediaQuerySelector'; +import { isLayerSelector } from './utils/isLayerSelector'; +import { isNestedSelector } from './utils/isNestedSelector'; +import { isSupportQuerySelector } from './utils/isSupportQuerySelector'; +import { isObject } from './utils/isObject'; +import { hyphenateProperty } from './utils/hyphenateProperty'; + +/** + * @internal + */ +function createStringFromStyles(styles: GriffelResetStyle) { + let ltrCSS = ''; + let rtlCSS = ''; + + // eslint-disable-next-line guard-for-in + for (const property in styles) { + const value = styles[property as keyof GriffelStyle]; + + // eslint-disable-next-line eqeqeq + if (value == null) { + continue; + } + + if (typeof value === 'string' || typeof value === 'number') { + const { key: rtlProperty, value: rtlValue } = convertProperty(property, value); + + ltrCSS += `${hyphenateProperty(property)}:${value};`; + rtlCSS += `${hyphenateProperty(rtlProperty)}:${rtlValue};`; + + continue; + } + + if (property === 'animationName') { + // TODO: handle animations + throw new Error(); + } + + if (Array.isArray(value)) { + // not animationName property but array in the value => fallback values + if (value.length === 0) { + if (process.env.NODE_ENV !== 'production') { + console.warn( + `makeResetStyles(): An empty array was passed as input to "${property}", the property will be omitted in the styles.`, + ); + } + continue; + } + + const rtlDefinitions = value.map(v => convertProperty(property, v!)); + const rtlPropertyConsistent = !rtlDefinitions.some(v => v.key !== rtlDefinitions[0].key); + + if (!rtlPropertyConsistent) { + if (process.env.NODE_ENV !== 'production') { + console.error( + 'makeStyles(): mixing CSS fallback values which result in multiple CSS properties in RTL is not supported.', + ); + } + continue; + } + + const rtlProperty = rtlDefinitions[0].key; + + ltrCSS += value.map(v => `${hyphenateProperty(property)}:${v};`).join(''); + rtlCSS += rtlDefinitions.map(definition => `${hyphenateProperty(rtlProperty)}:${definition.value};`).join(''); + + continue; + } + + if (isObject(value)) { + if (isNestedSelector(property)) { + const nestedSelector = normalizePseudoSelector(property); + const [ltrNested, rtlNested] = createStringFromStyles(value); + + ltrCSS += `${nestedSelector}{${ltrNested}}`; + rtlCSS += `${nestedSelector}{${rtlNested}}`; + + continue; + } + + if (isMediaQuerySelector(property) || isLayerSelector(property) || isSupportQuerySelector(property)) { + const [ltrNested, rtlNested] = createStringFromStyles(value); + + ltrCSS += `${property}{${ltrNested}}`; + rtlCSS += `${property}{${rtlNested}}`; + + continue; + } + } + + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line no-console + console.error(`Please fix the unresolved style rule: \n ${property} \n ${JSON.stringify(value, null, 2)}"`); + } + } + + return [ltrCSS, rtlCSS]; +} + +/** + * @internal + */ +export function resolveResetStyleRules(styles: GriffelResetStyle): [string, string | null, string[]] { + const [ltrRule, rtlRule] = createStringFromStyles(styles); + + const ltrClassName = RESET_HASH_PREFIX + hashString(ltrRule); + const ltrCSS = compileCSSRules(`.${ltrClassName}{${ltrRule}}`); + + if (ltrRule === rtlRule) { + return [ltrClassName, null, ltrCSS]; + } + + const rtlClassName = RESET_HASH_PREFIX + hashString(rtlRule); + const rtlCSS = compileCSSRules(`.${rtlClassName}{${rtlRule}}`); + + return [ltrClassName, rtlClassName, ltrCSS.concat(rtlCSS)]; +} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ccff5fc44..ff9260fbb 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -151,13 +151,10 @@ type GriffelStylesCSSObjectCustomL5 = { } & GriffelStylesStrictCSSObject; export type GriffelStyle = GriffelStylesStrictCSSObject | GriffelStylesCSSObjectCustomL1; - export type GriffelAnimation = Record<'from' | 'to' | string, GriffelStylesCSSObjectCustomL1>; -export interface MakeStylesOptions { - dir: 'ltr' | 'rtl'; - renderer: GriffelRenderer; -} +// Types for makeStaticStyles() +// --- export type GriffelStaticStyle = { [key: string]: CSS.Properties & @@ -180,6 +177,61 @@ export type GriffelStaticStyle = { }; export type GriffelStaticStyles = GriffelStaticStyle | string; +// Types for makeResetStyles() +// --- + +type GriffelResetStylesCSSProperties = Omit< + CSS.PropertiesFallback>, + // We have custom definition for "animationName" + 'animationName' +>; + +type GriffelResetStylesStrictCSSObject = GriffelResetStylesCSSProperties & + GriffelResetStylesCSSPseudos & { + animationName?: GriffelAnimation | GriffelAnimation[] | CSS.Property.Animation; + }; + +type GriffelResetStylesCSSPseudos = { + [Property in CSS.Pseudos]?: + | (GriffelResetStylesStrictCSSObject & { content?: string | string[] }) + | (GriffelResetStylesCSSObjectCustomL1 & { content?: string | string[] }); +}; + +// +// "GriffelStylesCSSObjectCustom*" is a workaround to avoid circular references in types that are breaking TS <4. +// Once we will support "typesVersions" (types downleleving) or update our requirements for TS this should be +// updated or removed. +// + +type GriffelResetStylesCSSObjectCustomL1 = { + [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL2; +} & GriffelResetStylesStrictCSSObject; + +type GriffelResetStylesCSSObjectCustomL2 = { + [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL3; +} & GriffelResetStylesStrictCSSObject; + +type GriffelResetStylesCSSObjectCustomL3 = { + [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL4; +} & GriffelResetStylesStrictCSSObject; + +type GriffelResetStylesCSSObjectCustomL4 = { + [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesCSSObjectCustomL5; +} & GriffelResetStylesStrictCSSObject; + +type GriffelResetStylesCSSObjectCustomL5 = { + [Property: string]: string | number | (string | number)[] | undefined | GriffelResetStylesStrictCSSObject; +} & GriffelResetStylesStrictCSSObject; + +export type GriffelResetStyle = GriffelResetStylesStrictCSSObject | GriffelResetStylesCSSObjectCustomL1; + +// --- + +export interface MakeStylesOptions { + dir: 'ltr' | 'rtl'; + renderer: GriffelRenderer; +} + export interface MakeStaticStylesOptions { renderer: GriffelRenderer; } @@ -238,6 +290,8 @@ export type CSSClassesMap = Record; export type CSSClassesMapBySlot = Record; export type CSSRulesByBucket = { + // reset + r?: CSSBucketEntry[]; // default d?: CSSBucketEntry[]; // link