diff --git a/modules/_labs/core/react/index.ts b/modules/_labs/core/react/index.ts index daee1d5ddd..6c6fc33306 100644 --- a/modules/_labs/core/react/index.ts +++ b/modules/_labs/core/react/index.ts @@ -2,9 +2,18 @@ import type from './lib/type'; import space from './lib/space'; import CanvasProvider from './lib/CanvasProvider'; import {breakpoints, CanvasBreakpoints, BreakpointKey} from './lib/theming/breakpoints'; +import createCanvasTheme from './lib/theming/createCanvasTheme'; export default type; -export {breakpoints, type, space, BreakpointKey, CanvasBreakpoints, CanvasProvider}; +export { + breakpoints, + type, + space, + BreakpointKey, + CanvasBreakpoints, + CanvasProvider, + createCanvasTheme, +}; export * from './lib/type'; export * from './lib/theming/types'; export * from './lib/theming/theme'; diff --git a/modules/_labs/core/react/lib/theming/createCanvasTheme.ts b/modules/_labs/core/react/lib/theming/createCanvasTheme.ts new file mode 100644 index 0000000000..9b0c9f6254 --- /dev/null +++ b/modules/_labs/core/react/lib/theming/createCanvasTheme.ts @@ -0,0 +1,87 @@ +import chroma from 'chroma-js'; +import deepmerge from 'deepmerge'; +import colors from '@workday/canvas-colors-web'; +import {defaultCanvasTheme} from './theme'; +import { + CanvasTheme, + PartialCanvasTheme, + CanvasThemePalette, + PartialCanvasThemePalette, +} from './types'; +import {CanvasColor} from '@workday/canvas-kit-react-core'; + +const {gradients, primary, ...allColors} = colors; + +// TODO: Make darken prop more readable. +function shiftColor(hexColor: string, darken: boolean = true) { + const canvasColor = Object.keys(allColors).find( + key => allColors[key as CanvasColor] === hexColor + ); + + if (canvasColor) { + const colorRegex = /([a-zAz]*)(\d{3})/g; + const match = colorRegex.exec(canvasColor); + + if (match) { + const baseColor = match[1]; + const shadeNumber = parseInt(match[2], 10); + + const newShade = darken ? shadeNumber + 100 : shadeNumber - 100; + + if (newShade >= 100 && newShade <= 600) { + return colors[(baseColor + newShade) as CanvasColor]; + } + } + } + + const newColor = darken ? chroma(hexColor).darken() : chroma(hexColor).brighten(); + + return newColor.hex(); +} + +function fillPalette(palette?: PartialCanvasThemePalette): CanvasThemePalette | {} { + if (!palette) { + return {}; + } + const shades = {...palette}; + + if (!shades.main) { + console.warn( + 'The color provided to fillPalette(palette) is invalid. The palette object needs to have a `main` property' + ); + return {}; + } + + const dark = shiftColor(shades.main); + const darkest = shiftColor(dark); + const light = shiftColor(shades.main, false); + const lightest = shiftColor(light, false); + + return { + lightest, + light, + main: shades.main, + dark, + darkest, + contrast: shades.contrast || colors.frenchVanilla100, + }; +} + +export default function createCanvasTheme(partialTheme: PartialCanvasTheme): CanvasTheme { + const {palette, breakpoints = {}} = partialTheme; + const {primary, alert, error, success, neutral, common = {}} = palette!; + + const mergable: PartialCanvasTheme = { + palette: { + common, + primary: fillPalette(primary), + alert: fillPalette(alert), + error: fillPalette(error), + success: fillPalette(success), + neutral: fillPalette(neutral), + }, + breakpoints, + }; + + return deepmerge(defaultCanvasTheme, mergable) as CanvasTheme; +} diff --git a/modules/_labs/core/react/lib/theming/theme.ts b/modules/_labs/core/react/lib/theming/theme.ts index 804cae9e8c..f2302087fc 100644 --- a/modules/_labs/core/react/lib/theming/theme.ts +++ b/modules/_labs/core/react/lib/theming/theme.ts @@ -1,22 +1,9 @@ import * as React from 'react'; import colors from '@workday/canvas-colors-web'; -import deepmerge from 'deepmerge'; import {CanvasTheme} from './types'; import {ThemeContext} from '@emotion/core'; import {breakpoints, up, down, between, only} from './breakpoints'; -/** - * Considerations: - * - Hex vs. Canvas colors - * - Hover/Active and constrasting text colors need to be calculateable. Two options: - * 1. Reverse lookup. i.e. given blueberry 400, we assume hover is 500 and active is 600. We still need to calculate colors outside our range though (i.e. Hex or if we run out of shades) - * 2. Calculate all colors. This would likely require a change to our colors, but would use the same approach in all cases. - * - Should consumers be able to theme grays? (e.g. pills, secondary buttons, etc.) - * - Should components that accept a color be controlled by the theme (e.g. StatusIndicator), or is it only errors and alerts? - * - Text colors? - * - Background? - */ - export const defaultCanvasTheme: CanvasTheme = { palette: { primary: { @@ -105,8 +92,3 @@ export function useTheme(theme?: Object): CanvasTheme { return defaultCanvasTheme; } - -// TODO: Should we use PartialCanvasTheme here? -export function createCanvasTheme(partialTheme: Object): CanvasTheme { - return deepmerge(defaultCanvasTheme, partialTheme); -} diff --git a/modules/_labs/core/react/lib/theming/types.ts b/modules/_labs/core/react/lib/theming/types.ts index 14424241ca..35c2e02f9c 100644 --- a/modules/_labs/core/react/lib/theming/types.ts +++ b/modules/_labs/core/react/lib/theming/types.ts @@ -3,7 +3,7 @@ import {CanvasBreakpoints, BreakpointFnParam} from './breakpoints'; /** * A single palette within a Canvas theme */ -type CanvasThemePalette = { +export type CanvasThemePalette = { lightest: string; light: string; main: string; @@ -25,7 +25,8 @@ export interface CanvasTheme { primary: CanvasThemePalette; error: CanvasThemePalette; alert: CanvasThemePalette; - [index: string]: CanvasThemePalette | CanvasThemeCommonPalette; + success: CanvasThemePalette; + neutral: CanvasThemePalette; }; breakpoints: { values: CanvasBreakpoints; @@ -51,3 +52,4 @@ type RecursivePartial = { }; export type PartialCanvasTheme = RecursivePartial; +export type PartialCanvasThemePalette = RecursivePartial; diff --git a/modules/_labs/core/react/stories/stories_theme.tsx b/modules/_labs/core/react/stories/stories_theme.tsx index 4f57fbd8ed..c9d7b404b4 100644 --- a/modules/_labs/core/react/stories/stories_theme.tsx +++ b/modules/_labs/core/react/stories/stories_theme.tsx @@ -57,7 +57,7 @@ const PaletteTitle = styled(Swatch)( const createSwatch = (name: string, color: string, contrast: string, Component = Swatch) => { return ( - + {name} {contrast && {name}}