Skip to content

Commit

Permalink
[system] Add createTheme util (#26490)
Browse files Browse the repository at this point in the history
  • Loading branch information
mnajdova authored Jun 4, 2021
1 parent 4300a41 commit 8a71384
Show file tree
Hide file tree
Showing 35 changed files with 236 additions and 169 deletions.
5 changes: 4 additions & 1 deletion packages/material-ui-lab/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@
},
"include": ["src/**/*.ts*"],
"exclude": ["src/**/*.d.ts", "src/**/*.test.*", "./**/*.spec.*", "**/test-utils.tsx"],
"references": [{ "path": "../material-ui/tsconfig.build.json" }]
"references": [
{ "path": "../material-ui/tsconfig.build.json" },
{ "path": "../material-ui-system/tsconfig.build.json" }
]
}
3 changes: 1 addition & 2 deletions packages/material-ui-system/src/Box/Box.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { OverridableComponent, OverrideProps } from '@material-ui/types';
import { Theme } from '../createTheme';
import {
SxProps,
StandardCSSProperties,
Expand Down Expand Up @@ -170,8 +171,6 @@ export type SystemProps = {
} &
CustomSystemProps;

export interface Theme {}

export interface BoxTypeMap<P = {}, D extends React.ElementType = 'div'> {
props: P &
SystemProps & {
Expand Down
5 changes: 4 additions & 1 deletion packages/material-ui-system/src/createBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import clsx from 'clsx';
import styled, { ThemeContext } from '@material-ui/styled-engine';
import styleFunctionSx, { extendSxProp } from './styleFunctionSx';
import createTheme from './createTheme';

function isObjectEmpty(obj) {
return Object.keys(obj).length === 0;
Expand All @@ -13,7 +14,9 @@ const useTheme = (defaultTheme) => {
return !contextTheme || isObjectEmpty(contextTheme) ? defaultTheme : contextTheme;
};

export default function createBox(defaultTheme = {}) {
export const systemDefaultTheme = createTheme();

export default function createBox(defaultTheme = systemDefaultTheme) {
const BoxRoot = styled('div')(styleFunctionSx);

const Box = React.forwardRef(function Box(inProps, ref) {
Expand Down
3 changes: 1 addition & 2 deletions packages/material-ui-system/src/createStyled.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as React from 'react';
import * as CSS from 'csstype';
import { SxProps } from './styleFunctionSx';

export interface DefaultTheme {}
import { Theme as DefaultTheme } from './createTheme';

export interface SerializedStyles {
name: string;
Expand Down
27 changes: 5 additions & 22 deletions packages/material-ui-system/src/createStyled.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import styledEngineStyled from '@material-ui/styled-engine';
import createTheme from './createTheme';
import styleFunctionSx from './styleFunctionSx';
import propsToClassKey from './propsToClassKey';

Expand Down Expand Up @@ -54,25 +55,7 @@ const variantsResolver = (props, styles, theme, name) => {
export const shouldForwardProp = (prop) =>
prop !== 'styleProps' && prop !== 'theme' && prop !== 'isRtl' && prop !== 'sx' && prop !== 'as';

export const systemDefaultTheme = {
breakpoints: {
values: {},
},
direction: 'ltr',
mixins: {},
components: {},
palette: {
mode: 'light',
},
shadows: {},
typography: {},
spacing: 8,
shape: {
borderRadius: 4,
},
transitions: {},
zIndex: {},
};
export const systemDefaultTheme = createTheme();

const lowercaseFirstLetter = (string) => {
return string.charAt(0).toLowerCase() + string.slice(1);
Expand All @@ -95,7 +78,7 @@ export default function createStyled(input = {}) {
...options
} = inputOptions;

// if skipVariantsResolver option is defined, take the value, otherwise, true for root and false for other slots
// if skipVariantsResolver option is defined, take the value, otherwise, true for root and false for other slots.
const skipVariantsResolver =
inputSkipVariantsResolver !== undefined
? inputSkipVariantsResolver
Expand Down Expand Up @@ -170,11 +153,11 @@ export default function createStyled(input = {}) {

if (Array.isArray(styleArg) && numOfCustomFnsApplied > 0) {
const placeholders = new Array(numOfCustomFnsApplied).fill('');
// If the type is array, than we need to add placeholders in the template for the overrides, variants and the sx styles
// If the type is array, than we need to add placeholders in the template for the overrides, variants and the sx styles.
transformedStyleArg = [...styleArg, ...placeholders];
transformedStyleArg.raw = [...styleArg.raw, ...placeholders];
} else if (typeof styleArg === 'function') {
// If the type is function, we need to define the default theme
// If the type is function, we need to define the default theme.
transformedStyleArg = ({ theme: themeInput, ...other }) =>
styleArg({ theme: isEmpty(themeInput) ? defaultTheme : themeInput, ...other });
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createUnarySpacing } from '@material-ui/system';
import { createUnarySpacing } from '../spacing';

export type SpacingOptions =
| number
Expand Down Expand Up @@ -33,7 +33,7 @@ export default function createSpacing(spacingInput: SpacingOptions = 8): Spacing
}

// Material Design layouts are visually balanced. Most measurements align to an 8dp grid, which aligns both spacing and the overall layout.
// Smaller components, such as icons and type, can align to a 4dp grid.
// Smaller components, such as icons, can align to a 4dp grid.
// https://material.io/design/layout/understanding-layout.html#usage
const transform = createUnarySpacing({
spacing: spacingInput,
Expand Down
43 changes: 43 additions & 0 deletions packages/material-ui-system/src/createTheme/createTheme.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Breakpoints, BreakpointsOptions } from './createBreakpoints';
import { Shape, ShapeOptions } from './shape';
import { Spacing, SpacingOptions } from './createSpacing';

export { Breakpoint, BreakpointOverrides } from './createBreakpoints';

export type Direction = 'ltr' | 'rtl';

export interface ThemeOptions {
shape?: ShapeOptions;
breakpoints?: BreakpointsOptions;
direction?: Direction;
mixins?: unknown;
palette?: Record<string, any>;
shadows?: unknown;
spacing?: SpacingOptions;
transitions?: unknown;
components?: Record<string, any>;
typography?: unknown;
zIndex?: Record<string, number>;
}

export interface Theme {
shape: Shape;
breakpoints: Breakpoints;
direction: Direction;
palette: Record<string, any> & { mode: 'light' | 'dark' };
shadows?: unknown;
spacing: Spacing;
transitions?: unknown;
components?: Record<string, any>;
mixins?: unknown;
typography?: unknown;
zIndex?: unknown;
}

/**
* Generate a theme base on the options received.
* @param options Takes an incomplete theme object and adds the missing parts.
* @param args Deep merge the arguments with the about to be returned theme.
* @returns A complete, ready to use theme object.
*/
export default function createTheme(options?: ThemeOptions, ...args: object[]): Theme;
35 changes: 35 additions & 0 deletions packages/material-ui-system/src/createTheme/createTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { deepmerge } from '@material-ui/utils';
import createBreakpoints from './createBreakpoints';
import shape from './shape';
import createSpacing from './createSpacing';

function createTheme(options = {}, ...args) {
const {
breakpoints: breakpointsInput = {},
palette: paletteInput = {},
spacing: spacingInput,
shape: shapeInput = {},
...other
} = options;

const breakpoints = createBreakpoints(breakpointsInput);
const spacing = createSpacing(spacingInput);

let muiTheme = deepmerge(
{
breakpoints,
direction: 'ltr',
components: {}, // Inject component definitions.
palette: { mode: 'light', ...paletteInput },
spacing,
shape: { ...shape, shapeInput },
},
other,
);

muiTheme = args.reduce((acc, argument) => deepmerge(acc, argument), muiTheme);

return muiTheme;
}

export default createTheme;
2 changes: 2 additions & 0 deletions packages/material-ui-system/src/createTheme/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './createTheme';
export * from './createTheme';
1 change: 1 addition & 0 deletions packages/material-ui-system/src/createTheme/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './createTheme';
File renamed without changes.
File renamed without changes.
80 changes: 14 additions & 66 deletions packages/material-ui-system/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as React from 'react';
import { CSSObject } from './createStyled';
import {
ComposedStyleFunction,
StyleFunction,
Expand All @@ -13,7 +11,6 @@ import {
positions,
shadows,
sizing,
spacing,
typography,
} from './Box';
// disable automatic export
Expand Down Expand Up @@ -82,69 +79,6 @@ export const sizeHeight: SimpleStyleFunction<'sizeHeight'>;
export const boxSizing: SimpleStyleFunction<'boxSizing'>;
export type SizingProps = PropsFor<typeof sizing>;

// spacing.js
export type SpacingProps = PropsFor<typeof spacing>;
export function createUnarySpacing<Spacing>(theme: { spacing: Spacing }): Spacing extends number
? (abs: number | string) => number | number
: Spacing extends any[]
? <Index extends number>(abs: Index | string) => Spacing[Index] | string
: Spacing extends (...args: unknown[]) => unknown
? Spacing
: // warns in Dev
() => undefined;

export const margin: SimpleStyleFunction<
| 'm'
| 'mt'
| 'mr'
| 'mb'
| 'ml'
| 'mx'
| 'my'
| 'margin'
| 'marginTop'
| 'marginRight'
| 'marginBottom'
| 'marginLeft'
| 'marginX'
| 'marginY'
>;

export type MarginProps = PropsFor<typeof margin>;

export const padding: SimpleStyleFunction<
| 'p'
| 'pt'
| 'pr'
| 'pb'
| 'pl'
| 'px'
| 'py'
| 'padding'
| 'paddingTop'
| 'paddingRight'
| 'paddingBottom'
| 'paddingLeft'
| 'paddingX'
| 'paddingY'
>;

export type PaddingProps = PropsFor<typeof padding>;

// style.js
export interface StyleOptions<PropKey> {
cssProperty?: PropKey | keyof React.CSSProperties | false;
prop: PropKey;
/**
* dot access in `Theme`
*/
themeKey?: string;
transform?: (cssValue: unknown) => number | string | React.CSSProperties | CSSObject;
}
export function style<PropKey extends string, Theme extends object>(
options: StyleOptions<PropKey>,
): StyleFunction<{ [K in PropKey]?: unknown } & { theme: Theme }>;

// typography.js
export const typographyVariant: SimpleStyleFunction<'typography'>;
export const fontFamily: SimpleStyleFunction<'fontFamily'>;
Expand All @@ -166,6 +100,9 @@ export function unstable_getThemeValue(prop: string, value: any, theme: object):
*/
export type ResponsiveStyleValue<T> = T | Array<T | null> | { [key: string]: T | null };

export * from './style';
export * from './spacing';

export {
default as unstable_styleFunctionSx,
extendSxProp as unstable_extendSxProp,
Expand All @@ -183,3 +120,14 @@ export * from './createStyled';

export { default as styled } from './styled';
export * from './styled';

export { default as createTheme } from './createTheme';
export * from './createTheme';

export { default as createBreakpoints } from './createTheme/createBreakpoints';
export * from './createTheme/createBreakpoints';

export { SpacingOptions, Spacing } from './createTheme/createSpacing';

export { default as shape } from './createTheme/shape';
export * from './createTheme/shape';
3 changes: 3 additions & 0 deletions packages/material-ui-system/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ export { default as createBox } from './createBox';
export { default as createStyled } from './createStyled';
export * from './createStyled';
export { default as styled } from './styled';
export { default as createTheme } from './createTheme';
export { default as createBreakpoints } from './createTheme/createBreakpoints';
export { default as shape } from './createTheme/shape';
1 change: 1 addition & 0 deletions packages/material-ui-system/src/merge.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default function merge(acc: object, item: object): object;
49 changes: 49 additions & 0 deletions packages/material-ui-system/src/spacing.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { SimpleStyleFunction, spacing, PropsFor } from './Box';

export type SpacingProps = PropsFor<typeof spacing>;
export function createUnarySpacing<Spacing>(theme: { spacing: Spacing }): Spacing extends number
? (abs: number | string) => number | number
: Spacing extends any[]
? <Index extends number>(abs: Index | string) => Spacing[Index] | string
: Spacing extends (...args: unknown[]) => unknown
? Spacing
: // warns in Dev
() => undefined;

export const margin: SimpleStyleFunction<
| 'm'
| 'mt'
| 'mr'
| 'mb'
| 'ml'
| 'mx'
| 'my'
| 'margin'
| 'marginTop'
| 'marginRight'
| 'marginBottom'
| 'marginLeft'
| 'marginX'
| 'marginY'
>;

export type MarginProps = PropsFor<typeof margin>;

export const padding: SimpleStyleFunction<
| 'p'
| 'pt'
| 'pr'
| 'pb'
| 'pl'
| 'px'
| 'py'
| 'padding'
| 'paddingTop'
| 'paddingRight'
| 'paddingBottom'
| 'paddingLeft'
| 'paddingX'
| 'paddingY'
>;

export type PaddingProps = PropsFor<typeof padding>;
15 changes: 15 additions & 0 deletions packages/material-ui-system/src/style.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { StyleFunction } from './Box';
import { CSSObject } from './createStyled';

export interface StyleOptions<PropKey> {
cssProperty?: PropKey | keyof React.CSSProperties | false;
prop: PropKey;
/**
* dot access in `Theme`
*/
themeKey?: string;
transform?: (cssValue: unknown) => number | string | React.CSSProperties | CSSObject;
}
export function style<PropKey extends string, Theme extends object>(
options: StyleOptions<PropKey>,
): StyleFunction<{ [K in PropKey]?: unknown } & { theme: Theme }>;
Loading

0 comments on commit 8a71384

Please sign in to comment.