From 26e5f4fea01c02c0ad07c6af4bb02d7c5ce05fb3 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 14 Jan 2021 13:50:34 -0600 Subject: [PATCH 01/23] poc --- .babelrc.js | 8 +- package.json | 4 + src-docs/src/routes.js | 59 +++++ src-docs/src/views/emotion/canopy.tsx | 317 ++++++++++++++++++++++++++ src-docs/src/views/emotion/index.ts | 6 + src/index.d.ts | 1 + src/services/theme/theme.ts | 241 ++++++++++++++++++++ yarn.lock | 179 ++++++++++++++- 8 files changed, 811 insertions(+), 4 deletions(-) create mode 100644 src-docs/src/views/emotion/canopy.tsx create mode 100644 src-docs/src/views/emotion/index.ts create mode 100644 src/services/theme/theme.ts diff --git a/.babelrc.js b/.babelrc.js index f7b986d99a3..56ae2a14425 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -12,7 +12,13 @@ module.exports = { "modules": process.env.BABEL_MODULES ? process.env.BABEL_MODULES === 'false' ? false : process.env.BABEL_MODULES : "commonjs" // babel's default is commonjs }], ["@babel/typescript", { isTSX: true, allExtensions: true }], - "@babel/react" + "@babel/react", + [ + "@emotion/babel-preset-css-prop", + { + "labelFormat": "[local]" + }, + ], ], "plugins": [ "@babel/plugin-syntax-dynamic-import", diff --git a/package.json b/package.json index f783b03e7f8..e3e076c5ed6 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,9 @@ "@elastic/charts": "^20.0.0", "@elastic/datemath": "^5.0.3", "@elastic/eslint-config-kibana": "^0.15.0", + "@emotion/babel-preset-css-prop": "^11.0.0", + "@emotion/eslint-plugin": "^11.0.0", + "@emotion/react": "^11.1.1", "@svgr/core": "5.4.0", "@svgr/plugin-svgo": "^4.0.3", "@types/classnames": "^2.2.10", @@ -214,6 +217,7 @@ }, "peerDependencies": { "@elastic/datemath": "^5.0.2", + "@emotion/react": "11.x", "@types/react": "^16.9.34", "@types/react-dom": "^16.9.6", "moment": "^2.13.0", diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 5905d4889a1..1cc37df8c9d 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -223,6 +223,18 @@ import { ElasticChartsCategoryExample } from './views/elastic_charts/category_ex import { ElasticChartsSparklinesExample } from './views/elastic_charts/sparklines_example'; import { ElasticChartsPieExample } from './views/elastic_charts/pie_example'; + +/** ! Temporary ! */ + +import { + // App as EnvoyApp, + // AppB as EnvoyAppB, + // AppC as EnvoyAppC, + Canopy, + // CoilApp, + // PropagateApp, +} from './views/emotion'; + /** * Lowercases input and replaces spaces with hyphens: * e.g. 'GridView Example' -> 'gridview-example' @@ -305,6 +317,53 @@ const createExample = (example, customTitle) => { }; const navigation = [ + { + name: 'Temporary', + items: [ + // createExample( + // { + // intro: , + // sections: [], + // }, + // 'Envoy' + // ), + // createExample( + // { + // intro: , + // sections: [], + // }, + // 'Envoy B' + // ), + // createExample( + // { + // intro: , + // sections: [], + // }, + // 'Envoy C' + // ), + createExample( + { + intro: , + sections: [], + }, + 'Canopy' + ), + // createExample( + // { + // intro: , + // sections: [], + // }, + // 'Coil' + // ), + // createExample( + // { + // intro: , + // sections: [], + // }, + // 'Propagate' + // ), + ], + }, { name: 'Guidelines', items: [ diff --git a/src-docs/src/views/emotion/canopy.tsx b/src-docs/src/views/emotion/canopy.tsx new file mode 100644 index 00000000000..3230221a4c6 --- /dev/null +++ b/src-docs/src/views/emotion/canopy.tsx @@ -0,0 +1,317 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; +// import { css } from '@emotion/react'; +import chroma from 'chroma-js'; +import { EuiSpacer } from '../../../../src/components/spacer'; +import { EuiIcon } from '../../../../src/components/icon'; +import { + createTheme, + computed, + Computed, + setOn, +} from '../../../../src/services/theme/theme'; + +const mergeDeep = (_target: any, source: any) => { + const isObject = (obj: any) => obj && typeof obj === 'object'; + const target = { ..._target }; + + if (!isObject(target) || !isObject(source)) { + return source; + } + + Object.keys(source).forEach((key) => { + const targetValue = target[key]; + const sourceValue = source[key]; + + if (isObject(targetValue) && isObject(sourceValue)) { + target[key] = mergeDeep({ ...targetValue }, { ...sourceValue }); + } else { + target[key] = sourceValue; + } + }); + + return target; +}; + +const DefaultEuiTheme = createTheme({ + light: { + colors: { + primary: '#006BB4', + secondary: computed(['light.colors.primary'], ([primary]) => { + return chroma(primary).darken(2).hex(); + }), + tertiary: computed(['light.colors.secondary'], ([secondary]) => { + return chroma(secondary).brighten().hex(); + }), + }, + }, + dark: { + colors: { + primary: '#DD0A73', + secondary: computed(['dark.colors.primary'], ([primary]) => { + return chroma(primary).brighten(3).hex(); + }), + tertiary: computed(['dark.colors.secondary'], ([secondary]) => { + return chroma(secondary).darken().hex(); + }), + }, + }, +}); + +const EuiThemeContext = React.createContext(undefined); +const EuiOverrideContext = React.createContext({}); +const EuiColorModeContext = React.createContext(undefined); + +export function EuiThemeProvider({ + theme: themeConfig, + colorMode: _colorMode, + overrides: _overrides = {}, + children, +}: { + theme?: any; + colorMode?: string; + overrides?: any; + children: any; +}) { + const parentSystem = React.useContext(EuiThemeContext); + const parentOverrides = React.useContext(EuiOverrideContext); + const parentColorMode = React.useContext(EuiColorModeContext); + // const isParentThemeProvider = parentSystem === undefined; + + const theme = React.useMemo(() => themeConfig || parentSystem, [ + themeConfig, + parentSystem, + ]); + + const colorMode = React.useMemo( + () => getColorMode(_colorMode, parentColorMode), + [_colorMode, parentColorMode] + ); + const overrides = React.useMemo(() => { + return mergeDeep(parentOverrides, _overrides); + }, [_overrides, parentOverrides]); + + return ( + + + + {children} + + + + ); +} + +function isInverseColorMode(colorMode?: string) { + return colorMode === 'inverse'; +} + +function getColorMode(colorMode?: string, parentColorMode?: string) { + if (colorMode == null) { + return parentColorMode || 'light'; + } else if (isInverseColorMode(colorMode)) { + return parentColorMode === 'dark' || parentColorMode === undefined + ? 'light' + : 'dark'; + } else { + return colorMode; + } +} + +function compute(base: any, over: any) { + const output = {}; + + function loop(base: any, over: any, path?: string) { + Object.keys(base).forEach((key) => { + const baseValue = + base[key] instanceof Computed + ? base[key].getValue(base.root, over.root, output) + : base[key]; + const overValue = + over[key] instanceof Computed + ? over[key].getValue(base.root, over.root, output) + : over[key]; + const newPath = path ? `${path}.${key}` : `${key}`; + if (baseValue && typeof baseValue === 'object') { + loop(baseValue, overValue ?? {}, newPath); + } else { + setOn(output, newPath, overValue ?? baseValue); + } + }); + } + loop(base, over); + return output; +} + +export function useEuiTheme() { + const theme = React.useContext(EuiThemeContext); + const overrides = React.useContext(EuiOverrideContext); + const colorMode = React.useContext(EuiColorModeContext); + const [values, setValues] = React.useState(() => { + return compute(theme, createTheme(overrides)); + }); + React.useEffect(() => { + setValues(compute(theme, createTheme(overrides))); + }, [theme, overrides]); + return [values[colorMode], theme, colorMode]; +} + +const View = () => { + const [theme, , colorMode] = useEuiTheme(); + return ( +
+
+ {colorMode} +
+          {JSON.stringify(theme, null, 2)}
+        
+
+
+

+

+

+

+

+

+
+
+ ); +}; + +const View3 = () => { + const overrides = { + light: { + colors: { primary: '#017D73' }, + }, + dark: { + colors: { primary: '#F5A700' }, + }, + }; + return ( + <> + + + + + Overriding primary + + + + ); +}; + +const View2 = () => { + const overrides = { + light: { + colors: { secondary: '#017D73' }, + }, + dark: { + colors: { secondary: '#F5A700' }, + }, + }; + return ( + <> + + + Overriding secondary + + + + ); +}; + +export const Canopy = () => { + const [colorMode, setColorMode] = React.useState('light'); + const toggleTheme = () => { + setColorMode((mode) => (mode === 'light' ? 'dark' : 'light')); + }; + const [overrides, setOverrides] = React.useState({}); + const lightColors = () => { + setOverrides({ + ...overrides, + light: { + colors: { + primary: chroma.random().hex(), + }, + }, + }); + }; + const darkColors = () => { + setOverrides({ + ...overrides, + dark: { + colors: { + primary: chroma.random().hex(), + }, + }, + }); + }; + + return ( + <> + + + + + {' '} + + + + Default view + + + + + Inverse colorMode + + + + + + ); +}; diff --git a/src-docs/src/views/emotion/index.ts b/src-docs/src/views/emotion/index.ts new file mode 100644 index 00000000000..5704721adc5 --- /dev/null +++ b/src-docs/src/views/emotion/index.ts @@ -0,0 +1,6 @@ +// export { App } from './app'; +// export { AppB } from './app_b'; +// export { AppC } from './app_c'; +export { Canopy } from './canopy'; +// export { PropagateApp } from './propagate'; +// export { Coil as CoilApp } from './coil'; diff --git a/src/index.d.ts b/src/index.d.ts index 3e29e8ae972..1e07a0b21b7 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -18,5 +18,6 @@ */ /* eslint-disable @typescript-eslint/triple-slash-reference */ +/// /// /// diff --git a/src/services/theme/theme.ts b/src/services/theme/theme.ts new file mode 100644 index 00000000000..97c53871f78 --- /dev/null +++ b/src/services/theme/theme.ts @@ -0,0 +1,241 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// interface Store { +// parent: { +// child?: { +// age: number; +// siblings?: number; +// }; +// }; +// other: false; +// name?: string; +// } + +// type primitive = string | number | boolean | undefined | null; + +// class Undefined { +// constructor(private t: T) {} +// } + +// type RequiredKeys = { +// [K in keyof T]-?: {} extends Pick ? never : K; +// }[keyof T]; +// type Accessor = { +// [key in keyof Shape]-?: Shape[key] extends primitive +// ? key extends RequiredKeys +// ? // required +// ForceOptional extends false +// ? Shape[key] +// : Shape[key] | undefined +// : // optional +// // Any `undefined` added here would be removed if --strictNullChecks mode is enabled +// // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html +// // >> Note that in --strictNullChecks mode, when a homomorphic mapped type removes a ? modifier from a property in the underlying type it also removes undefined from the type of that property: +// // instead, we can return a private class and convert it back to +// // `undefined` when the time comes to interpret the output type +// Undefined // note there is no `| undefined` here, as the type already includes it from being optional +// : key extends RequiredKeys +// ? Accessor // required +// : Accessor; // optional +// }; + +// type TypeFromAccessor = T extends primitive +// ? T +// : T extends Undefined +// ? U +// : T extends Accessor +// ? ForceOptional extends true +// ? Shape | undefined +// : Shape +// : never; + +// const storeAccessor: TypeFromAccessor> = null as any; +// storeAccessor.name; // string | undefined +// storeAccessor.other; // false +// storeAccessor.parent; // object +// storeAccessor.parent.child; // object | undefined + +// const otherAccessor: TypeFromAccessor['other']> = null as any; // false +// const nameAccessor: TypeFromAccessor['name']> = null as any; // string | undefined + +// const parentAccessor: TypeFromAccessor['parent']> = null as any; +// parentAccessor.child; // object | undefined + +// const childAccessor: TypeFromAccessor< +// Accessor['parent']['child'] +// > = null as any; +// childAccessor.age; // good error - possibly undefined +// childAccessor!.age; // number +// childAccessor!.siblings; // number | undefined + +// const ageAccessor: TypeFromAccessor< +// Accessor['parent']['child']['age'] +// > = null as any; +// ageAccessor; // number | undefined +// ageAccessor.toString(); // good error - possibly undefined +// const siblingsAccessor: TypeFromAccessor< +// Accessor['parent']['child']['siblings'] +// > = null as any; +// siblingsAccessor; // number || undefined +// siblingsAccessor.toString(); // good error + +/// +/// +/// + +export function getOn(model: { [key: string]: any }, _path: string) { + const path = _path.split('.'); + let node = model; + + while (path.length) { + const segment = path.shift()!; + if (node.hasOwnProperty(segment) === false) return undefined; + if (node[segment] instanceof Computed) { + node = node[segment].getValue(model); + } else { + node = node[segment]; + } + } + + return node; +} + +export function setOn( + model: { [key: string]: any }, + _path: string, + value: any +) { + const path = _path.split('.'); + const propertyName = path.pop()!; + let node = model; + + while (path.length) { + const segment = path.shift()!; + if (node.hasOwnProperty(segment) === false) { + node[segment] = {}; + } + node = node[segment]; + } + + node[propertyName] = value; + return true; +} + +export class Computed { + constructor( + public dependencies: any[], + public computer: (...values: any[]) => T + ) {} + + getValue(model: any, overrides = {}, working: any) { + return this.computer( + this.dependencies.map((dependency) => { + if (working) { + return getOn(working, dependency); + } + return getOn(overrides, dependency) ?? getOn(model, dependency); + }) + ); + } +} + +export function computed( + dependencies: any[], + computer: (values: any[]) => T +): T { + return (new Computed(dependencies, computer) as unknown) as T; +} + +export const createTheme = (model: any) => { + const handler = { + getPrototypeOf(target: any) { + return Reflect.getPrototypeOf(target.model); + }, + + setPrototypeOf(target: any, prototype: any) { + return Reflect.setPrototypeOf(target.model, prototype); + }, + + isExtensible(target: any) { + return Reflect.isExtensible(target); + }, + + preventExtensions(target: any) { + return Reflect.preventExtensions(target.model); + }, + + // TODO: FIGURE THIS OUT + // getOwnPropertyDescriptor(target, key) { + // return Reflect.getOwnPropertyDescriptor(target.model, key); + // }, + + getOwnPropertyDescriptor(target: any, key: any) { + const descriptor: any = Reflect.getOwnPropertyDescriptor( + target.model, + key + ) || { + value: handler.get(target, key), + }; + + Object.defineProperty(target, key, descriptor); + return descriptor; + }, + + defineProperty(target: any, property: any, attributes: any) { + return Reflect.defineProperty(target.model, property, attributes); + }, + + has(target: any, property: any) { + return Reflect.has(target.model, property); + }, + + get(_target: any, property: any): any { + const target = property === 'root' ? _target : _target.model || _target; + if (typeof target[property] === 'object' && target[property] !== null) { + return new Proxy( + { model: target[property], root: _target.root }, + handler + ); + } else { + return target[property]; + } + }, + + deleteProperty(target: any, property: any) { + return Reflect.deleteProperty(target.model, property); + }, + + ownKeys(target: any) { + return Reflect.ownKeys(target.model); + }, + + apply(target: any, thisArg: any, argumentList: any) { + console.log('apply'); + return Reflect.apply(target.model, thisArg, argumentList); + }, + + construct(target: any, argumentsList: any, newTarget: any) { + return Reflect.construct(target.model, argumentsList, newTarget); + }, + }; + const themeProxy = new Proxy({ model, root: model }, handler); + + return themeProxy; +}; diff --git a/yarn.lock b/yarn.lock index d41ce280d89..91209c657b6 100755 --- a/yarn.lock +++ b/yarn.lock @@ -96,6 +96,15 @@ "@babel/helper-module-imports" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-builder-react-jsx-experimental@^7.12.1": + version "7.12.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.4.tgz#55fc1ead5242caa0ca2875dcb8eed6d311e50f48" + integrity sha512-AjEa0jrQqNk7eDQOo0pTfUOwQBMF+xVqrausQwT9/rTKy0g04ggFNaJpaE09IQMn9yExluigWMJcj0WC7bq+Og== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-module-imports" "^7.12.1" + "@babel/types" "^7.12.1" + "@babel/helper-builder-react-jsx@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d" @@ -188,6 +197,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.7.0": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" + integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== + dependencies: + "@babel/types" "^7.12.5" + "@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" @@ -449,6 +465,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-jsx@^7.12.1", "@babel/plugin-syntax-jsx@^7.2.0": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -731,6 +754,16 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" +"@babel/plugin-transform-react-jsx@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.5.tgz#39ede0e30159770561b6963be143e40af3bde00c" + integrity sha512-2xkcPqqrYiOQgSlM/iwto1paPijjsDbUynN13tI6bosDz/jOW3CRzYguIE8wKX32h+msbBM22Dv5fwrFkUOZjQ== + dependencies: + "@babel/helper-builder-react-jsx" "^7.10.4" + "@babel/helper-builder-react-jsx-experimental" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.12.1" + "@babel/plugin-transform-react-pure-annotations@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501" @@ -954,6 +987,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.7.2": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.4.0", "@babel/template@^7.7.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" @@ -987,6 +1027,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.12.1", "@babel/types@^7.12.5": + version "7.12.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz#ae0e55ef1cce1fbc881cd26f8234eb3e657edc96" + integrity sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -1035,6 +1084,111 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-config-kibana/-/eslint-config-kibana-0.15.0.tgz#a552793497cdfc1829c2f9b7cd7018eb008f1606" integrity sha1-pVJ5NJfN/Bgpwvm3zXAY6wCPFgY= +"@emotion/babel-plugin-jsx-pragmatic@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin-jsx-pragmatic/-/babel-plugin-jsx-pragmatic-0.1.5.tgz#27debfe9c27c4d83574d509787ae553bf8a34d7e" + integrity sha512-y+3AJ0SItMDaAgGPVkQBC/S/BaqaPACkQ6MyCI2CUlrjTxKttTVfD3TMtcs7vLEcLxqzZ1xiG0vzwCXjhopawQ== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@emotion/babel-plugin@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.0.0.tgz#e6f40fa81ef52775773a53d50220c597ebc5c2ef" + integrity sha512-w3YP0jlqrNwBBaSI6W+r80fOKF6l9QmsPfLNx5YWSHwrxjVZhM+L50gY7YCVAvlfr1/qdD1vsFN+PDZmLvt42Q== + dependencies: + "@babel/helper-module-imports" "^7.7.0" + "@babel/plugin-syntax-jsx" "^7.12.1" + "@babel/runtime" "^7.7.2" + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.4" + "@emotion/serialize" "^1.0.0" + babel-plugin-macros "^2.6.1" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "^4.0.3" + +"@emotion/babel-preset-css-prop@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-preset-css-prop/-/babel-preset-css-prop-11.0.0.tgz#25b868affa620b9e97024b67f67ad32c03a0510e" + integrity sha512-E7z3jMf1OyThGpp3ngYGxOSGX5AdoSQTuqM9QgJNAHFh3Fi8N5CbWx6g+IdySJ8bjPiMgFQsIeEhkyy+4mDpCQ== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.12.1" + "@babel/runtime" "^7.7.2" + "@emotion/babel-plugin" "^11.0.0" + "@emotion/babel-plugin-jsx-pragmatic" "^0.1.5" + +"@emotion/cache@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.0.0.tgz#473adcaf9e04c6a0e30fb1421e79a209a96818f8" + integrity sha512-NStfcnLkL5vj3mBILvkR2m/5vFxo3G0QEreYKDGHNHm9IMYoT/t3j6xwjx6lMI/S1LUJfVHQqn0m9wSINttTTQ== + dependencies: + "@emotion/memoize" "^0.7.4" + "@emotion/sheet" "^1.0.0" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + stylis "^4.0.3" + +"@emotion/eslint-plugin@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@emotion/eslint-plugin/-/eslint-plugin-11.0.0.tgz#7666b750df62dc33a93bb1e09086f1caaecadc6f" + integrity sha512-V5w/LgV61xta+U6LKht3WQqfjTLueU2mh1aRTcK5OfkRhZ4OZFE0Inq/oVwLCq5g3Hzoaq27PRm+Tk9W18QScw== + +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + +"@emotion/memoize@^0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@emotion/react@^11.1.1": + version "11.1.1" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.1.1.tgz#4b304d494af321b0179e6763830e07cf674f0423" + integrity sha512-otA0Np8OnOeU9ChkOS9iuLB6vIxiM+bJiU0id33CsQn3R2Pk9ijVHnxevENIKV/P2S7AhrD8cFbUGysEciWlEA== + dependencies: + "@babel/runtime" "^7.7.2" + "@emotion/cache" "^11.0.0" + "@emotion/serialize" "^1.0.0" + "@emotion/sheet" "^1.0.0" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.0.tgz#1a61f4f037cf39995c97fc80ebe99abc7b191ca9" + integrity sha512-zt1gm4rhdo5Sry8QpCOpopIUIKU+mUSpV9WNmFILUraatm5dttNEaYzUWWSboSMUE6PtN2j1cAsuvcugfdI3mw== + dependencies: + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.4" + "@emotion/unitless" "^0.7.5" + "@emotion/utils" "^1.0.0" + csstype "^3.0.2" + +"@emotion/sheet@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.0.tgz#a0ef06080f339477ad4ba7f56e1c931f7ba50822" + integrity sha512-cdCHfZtf/0rahPDCZ9zyq+36EqfD/6c0WUqTFZ/hv9xadTUv2lGE5QK7/Z6Dnx2oRxC0usfVM2/BYn9q9B9wZA== + +"@emotion/unitless@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + +"@emotion/utils@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" + integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== + +"@emotion/weak-memoize@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" + integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== + "@eslint/eslintrc@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" @@ -2903,6 +3057,15 @@ babel-plugin-jest-hoist@^24.9.0: dependencies: "@types/babel__traverse" "^7.0.6" +babel-plugin-macros@^2.6.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" + integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== + dependencies: + "@babel/runtime" "^7.7.2" + cosmiconfig "^6.0.0" + resolve "^1.12.0" + babel-plugin-pegjs-inline-precompile@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-pegjs-inline-precompile/-/babel-plugin-pegjs-inline-precompile-0.1.1.tgz#f12d1aa9f947945c2bd8c9c1ae503a8fba7d810d" @@ -4279,7 +4442,7 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0: +convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -4707,6 +4870,11 @@ csstype@^2.2.0: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== +csstype@^3.0.2: + version "3.0.5" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.5.tgz#7fdec6a28a67ae18647c51668a9ff95bb2fa7bb8" + integrity sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -7802,7 +7970,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -14723,7 +14891,7 @@ source-map@^0.4.2: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -15258,6 +15426,11 @@ stylelint@^8.1.1: svg-tags "^1.0.0" table "^4.0.1" +stylis@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.3.tgz#0d714765f3f694a685550f0c45411ebf90a9bded" + integrity sha512-iAxdFyR9cHKp4H5M2dJlDnvcb/3TvPprzlKjvYVbH7Sh+y8hjY/mUu/ssdcvVz6Z4lKI3vsoS0jAkMYmX7ozfA== + sudo-block@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/sudo-block/-/sudo-block-1.2.0.tgz#cc539bf8191624d4f507d83eeb45b4cea27f3463" From cdf36a9f0dde0459f2a34b9f4a9021951292d874 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Thu, 14 Jan 2021 14:28:58 -0600 Subject: [PATCH 02/23] tidy --- src-docs/src/routes.js | 44 +----- src-docs/src/views/emotion/canopy.tsx | 134 +---------------- src-docs/src/views/emotion/index.ts | 6 - src/services/theme/context.ts | 24 ++++ src/services/theme/hooks.tsx | 40 ++++++ src/services/theme/index.ts | 23 +++ src/services/theme/provider.tsx | 66 +++++++++ src/services/theme/types.ts | 97 +++++++++++++ src/services/theme/{theme.ts => utils.ts} | 168 ++++++++++------------ 9 files changed, 333 insertions(+), 269 deletions(-) delete mode 100644 src-docs/src/views/emotion/index.ts create mode 100644 src/services/theme/context.ts create mode 100644 src/services/theme/hooks.tsx create mode 100644 src/services/theme/index.ts create mode 100644 src/services/theme/provider.tsx create mode 100644 src/services/theme/types.ts rename src/services/theme/{theme.ts => utils.ts} (56%) diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index 1cc37df8c9d..bc335414f14 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -226,14 +226,7 @@ import { ElasticChartsPieExample } from './views/elastic_charts/pie_example'; /** ! Temporary ! */ -import { - // App as EnvoyApp, - // AppB as EnvoyAppB, - // AppC as EnvoyAppC, - Canopy, - // CoilApp, - // PropagateApp, -} from './views/emotion'; +import Canopy from './views/emotion/canopy'; /** * Lowercases input and replaces spaces with hyphens: @@ -320,27 +313,6 @@ const navigation = [ { name: 'Temporary', items: [ - // createExample( - // { - // intro: , - // sections: [], - // }, - // 'Envoy' - // ), - // createExample( - // { - // intro: , - // sections: [], - // }, - // 'Envoy B' - // ), - // createExample( - // { - // intro: , - // sections: [], - // }, - // 'Envoy C' - // ), createExample( { intro: , @@ -348,20 +320,6 @@ const navigation = [ }, 'Canopy' ), - // createExample( - // { - // intro: , - // sections: [], - // }, - // 'Coil' - // ), - // createExample( - // { - // intro: , - // sections: [], - // }, - // 'Propagate' - // ), ], }, { diff --git a/src-docs/src/views/emotion/canopy.tsx b/src-docs/src/views/emotion/canopy.tsx index 3230221a4c6..91a165263fe 100644 --- a/src-docs/src/views/emotion/canopy.tsx +++ b/src-docs/src/views/emotion/canopy.tsx @@ -23,35 +23,13 @@ import chroma from 'chroma-js'; import { EuiSpacer } from '../../../../src/components/spacer'; import { EuiIcon } from '../../../../src/components/icon'; import { - createTheme, + buildTheme, computed, - Computed, - setOn, -} from '../../../../src/services/theme/theme'; + useEuiTheme, + EuiThemeProvider, +} from '../../../../src/services/theme'; -const mergeDeep = (_target: any, source: any) => { - const isObject = (obj: any) => obj && typeof obj === 'object'; - const target = { ..._target }; - - if (!isObject(target) || !isObject(source)) { - return source; - } - - Object.keys(source).forEach((key) => { - const targetValue = target[key]; - const sourceValue = source[key]; - - if (isObject(targetValue) && isObject(sourceValue)) { - target[key] = mergeDeep({ ...targetValue }, { ...sourceValue }); - } else { - target[key] = sourceValue; - } - }); - - return target; -}; - -const DefaultEuiTheme = createTheme({ +const globalTheme = buildTheme({ light: { colors: { primary: '#006BB4', @@ -76,104 +54,6 @@ const DefaultEuiTheme = createTheme({ }, }); -const EuiThemeContext = React.createContext(undefined); -const EuiOverrideContext = React.createContext({}); -const EuiColorModeContext = React.createContext(undefined); - -export function EuiThemeProvider({ - theme: themeConfig, - colorMode: _colorMode, - overrides: _overrides = {}, - children, -}: { - theme?: any; - colorMode?: string; - overrides?: any; - children: any; -}) { - const parentSystem = React.useContext(EuiThemeContext); - const parentOverrides = React.useContext(EuiOverrideContext); - const parentColorMode = React.useContext(EuiColorModeContext); - // const isParentThemeProvider = parentSystem === undefined; - - const theme = React.useMemo(() => themeConfig || parentSystem, [ - themeConfig, - parentSystem, - ]); - - const colorMode = React.useMemo( - () => getColorMode(_colorMode, parentColorMode), - [_colorMode, parentColorMode] - ); - const overrides = React.useMemo(() => { - return mergeDeep(parentOverrides, _overrides); - }, [_overrides, parentOverrides]); - - return ( - - - - {children} - - - - ); -} - -function isInverseColorMode(colorMode?: string) { - return colorMode === 'inverse'; -} - -function getColorMode(colorMode?: string, parentColorMode?: string) { - if (colorMode == null) { - return parentColorMode || 'light'; - } else if (isInverseColorMode(colorMode)) { - return parentColorMode === 'dark' || parentColorMode === undefined - ? 'light' - : 'dark'; - } else { - return colorMode; - } -} - -function compute(base: any, over: any) { - const output = {}; - - function loop(base: any, over: any, path?: string) { - Object.keys(base).forEach((key) => { - const baseValue = - base[key] instanceof Computed - ? base[key].getValue(base.root, over.root, output) - : base[key]; - const overValue = - over[key] instanceof Computed - ? over[key].getValue(base.root, over.root, output) - : over[key]; - const newPath = path ? `${path}.${key}` : `${key}`; - if (baseValue && typeof baseValue === 'object') { - loop(baseValue, overValue ?? {}, newPath); - } else { - setOn(output, newPath, overValue ?? baseValue); - } - }); - } - loop(base, over); - return output; -} - -export function useEuiTheme() { - const theme = React.useContext(EuiThemeContext); - const overrides = React.useContext(EuiOverrideContext); - const colorMode = React.useContext(EuiColorModeContext); - const [values, setValues] = React.useState(() => { - return compute(theme, createTheme(overrides)); - }); - React.useEffect(() => { - setValues(compute(theme, createTheme(overrides))); - }, [theme, overrides]); - return [values[colorMode], theme, colorMode]; -} - const View = () => { const [theme, , colorMode] = useEuiTheme(); return ( @@ -256,7 +136,7 @@ const View2 = () => { ); }; -export const Canopy = () => { +export default () => { const [colorMode, setColorMode] = React.useState('light'); const toggleTheme = () => { setColorMode((mode) => (mode === 'light' ? 'dark' : 'light')); @@ -286,7 +166,7 @@ export const Canopy = () => { return ( <> - Default view diff --git a/src/themes/index.ts b/src/themes/index.ts index 41a53f10fbe..917739942e2 100644 --- a/src/themes/index.ts +++ b/src/themes/index.ts @@ -18,3 +18,4 @@ */ export { EUI_THEMES, EUI_THEME } from './themes'; +export { DefaultEuiTheme } from './theme'; diff --git a/src/themes/theme.ts b/src/themes/theme.ts new file mode 100644 index 00000000000..738133bab5b --- /dev/null +++ b/src/themes/theme.ts @@ -0,0 +1,214 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import chroma from 'chroma-js'; +import { buildTheme, computed } from '../services/theme'; + +export const tint = (c: string, pct: any) => + pct && chroma.mix(c, '#fff', pct).hex(); +export const shade = (c: string, pct: any) => + pct && chroma.mix(c, '#000', pct).hex(); +const makeHighContrastColor = (c: string) => c; +const makeDisabledContrastColor = (c: string) => c; + +export const light = { + euiColorPrimary: '#006BB4', + euiColorSecondary: '#017D73', + euiColorAccent: '#DD0A73', + + // These colors stay the same no matter the theme + euiColorGhost: '#FFF', + euiColorInk: '#000', + + // Status + euiColorSuccess: computed( + ['light.colors.euiColorSecondary'], + ([euiColorSecondary]) => euiColorSecondary + ), + euiColorDanger: '#BD271E', + euiColorWarning: '#F5A700', + + // Grays + euiColorEmptyShade: '#FFF', + euiColorLightestShade: '#F5F7FA', + euiColorLightShade: '#D3DAE6', + euiColorMediumShade: '#98A2B3', + euiColorDarkShade: '#69707D', + euiColorDarkestShade: '#343741', + euiColorFullShade: '#000', + + // Backgrounds + euiPageBackgroundColor: computed( + ['light.colors.euiColorLightestShade'], + ([euiColorLightestShade]) => tint(euiColorLightestShade, 0.5) + ), + euiColorHighlight: '#FFFCDD', + + // Every color below must be based mathematically on the set above and in a particular order. + euiTextColor: computed( + ['light.colors.euiColorDarkestShade'], + ([euiColorDarkestShade]) => euiColorDarkestShade + ), + euiTitleColor: computed(['light.colors.euiTextColor'], ([euiTextColor]) => + shade(euiTextColor, 0.5) + ), + euiTextSubduedColor: computed( + ['light.colors.euiColorMediumShade'], + ([euiColorMediumShade]) => makeHighContrastColor(euiColorMediumShade) + ), + euiColorDisabled: computed(['light.colors.euiTextColor'], ([euiTextColor]) => + tint(euiTextColor, 0.7) + ), + + // Contrasty text variants + euiColorPrimaryText: computed( + ['light.colors.euiColorPrimary'], + ([euiColorPrimary]) => makeHighContrastColor(euiColorPrimary) + ), + euiColorSecondaryText: computed( + ['light.colors.euiColorSecondary'], + ([euiColorSecondary]) => makeHighContrastColor(euiColorSecondary) + ), + euiColorAccentText: computed( + ['light.colors.euiColorAccent'], + ([euiColorAccent]) => makeHighContrastColor(euiColorAccent) + ), + euiColorWarningText: computed( + ['light.colors.euiColorWarning'], + ([euiColorWarning]) => makeHighContrastColor(euiColorWarning) + ), + euiColorDangerText: computed( + ['light.colors.euiColorDanger'], + ([euiColorDanger]) => makeHighContrastColor(euiColorDanger) + ), + euiColorDisabledText: computed( + ['light.colors.euiColorDisabled'], + ([euiColorDisabled]) => makeDisabledContrastColor(euiColorDisabled) + ), + euiColorSuccessText: computed( + ['light.colors.euiColorSecondaryText'], + ([euiColorSecondaryText]) => euiColorSecondaryText + ), + euiLinkColor: computed( + ['light.colors.euiColorPrimaryText'], + ([euiColorPrimaryText]) => euiColorPrimaryText + ), + // State + euiFocusTransparency: 0.1, + euiFocusBackgroundColor: computed( + ['light.colors.euiColorPrimary', 'light.colors.euiFocusTransparency'], + ([euiColorPrimary, euiFocusTransparency]) => + tint(euiColorPrimary, 1 - euiFocusTransparency) + ), +}; + +export const dark = { + // These colors stay the same no matter the theme + euiColorGhost: '#FFF', + euiColorInk: '#000', + + // Core + euiColorPrimary: '#1BA9F5', + euiColorSecondary: '#7DE2D1', + euiColorAccent: '#F990C0', + + // Status + euiColorSuccess: computed( + ['dark.colors.euiColorSecondary'], + ([euiColorSecondary]) => euiColorSecondary + ), + euiColorWarning: '#FFCE7A', + euiColorDanger: '#F66', + + // Grays + euiColorEmptyShade: '#1D1E24', + euiColorLightestShade: '#25262E', + euiColorLightShade: '#343741', + euiColorMediumShade: '#535966', + euiColorDarkShade: '#98A2B3', + euiColorDarkestShade: '#D4DAE5', + euiColorFullShade: '#FFF', + + // Backgrounds + euiPageBackgroundColor: computed( + ['light.colors.euiColorLightestShade'], + ([euiColorLightestShade]) => shade(euiColorLightestShade, 0.3) + ), + euiColorHighlight: '#2E2D25', + + // Variations from core + euiTextColor: '#DFE5EF', + euiTitleColor: computed( + ['dark.colors.euiTextColor'], + ([euiTextColor]) => euiTextColor + ), + euiTextSubduedColor: computed( + ['light.colors.euiColorMediumShade'], + ([euiColorMediumShade]) => makeHighContrastColor(euiColorMediumShade) + ), + euiColorDisabled: computed(['light.colors.euiTextColor'], ([euiTextColor]) => + shade(euiTextColor, 0.7) + ), + + // Contrasty text variants + euiColorPrimaryText: computed( + ['light.colors.euiColorPrimary'], + ([euiColorPrimary]) => makeHighContrastColor(euiColorPrimary) + ), + euiColorSecondaryText: computed( + ['light.colors.euiColorSecondary'], + ([euiColorSecondary]) => makeHighContrastColor(euiColorSecondary) + ), + euiColorAccentText: computed( + ['light.colors.euiColorAccent'], + ([euiColorAccent]) => makeHighContrastColor(euiColorAccent) + ), + euiColorWarningText: computed( + ['light.colors.euiColorWarning'], + ([euiColorWarning]) => makeHighContrastColor(euiColorWarning) + ), + euiColorDangerText: computed( + ['light.colors.euiColorDanger'], + ([euiColorDanger]) => makeHighContrastColor(euiColorDanger) + ), + euiColorDisabledText: computed( + ['light.colors.euiColorDisabled'], + ([euiColorDisabled]) => makeDisabledContrastColor(euiColorDisabled) + ), + euiColorSuccessText: computed( + ['light.colors.euiColorSecondaryText'], + ([euiColorSecondaryText]) => euiColorSecondaryText + ), + euiLinkColor: computed( + ['light.colors.euiColorPrimaryText'], + ([euiColorPrimaryText]) => euiColorPrimaryText + ), + // State + euiFocusTransparency: 0.3, + euiFocusBackgroundColor: computed( + ['dark.colors.euiColorPrimary', 'dark.colors.euiFocusTransparency'], + ([euiColorPrimary, euiFocusTransparency]) => + shade(euiColorPrimary, 1 - euiFocusTransparency) + ), +}; + +export const DefaultEuiTheme = buildTheme({ + light: { colors: light }, + dark: { colors: dark }, +}); From 5614b0fd41b6a8767ac8baf8fb6ad1f0237cc2a4 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Fri, 15 Jan 2021 12:00:15 -0600 Subject: [PATCH 05/23] table colors via css-in-js --- src/components/table/_table.scss | 60 +++++++++---------- src/components/table/table.tsx | 8 ++- src/components/table/table_footer_cell.tsx | 8 ++- src/components/table/table_header_button.tsx | 16 ++++- src/components/table/table_header_cell.tsx | 23 ++++++++ src/components/table/table_row.tsx | 61 +++++++++++++++++++- src/components/table/table_row_cell.tsx | 7 +++ 7 files changed, 149 insertions(+), 34 deletions(-) diff --git a/src/components/table/_table.scss b/src/components/table/_table.scss index 0e7405fb66a..adbb99f9037 100644 --- a/src/components/table/_table.scss +++ b/src/components/table/_table.scss @@ -10,7 +10,7 @@ table-layout: fixed; border: none; border-collapse: collapse; - background-color: $euiColorEmptyShade; + // background-color: $euiColorEmptyShade; &.euiTable--auto { table-layout: auto; @@ -58,12 +58,12 @@ &:focus { .euiTableCellContent__text { text-decoration: underline; - color: $euiColorPrimary; + // color: $euiColorPrimary; } - .euiTableSortIcon { - fill: $euiColorPrimary; - } + // .euiTableSortIcon { + // fill: $euiColorPrimary; + // } } } @@ -71,9 +71,9 @@ margin-left: $euiSizeXS; flex-shrink: 0; // makes sure the icon doesn't change size because the text is long - .euiTableHeaderButton-isSorted & { - fill: $euiTitleColor; - } + // .euiTableHeaderButton-isSorted & { + // fill: $euiTitleColor; + // } } .euiTableHeaderCellCheckbox { @@ -82,38 +82,38 @@ } .euiTableRow { - &:hover { - background-color: $euiTableHoverColor; - } + // &:hover { + // background-color: $euiTableHoverColor; + // } &.euiTableRow-isExpandedRow { - .euiTableRowCell { - background-color: $euiTableHoverColor; - } + // .euiTableRowCell { + // background-color: $euiTableHoverColor; + // } &.euiTableRow-isSelectable .euiTableCellContent { padding-left: $euiTableCellCheckboxWidth + $euiTableCellContentPadding; } } - &.euiTableRow-isSelected { - background-color: $euiTableSelectedColor; + // &.euiTableRow-isSelected { + // background-color: $euiTableSelectedColor; - + .euiTableRow.euiTableRow-isExpandedRow .euiTableRowCell { - background-color: $euiTableSelectedColor; - } + // + .euiTableRow.euiTableRow-isExpandedRow .euiTableRowCell { + // background-color: $euiTableSelectedColor; + // } - &:hover, - &:hover + .euiTableRow.euiTableRow-isExpandedRow .euiTableRowCell { - background-color: $euiTableHoverSelectedColor; - } - } + // &:hover, + // &:hover + .euiTableRow.euiTableRow-isExpandedRow .euiTableRowCell { + // background-color: $euiTableHoverSelectedColor; + // } + // } } .euiTableRowCell { @include euiTableCell; - color: $euiTextColor; + // color: $euiTextColor; &.euiTableRowCell--isMobileHeader { display: none; // Hide if not mobile breakpoint @@ -126,7 +126,7 @@ // Must come after .euiTableRowCell selector for border to be removed .euiTableFooterCell { - background-color: $euiColorLightestShade; + // background-color: $euiColorLightestShade; border-bottom: none; } @@ -237,11 +237,11 @@ .euiTableRow-isClickable { &:hover { - background-color: $euiTableHoverClickableColor; + // background-color: $euiTableHoverClickableColor; cursor: pointer; } - &:focus { - background-color: $euiTableFocusClickableColor; - } + // &:focus { + // background-color: $euiTableFocusClickableColor; + // } } diff --git a/src/components/table/table.tsx b/src/components/table/table.tsx index 738e9603af9..7aa43e57e2f 100644 --- a/src/components/table/table.tsx +++ b/src/components/table/table.tsx @@ -19,7 +19,9 @@ import React, { FunctionComponent, TableHTMLAttributes } from 'react'; import classNames from 'classnames'; +import { css } from '@emotion/react'; import { CommonProps } from '../common'; +import { useEuiTheme } from '../../services/theme'; export interface EuiTableProps extends CommonProps, @@ -45,6 +47,10 @@ export const EuiTable: FunctionComponent = ({ responsive = true, ...rest }) => { + const [theme] = useEuiTheme(); + const styles = css` + background-color: ${theme.colors.euiColorEmptyShade}; + `; const classes = classNames( 'euiTable', className, @@ -56,7 +62,7 @@ export const EuiTable: FunctionComponent = ({ ); return ( - +
{children}
); diff --git a/src/components/table/table_footer_cell.tsx b/src/components/table/table_footer_cell.tsx index dd3737f32f1..1315bee92c3 100644 --- a/src/components/table/table_footer_cell.tsx +++ b/src/components/table/table_footer_cell.tsx @@ -18,8 +18,10 @@ */ import React, { FunctionComponent, TdHTMLAttributes } from 'react'; +import { css } from '@emotion/react'; import { CommonProps } from '../common'; import classNames from 'classnames'; +import { useEuiTheme } from '../../services/theme'; import { HorizontalAlignment, @@ -44,6 +46,10 @@ export const EuiTableFooterCell: FunctionComponent = ({ style, ...rest }) => { + const [theme] = useEuiTheme(); + const styles = css` + background-color: ${theme.colors.euiColorLightestShade}; + `; const classes = classNames('euiTableFooterCell', className); const contentClasses = classNames('euiTableCellContent', className, { 'euiTableCellContent--alignRight': align === RIGHT_ALIGNMENT, @@ -52,7 +58,7 @@ export const EuiTableFooterCell: FunctionComponent = ({ const styleObj = resolveWidthAsStyle(style, width); return ( - +
{children}
diff --git a/src/components/table/table_header_button.tsx b/src/components/table/table_header_button.tsx index b72d6fabc69..0940f387090 100644 --- a/src/components/table/table_header_button.tsx +++ b/src/components/table/table_header_button.tsx @@ -19,8 +19,10 @@ import React, { ButtonHTMLAttributes, FunctionComponent } from 'react'; import classNames from 'classnames'; +import { css } from '@emotion/react'; import { CommonProps } from '../common'; import { EuiInnerText } from '../inner_text'; +import { useEuiTheme } from '../../services/theme'; import { IconType, EuiIcon } from '../icon'; @@ -35,6 +37,18 @@ export const EuiTableHeaderButton: FunctionComponent = ({ iconType, ...rest }) => { + const [theme] = useEuiTheme(); + const styles = css` + &:hover, + &:focus { + & .euiTableCellContent__text { + color: ${theme.colors.euiColorPrimary}; + } + + & .euiTableSortIcon { + fill: ${theme.colors.euiColorPrimary}; + } + `; const classes = classNames('euiTableHeaderButton', className); // Add an icon to the button if one exists. @@ -52,7 +66,7 @@ export const EuiTableHeaderButton: FunctionComponent = ({ } return ( -