From 065495a009717aac2707e0c28dc7204a3a23a47f Mon Sep 17 00:00:00 2001 From: Lene Gadewoll Date: Thu, 7 Nov 2024 17:49:35 +0100 Subject: [PATCH] [Visual Refresh] Update vis colors and palettes (#8112) --- .../docusaurus-theme/src/theme/Root.styles.ts | 4 +- packages/eui-theme-borealis/src/index.ts | 1 + .../src/variables/_components.ts | 59 +++ .../src/variables/_index.scss | 1 + .../src/variables/colors/_colors_vis.scss | 72 +++ .../src/variables/colors/_colors_vis.ts | 101 ++++ .../src/variables/colors/index.ts | 2 + packages/eui-theme-common/package.json | 4 + .../src/global_styling/variables/_index.scss | 1 - .../src/global_styling/variables/colors.ts | 54 +++ .../global_styling/variables/components.ts | 29 ++ .../src/global_styling/variables/flags.ts | 1 + .../src/global_styling/variables/index.ts | 1 - packages/eui-theme-common/src/index.ts | 1 + .../eui-theme-common/src/services/index.ts | 9 + .../src/services/vis_color_store.test.ts | 95 ++++ .../src/services/vis_color_store.ts | 117 +++++ .../guide_locale_selector.js | 40 -- .../components/guide_locale_selector/index.js | 1 - .../guide_theme_selector.tsx | 124 +++-- .../src/components/with_theme/index.ts | 6 +- .../with_theme/language_selector.tsx | 66 +-- .../components/with_theme/theme_context.tsx | 115 +++-- packages/eui/src-docs/src/index.js | 23 +- .../eui/src-docs/src/services/theme/theme.js | 6 +- ...new_dark.scss => theme_borealis_dark.scss} | 0 ...w_light.scss => theme_borealis_light.scss} | 0 .../eui/src-docs/src/views/app_context.js | 10 +- .../src/views/color_palette/color_palette.js | 145 +++--- .../color_palette_quantitative.js | 28 +- .../color_picker/color_palette_display.js | 42 +- .../color_picker/color_palette_picker.js | 34 +- .../src/views/elastic_charts/theming.tsx | 51 +- packages/eui/src/components/avatar/avatar.tsx | 20 +- .../src/components/code/code_syntax.styles.ts | 62 +-- .../color_palette_display.stories.tsx | 33 +- .../color_palette_picker.stories.tsx | 53 +- .../components/color_picker/color_picker.tsx | 13 +- .../loading/loading_chart.styles.ts | 108 +++-- .../components/progress/progress.styles.ts | 47 +- .../eui/src/components/token/token.styles.ts | 30 +- .../eui/src/components/token/token.test.tsx | 4 +- packages/eui/src/components/token/token.tsx | 21 +- .../eui/src/components/token/token_map.ts | 248 +++++++++- .../global_styling/variables/_colors_vis.scss | 2 +- .../global_styling/variables/_colors_vis.ts | 2 +- .../eui/src/services/color/eui_palettes.ts | 242 +++++++-- packages/eui/src/services/color/index.ts | 1 + .../eui/src/services/color/vis_color_store.ts | 15 + packages/eui/src/services/index.ts | 1 + packages/eui/src/services/theme/provider.tsx | 33 +- .../global_styling/variables/_colors.ts | 2 + .../global_styling/variables/_colors_vis.scss | 0 .../global_styling/variables/_colors_vis.ts | 44 +- .../global_styling/variables/_components.ts | 120 +++++ .../global_styling/variables/_index.scss | 1 + packages/eui/src/themes/amsterdam/theme.ts | 1 + packages/eui/src/themes/themes.ts | 9 +- .../docs/components/utilities/_category_.yml | 3 + .../utilities/color_palettes/_category_.yml | 3 + .../utilities/color_palettes/overview.mdx | 459 ++++++++++++++++++ packages/website/package.json | 1 + .../website/src/theme/Demo/default_scope.ts | 6 +- yarn.lock | 3 + 64 files changed, 2294 insertions(+), 536 deletions(-) create mode 100644 packages/eui-theme-borealis/src/variables/colors/_colors_vis.scss create mode 100644 packages/eui-theme-borealis/src/variables/colors/_colors_vis.ts create mode 100644 packages/eui-theme-common/src/services/index.ts create mode 100644 packages/eui-theme-common/src/services/vis_color_store.test.ts create mode 100644 packages/eui-theme-common/src/services/vis_color_store.ts delete mode 100644 packages/eui/src-docs/src/components/guide_locale_selector/guide_locale_selector.js delete mode 100644 packages/eui/src-docs/src/components/guide_locale_selector/index.js rename packages/eui/src-docs/src/{theme_new_dark.scss => theme_borealis_dark.scss} (100%) rename packages/eui/src-docs/src/{theme_new_light.scss => theme_borealis_light.scss} (100%) create mode 100644 packages/eui/src/services/color/vis_color_store.ts rename packages/{eui-theme-common/src => eui/src/themes/amsterdam}/global_styling/variables/_colors_vis.scss (100%) rename packages/{eui-theme-common/src => eui/src/themes/amsterdam}/global_styling/variables/_colors_vis.ts (55%) create mode 100644 packages/website/docs/components/utilities/_category_.yml create mode 100644 packages/website/docs/components/utilities/color_palettes/_category_.yml create mode 100644 packages/website/docs/components/utilities/color_palettes/overview.mdx diff --git a/packages/docusaurus-theme/src/theme/Root.styles.ts b/packages/docusaurus-theme/src/theme/Root.styles.ts index 15fa26a0457..6cf2246d905 100644 --- a/packages/docusaurus-theme/src/theme/Root.styles.ts +++ b/packages/docusaurus-theme/src/theme/Root.styles.ts @@ -18,7 +18,7 @@ import { css } from '@emotion/react'; // on the global level to reduce how often they are called export const getGlobalStyles = (theme: UseEuiTheme) => { const { euiTheme } = theme; - const { font, base, colors, size } = euiTheme; + const { font, base, colors, size, components } = euiTheme; const fontBodyScale = font.scale[font.body.scale]; const fontBase = { fontFamily: font.family, @@ -67,7 +67,7 @@ export const getGlobalStyles = (theme: UseEuiTheme) => { --ifm-menu-color-background-active: ${colors.backgroundBaseSubdued}; --ifm-menu-color-background-hover: var(--eui-background-color-primary); - --ifm-pre-background: ${colors.lightestShade}; + --ifm-pre-background: ${components.codeBackground}; } :root { diff --git a/packages/eui-theme-borealis/src/index.ts b/packages/eui-theme-borealis/src/index.ts index 9bc6532df79..23da9cba464 100644 --- a/packages/eui-theme-borealis/src/index.ts +++ b/packages/eui-theme-borealis/src/index.ts @@ -33,6 +33,7 @@ export const euiThemeBorealis: EuiThemeShape = { components, flags: { hasGlobalFocusColor: true, + hasVisColorAdjustment: false, }, }; diff --git a/packages/eui-theme-borealis/src/variables/_components.ts b/packages/eui-theme-borealis/src/variables/_components.ts index e07ef11d988..c10426fe094 100644 --- a/packages/eui-theme-borealis/src/variables/_components.ts +++ b/packages/eui-theme-borealis/src/variables/_components.ts @@ -27,6 +27,7 @@ import { brand_text_colors, text_colors, } from './colors/_colors_light'; +import { colorVis } from './colors/_colors_vis'; const component_colors: _EuiThemeComponentColors = { badgeBackgroundSubdued: background_colors.backgroundBaseSubdued, @@ -43,6 +44,30 @@ const component_colors: _EuiThemeComponentColors = { buttonGroupBorderColorSelected: border_colors.borderBasePlain, buttonGroupFocusColor: brand_text_colors.textPrimary, + codeBackground: background_colors.backgroundBaseFormsPrepend, + codeBackgroundSelected: 'inherit', + codeColor: text_colors.textParagraph, + codeInlineColor: colorVis.euiColorVisAsTextLight6, + codeCommentColor: text_colors.textSubdued, + codeSelectorColor: 'inherit', + codeStringColor: colorVis.euiColorVisAsTextLight2, + codeTagColor: colorVis.euiColorVisAsTextLight1, + codeNameColor: colorVis.euiColorVisAsTextLight1, + codeNumberColor: colorVis.euiColorVisAsTextLight0, + codeKeywordColor: colorVis.euiColorVisAsTextLight6, + codeFunctionTitleColor: 'inherit', + codeTypeColor: colorVis.euiColorVisAsTextLight1, + codeAttributeColor: 'inherit', + codeSymbolColor: colorVis.euiColorVisAsTextLight3, + codeParamsColor: 'inherit', + codeMetaColor: text_colors.textSubdued, + codeTitleColor: colorVis.euiColorVisAsTextLight4, + codeSectionColor: colorVis.euiColorVisAsTextLight3, + codeAdditionColor: colorVis.euiColorVisAsTextLight0, + codeDeletionColor: colorVis.euiColorVisAsTextLight3, + codeSelectorClassColor: 'inherit', + codeSelectorIdColor: 'inherit', + collapsibleNavGroupBackground: background_colors.backgroundBaseSubdued, collapsibleNavGroupBackgroundDark: dark_background_colors.backgroundBaseSubdued, @@ -77,6 +102,11 @@ const component_colors: _EuiThemeComponentColors = { listGroupItemBackgroundPrimaryHover: background_colors.backgroundBaseInteractiveHover, + loadingChartMonoBackground0: SEMANTIC_COLORS.shade20, + loadingChartMonoBackground1: SEMANTIC_COLORS.shade30, + loadingChartMonoBackground2: SEMANTIC_COLORS.shade40, + loadingChartMonoBackground3: SEMANTIC_COLORS.shade50, + markBackground: background_colors.backgroundLightPrimary, markdownFormatTableBorderColor: border_colors.borderBasePlain, @@ -145,6 +175,30 @@ export const components: _EuiThemeComponents = { buttonGroupBorderColorSelected: dark_border_colors.borderBasePlain, buttonGroupFocusColor: dark_brand_text_colors.textPrimary, + codeBackground: dark_background_colors.backgroundBaseFormsPrepend, + codeBackgroundSelected: 'inherit', + codeColor: dark_text_colors.textParagraph, + codeInlineColor: colorVis.euiColorVisAsTextDark6, + codeCommentColor: dark_text_colors.textSubdued, + codeSelectorColor: 'inherit', + codeStringColor: colorVis.euiColorVisAsTextDark2, + codeTagColor: colorVis.euiColorVisAsTextDark1, + codeNameColor: colorVis.euiColorVisAsTextDark1, + codeNumberColor: colorVis.euiColorVisAsTextDark0, + codeKeywordColor: colorVis.euiColorVisAsTextDark6, + codeFunctionTitleColor: 'inherit', + codeTypeColor: colorVis.euiColorVisAsTextDark1, + codeAttributeColor: 'inherit', + codeSymbolColor: colorVis.euiColorVisAsTextDark3, + codeParamsColor: 'inherit', + codeMetaColor: dark_text_colors.textSubdued, + codeTitleColor: colorVis.euiColorVisAsTextDark4, + codeSectionColor: colorVis.euiColorVisAsTextDark3, + codeAdditionColor: colorVis.euiColorVisAsTextDark0, + codeDeletionColor: colorVis.euiColorVisAsTextDark3, + codeSelectorClassColor: 'inherit', + codeSelectorIdColor: 'inherit', + collapsibleNavGroupBackground: dark_background_colors.backgroundBaseSubdued, collapsibleNavGroupBackgroundDark: dark_background_colors.backgroundBaseSubdued, @@ -178,6 +232,11 @@ export const components: _EuiThemeComponents = { listGroupItemBackgroundPrimaryHover: dark_background_colors.backgroundBaseInteractiveHover, + loadingChartMonoBackground0: SEMANTIC_COLORS.shade95, + loadingChartMonoBackground1: SEMANTIC_COLORS.shade105, + loadingChartMonoBackground2: SEMANTIC_COLORS.shade115, + loadingChartMonoBackground3: SEMANTIC_COLORS.shade125, + markBackground: dark_background_colors.backgroundLightPrimary, markdownFormatTableBorderColor: dark_border_colors.borderBasePlain, diff --git a/packages/eui-theme-borealis/src/variables/_index.scss b/packages/eui-theme-borealis/src/variables/_index.scss index fbb64ce0c50..2e2f9eb8db3 100644 --- a/packages/eui-theme-borealis/src/variables/_index.scss +++ b/packages/eui-theme-borealis/src/variables/_index.scss @@ -2,6 +2,7 @@ @import 'node_modules/@elastic/eui-theme-common/src/global_styling/variables/index'; @import 'states'; +@import './colors/colors_vis'; @import 'borders'; @import 'form'; @import 'page'; diff --git a/packages/eui-theme-borealis/src/variables/colors/_colors_vis.scss b/packages/eui-theme-borealis/src/variables/colors/_colors_vis.scss new file mode 100644 index 00000000000..a06c6f53367 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_colors_vis.scss @@ -0,0 +1,72 @@ +// Visualization colors +// stylelint-disable color-no-hex + +// Maps allow for easier JSON usage +// Use map_merge($euiColorVisColors, $yourMap) to change individual colors after importing ths file +// The `behindText` variant is a direct copy of the hex output by the JS euiPaletteColorBlindBehindText() function +$euiPaletteColorBlind: ( + euiColorVis0: ( + graphic: #00BEB8, + behindText: #00BEB8, + ), + euiColorVis1: ( + graphic: #93E5E0, + behindText: #93E5E0, + ), + euiColorVis2: ( + graphic: #599DFF, + behindText: #599DFF, + ), + euiColorVis3: ( + graphic: #B4D5FF, + behindText: #B4D5FF, + ), + euiColorVis4: ( + graphic: #ED6BA2, + behindText: #ED6BA2, + ), + euiColorVis5: ( + graphic: #FFBED5, + behindText: #FFBED5, + ), + euiColorVis6: ( + graphic: #F66D64, + behindText: #F66D64, + ), + euiColorVis7: ( + graphic: #FFC0B8, + behindText: #FFC0B8, + ), + euiColorVis8: ( + graphic: #ED9E00, + behindText: #ED9E00, + ), + euiColorVis9: ( + graphic: #FFD569, + behindText: #FFD569, + ) +) !default; + +$euiPaletteColorBlindKeys: map-keys($euiPaletteColorBlind); + +$euiColorVis0: map-get(map-get($euiPaletteColorBlind, 'euiColorVis0'), 'graphic') !default; +$euiColorVis1: map-get(map-get($euiPaletteColorBlind, 'euiColorVis1'), 'graphic') !default; +$euiColorVis2: map-get(map-get($euiPaletteColorBlind, 'euiColorVis2'), 'graphic') !default; +$euiColorVis3: map-get(map-get($euiPaletteColorBlind, 'euiColorVis3'), 'graphic') !default; +$euiColorVis4: map-get(map-get($euiPaletteColorBlind, 'euiColorVis4'), 'graphic') !default; +$euiColorVis5: map-get(map-get($euiPaletteColorBlind, 'euiColorVis5'), 'graphic') !default; +$euiColorVis6: map-get(map-get($euiPaletteColorBlind, 'euiColorVis6'), 'graphic') !default; +$euiColorVis7: map-get(map-get($euiPaletteColorBlind, 'euiColorVis7'), 'graphic') !default; +$euiColorVis8: map-get(map-get($euiPaletteColorBlind, 'euiColorVis8'), 'graphic') !default; +$euiColorVis9: map-get(map-get($euiPaletteColorBlind, 'euiColorVis9'), 'graphic') !default; + +$euiColorVis0_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis0'), 'behindText') !default; +$euiColorVis1_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis1'), 'behindText') !default; +$euiColorVis2_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis2'), 'behindText') !default; +$euiColorVis3_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis3'), 'behindText') !default; +$euiColorVis4_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis4'), 'behindText') !default; +$euiColorVis5_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis5'), 'behindText') !default; +$euiColorVis6_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis6'), 'behindText') !default; +$euiColorVis7_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis7'), 'behindText') !default; +$euiColorVis8_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis8'), 'behindText') !default; +$euiColorVis9_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis9'), 'behindText') !default; diff --git a/packages/eui-theme-borealis/src/variables/colors/_colors_vis.ts b/packages/eui-theme-borealis/src/variables/colors/_colors_vis.ts new file mode 100644 index 00000000000..1a595684a44 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_colors_vis.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { _EuiThemeVisColors } from '@elastic/eui-theme-common'; + +import { SEMANTIC_COLORS } from './_semantic_colors'; +import { PRIMITIVE_COLORS } from './_primitive_colors'; + +// Maps allow for easier JSON usage +// Use map_merge(euiColorVisColors, $yourMap) to change individual colors after importing ths file +// The `behindText` variant is a direct copy of the hex output by the JS euiPaletteColorBlindBehindText() function +const euiPaletteColorBlind = { + euiColorVis0: { + graphic: SEMANTIC_COLORS.accentSecondary60, + }, + euiColorVis1: { + graphic: SEMANTIC_COLORS.accentSecondary30, + }, + euiColorVis2: { + graphic: SEMANTIC_COLORS.primary60, + }, + euiColorVis3: { + graphic: SEMANTIC_COLORS.primary30, + }, + euiColorVis4: { + graphic: SEMANTIC_COLORS.accent60, + }, + euiColorVis5: { + graphic: SEMANTIC_COLORS.accent30, + }, + euiColorVis6: { + graphic: SEMANTIC_COLORS.danger60, + }, + euiColorVis7: { + graphic: SEMANTIC_COLORS.danger30, + }, + euiColorVis8: { + graphic: SEMANTIC_COLORS.warning60, + }, + euiColorVis9: { + graphic: SEMANTIC_COLORS.warning30, + }, +}; + +export const colorVis: _EuiThemeVisColors = { + euiColorVis0: euiPaletteColorBlind.euiColorVis0.graphic, + euiColorVis1: euiPaletteColorBlind.euiColorVis1.graphic, + euiColorVis2: euiPaletteColorBlind.euiColorVis2.graphic, + euiColorVis3: euiPaletteColorBlind.euiColorVis3.graphic, + euiColorVis4: euiPaletteColorBlind.euiColorVis4.graphic, + euiColorVis5: euiPaletteColorBlind.euiColorVis5.graphic, + euiColorVis6: euiPaletteColorBlind.euiColorVis6.graphic, + euiColorVis7: euiPaletteColorBlind.euiColorVis7.graphic, + euiColorVis8: euiPaletteColorBlind.euiColorVis8.graphic, + euiColorVis9: euiPaletteColorBlind.euiColorVis9.graphic, + + euiColorVisAsTextLight0: SEMANTIC_COLORS.accentSecondary100, + euiColorVisAsTextLight1: SEMANTIC_COLORS.primary100, + euiColorVisAsTextLight2: SEMANTIC_COLORS.accent100, + euiColorVisAsTextLight3: SEMANTIC_COLORS.danger100, + euiColorVisAsTextLight4: SEMANTIC_COLORS.warning100, + euiColorVisAsTextLight5: SEMANTIC_COLORS.success100, + euiColorVisAsTextLight6: SEMANTIC_COLORS.assistance100, + + euiColorVisAsTextDark0: SEMANTIC_COLORS.accentSecondary60, + euiColorVisAsTextDark1: SEMANTIC_COLORS.primary60, + euiColorVisAsTextDark2: SEMANTIC_COLORS.accent60, + euiColorVisAsTextDark3: SEMANTIC_COLORS.danger60, + euiColorVisAsTextDark4: SEMANTIC_COLORS.warning60, + euiColorVisAsTextDark5: SEMANTIC_COLORS.success60, + euiColorVisAsTextDark6: SEMANTIC_COLORS.assistance60, + + euiColorVisSuccess0: SEMANTIC_COLORS.success60, + euiColorVisSuccess1: SEMANTIC_COLORS.success30, + euiColorVisWarning0: SEMANTIC_COLORS.warning30, + euiColorVisDanger0: SEMANTIC_COLORS.danger60, + euiColorVisDanger1: SEMANTIC_COLORS.danger30, + + euiColorVisNeutral0: PRIMITIVE_COLORS.mutedGrey10, + + euiColorVisGrey0: PRIMITIVE_COLORS.blueGrey30, + euiColorVisGrey1: PRIMITIVE_COLORS.blueGrey60, + euiColorVisGrey2: PRIMITIVE_COLORS.blueGrey90, + euiColorVisGrey3: PRIMITIVE_COLORS.blueGrey130, + + euiColorVisWarm0: SEMANTIC_COLORS.danger10, + euiColorVisWarm1: SEMANTIC_COLORS.danger40, + euiColorVisWarm2: SEMANTIC_COLORS.danger60, + + euiColorVisCool0: SEMANTIC_COLORS.primary10, + euiColorVisCool1: SEMANTIC_COLORS.primary40, + euiColorVisCool2: SEMANTIC_COLORS.primary60, + + euiColorVisComplementary0: SEMANTIC_COLORS.primary60, + euiColorVisComplementary1: SEMANTIC_COLORS.warning60, +}; diff --git a/packages/eui-theme-borealis/src/variables/colors/index.ts b/packages/eui-theme-borealis/src/variables/colors/index.ts index a9b746d160d..e59c0fbff87 100644 --- a/packages/eui-theme-borealis/src/variables/colors/index.ts +++ b/packages/eui-theme-borealis/src/variables/colors/index.ts @@ -11,10 +11,12 @@ import type { _EuiThemeColors } from '@elastic/eui-theme-common'; import { SEMANTIC_COLORS } from './_semantic_colors'; import { light_colors } from './_colors_light'; import { dark_colors } from './_colors_dark'; +import { colorVis } from './_colors_vis'; export const colors: _EuiThemeColors = { ghost: SEMANTIC_COLORS.plainLight, ink: SEMANTIC_COLORS.plainDark, LIGHT: light_colors, DARK: dark_colors, + vis: colorVis, }; diff --git a/packages/eui-theme-common/package.json b/packages/eui-theme-common/package.json index 86befe61e5f..57b020cf3b2 100644 --- a/packages/eui-theme-common/package.json +++ b/packages/eui-theme-common/package.json @@ -22,6 +22,10 @@ "directory": "packages/eui-theme-common" }, "private": true, + "dependencies": { + "@types/lodash": "^4.14.202", + "lodash": "^4.17.21" + }, "devDependencies": { "@babel/cli": "^7.21.5", "@babel/core": "^7.21.8", diff --git a/packages/eui-theme-common/src/global_styling/variables/_index.scss b/packages/eui-theme-common/src/global_styling/variables/_index.scss index 993979a78a5..2397647bde9 100644 --- a/packages/eui-theme-common/src/global_styling/variables/_index.scss +++ b/packages/eui-theme-common/src/global_styling/variables/_index.scss @@ -9,7 +9,6 @@ // Global colors are established and importer per theme, before this manifest // Import order is important. Size, vis colors, ...etc are used in other variables. @import 'size'; -@import 'colors_vis'; @import 'animations'; @import 'font_weight'; @import 'typography'; diff --git a/packages/eui-theme-common/src/global_styling/variables/colors.ts b/packages/eui-theme-common/src/global_styling/variables/colors.ts index d77ad66f88d..b0d36f3eab8 100644 --- a/packages/eui-theme-common/src/global_styling/variables/colors.ts +++ b/packages/eui-theme-common/src/global_styling/variables/colors.ts @@ -252,6 +252,59 @@ export type _EuiThemeBorderColors = { borderStrongDanger: ColorModeSwitch; }; +export type _EuiThemeVisColors = { + euiColorVis0: string; + euiColorVis1: string; + euiColorVis2: string; + euiColorVis3: string; + euiColorVis4: string; + euiColorVis5: string; + euiColorVis6: string; + euiColorVis7: string; + euiColorVis8: string; + euiColorVis9: string; + + euiColorVisAsTextLight0: string; + euiColorVisAsTextLight1: string; + euiColorVisAsTextLight2: string; + euiColorVisAsTextLight3: string; + euiColorVisAsTextLight4: string; + euiColorVisAsTextLight5: string; + euiColorVisAsTextLight6: string; + + euiColorVisAsTextDark0: string; + euiColorVisAsTextDark1: string; + euiColorVisAsTextDark2: string; + euiColorVisAsTextDark3: string; + euiColorVisAsTextDark4: string; + euiColorVisAsTextDark5: string; + euiColorVisAsTextDark6: string; + + euiColorVisSuccess0: string; + euiColorVisSuccess1: string; + euiColorVisWarning0: string; + euiColorVisDanger0: string; + euiColorVisDanger1: string; + + euiColorVisNeutral0: string; + + euiColorVisGrey0: string; + euiColorVisGrey1: string; + euiColorVisGrey2: string; + euiColorVisGrey3: string; + + euiColorVisWarm0: string; + euiColorVisWarm1: string; + euiColorVisWarm2: string; + + euiColorVisCool0: string; + euiColorVisCool1: string; + euiColorVisCool2: string; + + euiColorVisComplementary0: string; + euiColorVisComplementary1: string; +}; + export type _EuiThemeConstantColors = { /** * Purest **white** @@ -263,6 +316,7 @@ export type _EuiThemeConstantColors = { * @deprecated */ ink: string; + vis: _EuiThemeVisColors; }; export type _EuiThemeColorsMode = _EuiThemeBrandColors & diff --git a/packages/eui-theme-common/src/global_styling/variables/components.ts b/packages/eui-theme-common/src/global_styling/variables/components.ts index 59901bae4f4..ad0681fbec4 100644 --- a/packages/eui-theme-common/src/global_styling/variables/components.ts +++ b/packages/eui-theme-common/src/global_styling/variables/components.ts @@ -24,6 +24,30 @@ export type _EuiThemeComponentColors = { breadcrumbsApplicationBackground: ColorModeSwitch; breadcrumbsApplicationColor: ColorModeSwitch; + codeBackground: ColorModeSwitch; + codeBackgroundSelected: ColorModeSwitch; + codeColor: ColorModeSwitch; + codeInlineColor: ColorModeSwitch; + codeCommentColor: ColorModeSwitch; + codeSelectorColor: ColorModeSwitch; + codeStringColor: ColorModeSwitch; + codeTagColor: ColorModeSwitch; + codeNameColor: ColorModeSwitch; + codeNumberColor: ColorModeSwitch; + codeKeywordColor: ColorModeSwitch; + codeFunctionTitleColor: ColorModeSwitch; + codeTypeColor: ColorModeSwitch; + codeAttributeColor: ColorModeSwitch; + codeSymbolColor: ColorModeSwitch; + codeParamsColor: ColorModeSwitch; + codeMetaColor: ColorModeSwitch; + codeTitleColor: ColorModeSwitch; + codeSectionColor: ColorModeSwitch; + codeAdditionColor: ColorModeSwitch; + codeDeletionColor: ColorModeSwitch; + codeSelectorClassColor: ColorModeSwitch; + codeSelectorIdColor: ColorModeSwitch; + collapsibleNavGroupBackground: ColorModeSwitch; collapsibleNavGroupBackgroundDark: ColorModeSwitch; @@ -50,6 +74,11 @@ export type _EuiThemeComponentColors = { listGroupItemBackgroundHover: ColorModeSwitch; listGroupItemBackgroundPrimaryHover: ColorModeSwitch; + loadingChartMonoBackground0: ColorModeSwitch; + loadingChartMonoBackground1: ColorModeSwitch; + loadingChartMonoBackground2: ColorModeSwitch; + loadingChartMonoBackground3: ColorModeSwitch; + markBackground: ColorModeSwitch; markdownFormatTableBorderColor: ColorModeSwitch; diff --git a/packages/eui-theme-common/src/global_styling/variables/flags.ts b/packages/eui-theme-common/src/global_styling/variables/flags.ts index 355d94c2568..e56ed84a9f1 100644 --- a/packages/eui-theme-common/src/global_styling/variables/flags.ts +++ b/packages/eui-theme-common/src/global_styling/variables/flags.ts @@ -11,4 +11,5 @@ */ export type _EuiThemeFlags = { hasGlobalFocusColor: boolean; + hasVisColorAdjustment: boolean; }; diff --git a/packages/eui-theme-common/src/global_styling/variables/index.ts b/packages/eui-theme-common/src/global_styling/variables/index.ts index 90969015769..4050b351e3d 100644 --- a/packages/eui-theme-common/src/global_styling/variables/index.ts +++ b/packages/eui-theme-common/src/global_styling/variables/index.ts @@ -10,7 +10,6 @@ export * from './animations'; export * from './borders'; export * from './breakpoint'; export * from './colors'; -export * from './_colors_vis'; export * from './levels'; export * from './size'; export * from './shadow'; diff --git a/packages/eui-theme-common/src/index.ts b/packages/eui-theme-common/src/index.ts index 05e235c669b..ae83a067c24 100644 --- a/packages/eui-theme-common/src/index.ts +++ b/packages/eui-theme-common/src/index.ts @@ -7,6 +7,7 @@ */ export * from './global_styling'; +export * from './services'; export * from './types'; export * from './utils'; diff --git a/packages/eui-theme-common/src/services/index.ts b/packages/eui-theme-common/src/services/index.ts new file mode 100644 index 00000000000..464a0a1b58b --- /dev/null +++ b/packages/eui-theme-common/src/services/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './vis_color_store'; diff --git a/packages/eui-theme-common/src/services/vis_color_store.test.ts b/packages/eui-theme-common/src/services/vis_color_store.test.ts new file mode 100644 index 00000000000..6405d4e26e2 --- /dev/null +++ b/packages/eui-theme-common/src/services/vis_color_store.test.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { _EuiThemeVisColors } from '../global_styling'; +import { EuiVisColorStore, VIS_COLOR_STORE_EVENTS } from './vis_color_store'; + +jest.useFakeTimers(); + +const visColors: _EuiThemeVisColors = { + euiColorVis0: '#aabbcc', +} as _EuiThemeVisColors; + +describe('EuiVisColorStore', () => { + const visColorStore = EuiVisColorStore.getInstance(visColors, false); + let updateFn: jest.Mock; + + beforeEach(() => { + updateFn = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('setVisColors()', () => { + const customColors = { + ...visColors, + euiColorVis0: '#00ff00', + }; + + it('has initial colors', () => { + expect(visColorStore.visColors).toEqual(visColors); + }); + + it('updates colors', () => { + visColorStore.setVisColors(customColors); + + expect(visColorStore.visColors).toEqual(customColors); + }); + + it('updates hasVisColorAdjustment', () => { + visColorStore.setVisColors(customColors, true); + + expect(visColorStore.hasVisColorAdjustment).toEqual(true); + }); + }); + + describe('subcribe and unsubscribe', () => { + it('subscribes to store updates', () => { + const customColors = { + ...visColors, + euiColorVis0: '#ff0000', + }; + + const id = visColorStore.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + updateFn + ); + + expect(id).toContain('UPDATE'); + + visColorStore.setVisColors(customColors); + jest.runAllTimers(); + + expect(updateFn).toHaveBeenCalledTimes(1); + expect(updateFn).toHaveBeenCalledWith(customColors); + }); + + it('unsubscribes from store updates', () => { + const customColors = { + ...visColors, + euiColorVis0: '#0000ff', + }; + + const id = visColorStore.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + updateFn + ); + + expect(id).toContain('UPDATE'); + + visColorStore.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, id); + + visColorStore.setVisColors(customColors); + jest.runAllTimers(); + + expect(updateFn).toHaveBeenCalledTimes(0); + }); + }); +}); diff --git a/packages/eui-theme-common/src/services/vis_color_store.ts b/packages/eui-theme-common/src/services/vis_color_store.ts new file mode 100644 index 00000000000..57c6d3d62b4 --- /dev/null +++ b/packages/eui-theme-common/src/services/vis_color_store.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import isEqual from 'lodash/isEqual'; + +import { _EuiThemeVisColors } from '../global_styling'; +import { uniqueId } from 'lodash'; + +export const VIS_COLOR_STORE_EVENTS = { + UPDATE: 'UPDATE', +} as const; +export type VisColorStoreEvents = keyof typeof VIS_COLOR_STORE_EVENTS; +type EventId = string; + +export type _EuiVisColorStore = { + visColors: _EuiThemeVisColors; + hasVisColorAdjustment: boolean; + setVisColors: ( + colors: _EuiThemeVisColors, + hasVisColorAdjustment?: boolean + ) => void; + subscribe: (eventName: VisColorStoreEvents, callback: any) => EventId; + unsubscribe: (eventName: VisColorStoreEvents, id: EventId) => void; +}; + +class EuiVisColorStoreImpl implements _EuiVisColorStore { + private _visColors: _EuiVisColorStore['visColors']; + private _hasVisColorAdjustment: _EuiVisColorStore['hasVisColorAdjustment']; + private events: Record> = {} as Record< + VisColorStoreEvents, + Map + >; + + constructor(dependencies: { + defaultColors: _EuiThemeVisColors; + hasVisColorAdjustment: boolean; + }) { + this._visColors = dependencies.defaultColors; + this._hasVisColorAdjustment = dependencies.hasVisColorAdjustment; + } + + get visColors(): _EuiVisColorStore['visColors'] { + return this._visColors; + } + + get hasVisColorAdjustment(): _EuiVisColorStore['hasVisColorAdjustment'] { + return this._hasVisColorAdjustment; + } + + setVisColors = (colors: _EuiThemeVisColors, hasColorAdjustment?: boolean) => { + if ( + !isEqual(this._visColors, colors) || + hasColorAdjustment !== this._hasVisColorAdjustment + ) { + this._visColors = colors; + + if (hasColorAdjustment != null) { + this._hasVisColorAdjustment = hasColorAdjustment; + } + + this.publishUpdate(VIS_COLOR_STORE_EVENTS.UPDATE, this._visColors); + } + }; + + subscribe = (eventName: VisColorStoreEvents, callback: NonNullable) => { + if (this.events[eventName] == null) { + this.events[eventName] = new Map(); + } + + const id = uniqueId(`${eventName}_`); + + this.events[eventName].set(id, callback); + return id; + }; + + unsubscribe = (eventName: VisColorStoreEvents, id: EventId) => { + if (this.events[eventName] != null && this.events[eventName].has(id)) { + this.events[eventName].delete(id); + } + }; + + private publishUpdate = ( + eventName: VisColorStoreEvents, + colors?: _EuiThemeVisColors + ) => { + if (this.events[eventName]) + this.events[eventName].forEach((callback) => { + // use timeout to execute callbacks after theme updates + setTimeout(() => { + callback?.(colors); + }); + }); + }; +} + +export class EuiVisColorStore { + private static instance: EuiVisColorStoreImpl; + + static getInstance( + defaultColors: _EuiThemeVisColors, + hasVisColorAdjustment: boolean + ): EuiVisColorStoreImpl { + if (!this.instance) { + this.instance = new EuiVisColorStoreImpl({ + defaultColors, + hasVisColorAdjustment, + }); + } + + return this.instance; + } +} diff --git a/packages/eui/src-docs/src/components/guide_locale_selector/guide_locale_selector.js b/packages/eui/src-docs/src/components/guide_locale_selector/guide_locale_selector.js deleted file mode 100644 index 4b53fc35199..00000000000 --- a/packages/eui/src-docs/src/components/guide_locale_selector/guide_locale_selector.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import moment from 'moment'; -import { translateUsingPseudoLocale } from '../../../src/services/string/pseudo_locale_translator'; - -// For testing/demoing EuiDatePicker, process moment's `en` locale config into a babelfished version -const enConfig = moment.localeData('en')._config; -moment.defineLocale('en-xa', { - ...enConfig, - months: enConfig.months.map(translateUsingPseudoLocale), - monthsShort: enConfig.monthsShort.map(translateUsingPseudoLocale), - weekdays: enConfig.weekdays.map(translateUsingPseudoLocale), - weekdaysMin: enConfig.weekdaysMin.map(translateUsingPseudoLocale), - weekdaysShort: enConfig.weekdaysShort.map(translateUsingPseudoLocale), -}); -// Reset default moment locale after using `defineLocale` -moment.locale('en'); - -import { EuiSwitch, EuiToolTip } from '../../../../src/components'; - -export const GuideLocaleSelector = ({ selectedLocale, onToggleLocale }) => { - return ( - - - onToggleLocale(selectedLocale === 'en' ? 'en-xa' : 'en') - } - /> - - ); -}; - -GuideLocaleSelector.propTypes = { - onToggleLocale: PropTypes.func.isRequired, - selectedLocale: PropTypes.string.isRequired, -}; diff --git a/packages/eui/src-docs/src/components/guide_locale_selector/index.js b/packages/eui/src-docs/src/components/guide_locale_selector/index.js deleted file mode 100644 index 7f4a4fbcfb4..00000000000 --- a/packages/eui/src-docs/src/components/guide_locale_selector/index.js +++ /dev/null @@ -1 +0,0 @@ -export { GuideLocaleSelector } from './guide_locale_selector'; diff --git a/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx b/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx index 0f99e738533..88a73c1c4bc 100644 --- a/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx +++ b/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx @@ -1,79 +1,39 @@ /* eslint-disable no-restricted-globals */ -import React, { useState } from 'react'; +import React, { useState, useContext } from 'react'; -import { - EuiThemeProvider, - useEuiTheme, - useIsWithinBreakpoints, -} from '../../../../src/services'; +import { EuiThemeProvider, useEuiTheme } from '../../../../src/services'; import { EUI_THEME } from '../../../../src/themes'; -import { AVAILABLE_THEMES } from '../with_theme/theme_context'; import { ThemeContext } from '../with_theme'; -// @ts-ignore Not TS -import { GuideLocaleSelector } from '../guide_locale_selector'; import { EuiPopover, EuiHorizontalRule, EuiButton, EuiContextMenuPanel, EuiContextMenuItem, + EuiSwitch, + EuiSwitchEvent, } from '../../../../src/components'; +import { AVAILABLE_THEMES } from '../with_theme/theme_context'; type GuideThemeSelectorProps = { - onToggleLocale: () => {}; + onToggleLocale: Function; selectedLocale: string; - context?: any; }; export const GuideThemeSelector: React.FunctionComponent< GuideThemeSelectorProps -> = ({ ...rest }) => { - return ( - - {(context) => } - - ); -}; - -const GuideThemeSelectorComponent: React.FunctionComponent< - GuideThemeSelectorProps -> = ({ context, onToggleLocale, selectedLocale }) => { - const isMobileSize = useIsWithinBreakpoints(['xs', 's']); - const [isPopoverOpen, setPopover] = useState(false); - - const onButtonClick = () => { - setPopover(!isPopoverOpen); - }; - - const closePopover = () => { - setPopover(false); - }; - - const systemColorMode = useEuiTheme().colorMode.toLowerCase(); +> = ({ onToggleLocale, selectedLocale }) => { + const context = useContext(ThemeContext); + const euiThemeContext = useEuiTheme(); + const colorMode = context.colorMode ?? euiThemeContext.colorMode; const currentTheme: EUI_THEME = - AVAILABLE_THEMES.find( - (theme) => theme.value === (context.theme ?? systemColorMode) - ) || AVAILABLE_THEMES[0]; - - const getIconType = (value: EUI_THEME['value']) => { - return value === currentTheme.value ? 'check' : 'empty'; - }; + AVAILABLE_THEMES.find((theme) => theme.value === context.theme) || + AVAILABLE_THEMES[0]; - const items = AVAILABLE_THEMES.map((theme) => { - return ( - { - closePopover(); - context.changeTheme(theme.value); - }} - > - {theme.text} - - ); - }); + const [isPopoverOpen, setPopover] = useState(false); + const onButtonClick = () => setPopover(!isPopoverOpen); + const closePopover = () => setPopover(false); const button = ( @@ -85,11 +45,28 @@ const GuideThemeSelectorComponent: React.FunctionComponent< minWidth={0} onClick={onButtonClick} > - {isMobileSize ? 'Theme' : currentTheme.text} + Theme ); + const toggles = [ + { + label: 'Dark mode', + checked: colorMode.toLowerCase() === 'dark', + onChange: (e: EuiSwitchEvent) => + context.setContext({ + colorMode: e.target.checked ? 'DARK' : 'LIGHT', + }), + }, + location.host.includes('803') && { + label: 'i18n testing', + checked: selectedLocale === 'en-xa', + onChange: (e: EuiSwitchEvent) => + onToggleLocale(e.target.checked ? 'en-xa' : 'en'), + }, + ]; + return ( - - {location.host.includes('803') && ( - <> - -
- { + return ( + { + context.setContext({ theme: theme.value }); + }} + > + {theme.text} + + ); + })} + /> + + {toggles.map((item) => + item ? ( +
({ padding: euiTheme.size.s })}> +
- + ) : null )} ); diff --git a/packages/eui/src-docs/src/components/with_theme/index.ts b/packages/eui/src-docs/src/components/with_theme/index.ts index 46d6e96d7c1..7cc4f114627 100644 --- a/packages/eui/src-docs/src/components/with_theme/index.ts +++ b/packages/eui/src-docs/src/components/with_theme/index.ts @@ -1,2 +1,6 @@ -export { ThemeProvider, ThemeContext } from './theme_context'; +export { + ThemeProvider, + ThemeContext, + type ThemeContextType, +} from './theme_context'; export { LanguageSelector } from './language_selector'; diff --git a/packages/eui/src-docs/src/components/with_theme/language_selector.tsx b/packages/eui/src-docs/src/components/with_theme/language_selector.tsx index 45931a8d423..aea26bd226b 100644 --- a/packages/eui/src-docs/src/components/with_theme/language_selector.tsx +++ b/packages/eui/src-docs/src/components/with_theme/language_selector.tsx @@ -1,12 +1,6 @@ -import React, { useContext, useState } from 'react'; +import React, { useContext } from 'react'; -import { - EuiButtonGroup, - EuiIcon, - EuiLink, - EuiText, - EuiTourStep, -} from '../../../../src/components'; +import { EuiButtonGroup } from '../../../../src/components'; import { ThemeContext, @@ -14,62 +8,28 @@ import { THEME_LANGUAGES, } from './theme_context'; -const NOTIF_STORAGE_KEY = 'js_vs_sass_notification'; -const NOTIF_STORAGE_VALUE = 'dismissed'; - export const LanguageSelector = ({ onChange, - showTour = false, }: { onChange?: (id: string) => void; - showTour?: boolean; }) => { const themeContext = useContext(ThemeContext); const toggleIdSelected = themeContext.themeLanguage; const onLanguageChange = (optionId: string) => { - themeContext.changeThemeLanguage(optionId as THEME_LANGUAGES['id']); + themeContext.setContext({ + themeLanguage: optionId as THEME_LANGUAGES['id'], + }); onChange?.(optionId); - setTourIsOpen(false); - localStorage.setItem(NOTIF_STORAGE_KEY, NOTIF_STORAGE_VALUE); - }; - - const [isTourOpen, setTourIsOpen] = useState( - localStorage.getItem(NOTIF_STORAGE_KEY) === NOTIF_STORAGE_VALUE - ? false - : showTour - ); - - const onTourDismiss = () => { - setTourIsOpen(false); - localStorage.setItem(NOTIF_STORAGE_KEY, NOTIF_STORAGE_VALUE); }; return ( - -

Select your preferred styling language with this toggle button.

- - } - isStepOpen={isTourOpen} - onFinish={onTourDismiss} - step={1} - stepsTotal={1} - title={ - <> -   Theming update - - } - footerAction={Got it!} - > - onLanguageChange(id)} - /> -
+ onLanguageChange(id)} + /> ); }; diff --git a/packages/eui/src-docs/src/components/with_theme/theme_context.tsx b/packages/eui/src-docs/src/components/with_theme/theme_context.tsx index 2fac3a5c545..6352458b1a1 100644 --- a/packages/eui/src-docs/src/components/with_theme/theme_context.tsx +++ b/packages/eui/src-docs/src/components/with_theme/theme_context.tsx @@ -1,40 +1,64 @@ import React, { PropsWithChildren } from 'react'; -import { EuiThemeBorealis } from '@elastic/eui-theme-borealis'; - +import { + EUI_THEME_BOREALIS_KEY, + EuiThemeBorealis, +} from '@elastic/eui-theme-borealis'; import { EUI_THEMES, EUI_THEME, + AMSTERDAM_NAME_KEY, isExperimentalThemeEnabled, } from '../../../../src/themes'; +import { EuiThemeColorModeStandard } from '../../../../src/services'; // @ts-ignore importing from a JS file -import { applyTheme } from '../../services'; - -const STYLE_STORAGE_KEY = 'js_vs_sass_preference'; -const URL_PARAM_KEY = 'themeLanguage'; +import { applyTheme, registerTheme } from '../../services'; + +// @ts-ignore Sass +import amsterdamThemeLight from '../../theme_light.scss'; +// @ts-ignore Sass +import borealisThemeLight from '../../theme_borealis_light.scss'; +// @ts-ignore Sass +import amsterdamThemeDark from '../../theme_dark.scss'; +// @ts-ignore Sass +import borealisThemeDark from '../../theme_borealis_dark.scss'; + +const THEME_CSS_MAP = { + [AMSTERDAM_NAME_KEY]: { + LIGHT: amsterdamThemeLight, + DARK: amsterdamThemeDark, + }, + [EUI_THEME_BOREALIS_KEY]: { + LIGHT: borealisThemeLight, + DARK: borealisThemeDark, + }, +}; const EXPERIMENTAL_THEMES: EUI_THEME[] = isExperimentalThemeEnabled() ? [ { - text: 'Borealis (Light)', - value: `${EuiThemeBorealis.key}_LIGHT`, - provider: EuiThemeBorealis, - }, - { - text: 'Borealis (Dark)', - value: `${EuiThemeBorealis.key}_DARK`, + text: 'Borealis', + value: EuiThemeBorealis.key, provider: EuiThemeBorealis, }, ] : []; export const AVAILABLE_THEMES = [...EUI_THEMES, ...EXPERIMENTAL_THEMES]; +const THEME_NAMES = AVAILABLE_THEMES.map(({ value }) => value); + +AVAILABLE_THEMES.forEach((theme) => { + registerTheme( + theme.value, + THEME_CSS_MAP[theme.value as keyof typeof THEME_CSS_MAP] + ); +}); +const URL_PARAM_KEY = 'themeLanguage'; export type THEME_LANGUAGES = { id: 'language--js' | 'language--sass'; label: string; title: string; }; - export const theme_languages: THEME_LANGUAGES[] = [ { id: 'language--js', @@ -47,24 +71,22 @@ export const theme_languages: THEME_LANGUAGES[] = [ title: 'Language selector: Sass', }, ]; - -const THEME_NAMES = AVAILABLE_THEMES.map(({ value }) => value); const THEME_LANGS = theme_languages.map(({ id }) => id); -type ThemeContextType = { +export type ThemeContextType = { theme?: EUI_THEME['value']; - changeTheme: (themeValue: EUI_THEME['value']) => void; + colorMode?: EuiThemeColorModeStandard; themeLanguage: THEME_LANGUAGES['id']; - changeThemeLanguage: (language: THEME_LANGUAGES['id']) => void; + setContext: (context: Partial) => void; }; export const ThemeContext = React.createContext({ theme: undefined, - changeTheme: () => {}, + colorMode: undefined, themeLanguage: THEME_LANGS[0], - changeThemeLanguage: () => {}, + setContext: () => {}, }); -type State = Pick; +type State = Pick; export class ThemeProvider extends React.Component { constructor(props: object) { @@ -73,21 +95,45 @@ export class ThemeProvider extends React.Component { const theme = localStorage.getItem('theme') || undefined; applyTheme(theme && THEME_NAMES.includes(theme) ? theme : THEME_NAMES[0]); + const colorMode = + (localStorage.getItem('colorMode') as EuiThemeColorModeStandard) || + undefined; + const themeLanguage = this.getThemeLanguage(); this.state = { theme, + colorMode, themeLanguage, }; } - changeTheme = (themeValue: EUI_THEME['value']) => { - this.setState({ theme: themeValue }, () => { - localStorage.setItem('theme', themeValue); - applyTheme(themeValue); - }); + setContext = (state: Partial) => { + this.setState(state as State); }; + componentDidUpdate(_prevProps: never, prevState: State) { + const stateToSetInLocalStorage = [ + 'theme', + 'colorMode', + 'themeLanguage', + ] as const; + + stateToSetInLocalStorage.forEach((key) => { + if (prevState[key] !== this.state[key]) { + localStorage.setItem(key, String(this.state[key])); + + // Side effects + if (key === 'theme') { + applyTheme(this.state.theme); + } + if (key === 'themeLanguage') { + this.setThemeLanguageParam(this.state.themeLanguage!); + } + } + }); + } + getThemeLanguage = () => { // Allow theme language to be set by URL param, so we can link people // to specific docs, e.g. ?themeLanguage=js, ?themeLanguage=sass @@ -95,7 +141,7 @@ export class ThemeProvider extends React.Component { const urlParams = window?.location?.href?.split('?')[1]; // Note: we can't use location.search because of our hash router const fromUrlParam = new URLSearchParams(urlParams).get(URL_PARAM_KEY); // Otherwise, obtain it from localStorage - const fromLocalStorage = localStorage.getItem(STYLE_STORAGE_KEY); + const fromLocalStorage = localStorage.getItem(URL_PARAM_KEY); let themeLanguage = ( fromUrlParam ? `language--${fromUrlParam}` : fromLocalStorage @@ -119,23 +165,16 @@ export class ThemeProvider extends React.Component { window.location.hash = `${hash[0]}?${params.toString()}`; }; - changeThemeLanguage = (language: THEME_LANGUAGES['id']) => { - this.setState({ themeLanguage: language }, () => { - localStorage.setItem(STYLE_STORAGE_KEY, language); - this.setThemeLanguageParam(language); - }); - }; - render() { const { children } = this.props; - const { theme, themeLanguage } = this.state; + const { theme, colorMode, themeLanguage } = this.state; return ( {children} diff --git a/packages/eui/src-docs/src/index.js b/packages/eui/src-docs/src/index.js index d9b072fccf4..7132f648180 100644 --- a/packages/eui/src-docs/src/index.js +++ b/packages/eui/src-docs/src/index.js @@ -4,35 +4,16 @@ import { Provider } from 'react-redux'; import { HashRouter, Switch, Route, Redirect } from 'react-router-dom'; import { Helmet } from 'react-helmet'; -import { isExperimentalThemeEnabled } from '../../src/themes'; - import configureStore from './store/configure_store'; import { AppContext } from './views/app_context'; import { AppView } from './views/app_view'; import { HomeView } from './views/home/home_view'; import { NotFoundView } from './views/not_found/not_found_view'; -import { registerTheme, ExampleContext } from './services'; +import { ExampleContext } from './services'; import Routes from './routes'; -import { - ThemeProvider, - AVAILABLE_THEMES, -} from './components/with_theme/theme_context'; - -// TODO: update SCSS files for new theme once available -import themeLight from './theme_light.scss'; -import themeDark from './theme_dark.scss'; -import themeNewLight from './theme_new_light.scss'; -import themeNewDark from './theme_new_dark.scss'; - -registerTheme('light', [themeLight]); -registerTheme('dark', [themeDark]); - -if (isExperimentalThemeEnabled()) { - registerTheme(AVAILABLE_THEMES[2].value, [themeNewLight]); - registerTheme(AVAILABLE_THEMES[3].value, [themeNewDark]); -} +import { ThemeProvider } from './components/with_theme/theme_context'; // Set up app diff --git a/packages/eui/src-docs/src/services/theme/theme.js b/packages/eui/src-docs/src/services/theme/theme.js index a3bbf6030c2..316ac207bc6 100644 --- a/packages/eui/src-docs/src/services/theme/theme.js +++ b/packages/eui/src-docs/src/services/theme/theme.js @@ -4,9 +4,9 @@ export function registerTheme(theme, cssFiles) { themes[theme] = cssFiles; } -export function applyTheme(newTheme) { +export function applyTheme(newTheme, colorMode = 'LIGHT') { Object.keys(themes).forEach((theme) => - themes[theme].forEach((cssFile) => cssFile.unuse()) + Object.values(themes[theme]).forEach((cssFile) => cssFile.unuse()) ); - themes[newTheme]?.forEach((cssFile) => cssFile.use()); + themes[newTheme]?.[colorMode]?.use(); } diff --git a/packages/eui/src-docs/src/theme_new_dark.scss b/packages/eui/src-docs/src/theme_borealis_dark.scss similarity index 100% rename from packages/eui/src-docs/src/theme_new_dark.scss rename to packages/eui/src-docs/src/theme_borealis_dark.scss diff --git a/packages/eui/src-docs/src/theme_new_light.scss b/packages/eui/src-docs/src/theme_borealis_light.scss similarity index 100% rename from packages/eui/src-docs/src/theme_new_light.scss rename to packages/eui/src-docs/src/theme_borealis_light.scss diff --git a/packages/eui/src-docs/src/views/app_context.js b/packages/eui/src-docs/src/views/app_context.js index 42645f5b2f1..0e0bd6ae658 100644 --- a/packages/eui/src-docs/src/views/app_context.js +++ b/packages/eui/src-docs/src/views/app_context.js @@ -33,7 +33,7 @@ const utilityCache = createCache({ }); export const AppContext = ({ children }) => { - const { theme } = useContext(ThemeContext); + const { theme, colorMode } = useContext(ThemeContext); const locale = useSelector((state) => getLocale(state)); const mappingFuncs = { @@ -56,13 +56,7 @@ export const AppContext = ({ children }) => { utility: utilityCache, }} theme={AVAILABLE_THEMES.find((t) => t.value === theme)?.provider} - colorMode={ - theme - ? theme.toLowerCase().includes('light') - ? 'light' - : 'dark' - : undefined - } + colorMode={colorMode} > [ { title: 'Max 10 colors', palette: euiPaletteColorBlind(), @@ -44,63 +46,82 @@ const customPalettes = [ }, ]; -export default () => ( - - {customPalettes.map((palette) => ( - - -

{palette.title}

-
- - - - - {palette.palette.map((hexCode) => ( - - ))} - - - - - - - -
- ))} - -

Behind text variant

-
- - - - - {euiPaletteColorBlindBehindText({ sortBy: 'natural' }).map( - (color, i) => ( - - - Text - - - ) - )} - - - - - - -
-); +export default () => { + const [customPalettes, setCustomPalettes] = useState(getCustomPalettes); + + const handleVisColorThemeChange = () => { + setCustomPalettes(getCustomPalettes); + }; + + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + handleVisColorThemeChange + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, []); + + return ( + + {customPalettes.map((palette) => ( + + +

{palette.title}

+
+ + + + + {palette.palette.map((hexCode) => ( + + ))} + + + + + + + +
+ ))} + +

Behind text variant

+
+ + + + + {euiPaletteColorBlindBehindText({ sortBy: 'natural' }).map( + (color, i) => ( + + + Text + + + ) + )} + + + + + + +
+ ); +}; diff --git a/packages/eui/src-docs/src/views/color_palette/color_palette_quantitative.js b/packages/eui/src-docs/src/views/color_palette/color_palette_quantitative.js index ae5cbdbf513..38e9de17da8 100644 --- a/packages/eui/src-docs/src/views/color_palette/color_palette_quantitative.js +++ b/packages/eui/src-docs/src/views/color_palette/color_palette_quantitative.js @@ -1,4 +1,5 @@ -import React, { Fragment, useState } from 'react'; +import React, { Fragment, useEffect, useMemo, useState } from 'react'; +import { VIS_COLOR_STORE_EVENTS } from '@elastic/eui-theme-common'; import { EuiFlexGroup, @@ -19,8 +20,10 @@ import { euiPaletteRed, euiPaletteGreen, euiPaletteGray, + EUI_VIS_COLOR_STORE, } from '../../../../src/services'; -const paletteData = { + +const getPaletteData = () => ({ euiPaletteForStatus, euiPaletteForTemperature, euiPaletteComplementary, @@ -29,16 +32,33 @@ const paletteData = { euiPaletteCool, euiPaletteWarm, euiPaletteGray, -}; -const paletteNames = Object.keys(paletteData); +}); export default () => { + const [paletteData, setPaletteData] = useState(getPaletteData); const [length, setLength] = useState(5); + const paletteNames = useMemo(() => Object.keys(paletteData), [paletteData]); + const onLengthChange = (e) => { setLength(e.currentTarget.value); }; + const handleVisColorThemeChange = () => { + setPaletteData(getPaletteData); + }; + + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + handleVisColorThemeChange + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, []); + return ( diff --git a/packages/eui/src-docs/src/views/color_picker/color_palette_display.js b/packages/eui/src-docs/src/views/color_picker/color_palette_display.js index 4c56ee61d00..8c4c88625cd 100644 --- a/packages/eui/src-docs/src/views/color_picker/color_palette_display.js +++ b/packages/eui/src-docs/src/views/color_picker/color_palette_display.js @@ -1,4 +1,5 @@ -import React, { useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; +import { VIS_COLOR_STORE_EVENTS } from '@elastic/eui-theme-common'; import { euiPaletteColorBlind, euiPaletteForStatus, @@ -9,6 +10,7 @@ import { euiPaletteCool, euiPaletteWarm, euiPaletteGray, + EUI_VIS_COLOR_STORE, } from '../../../../src/services/color'; import { @@ -67,11 +69,40 @@ const sizes = [ export default () => { const [palette, setPalette] = useState('1'); + const [categories, setCategories] = useState(5); const [selectionType, setSelectionType] = useState(true); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [size, setSize] = useState(sizes[1].value); + const getPalettes = useCallback( + () => + paletteNames.map((paletteName, index) => { + return { + value: String(index + 1), + title: paletteName, + palette: paletteData[paletteNames[index]](categories), + type: selectionType ? 'fixed' : 'gradient', + }; + }), + [categories, selectionType] + ); + + const [palettes, setPalettes] = useState(getPalettes()); + + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + () => { + setPalettes(getPalettes()); + } + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, [getPalettes]); + const onChangeSize = (e) => { setSize(e.target.value); }; @@ -80,15 +111,6 @@ export default () => { setCategories(parseInt(e.target.value)); }; - const palettes = paletteNames.map((paletteName, index) => { - return { - value: String(index + 1), - title: paletteName, - palette: paletteData[paletteNames[index]](categories), - type: selectionType ? 'fixed' : 'gradient', - }; - }); - const selectedPalette = paletteData[paletteNames[palette - 1]](categories); const onButtonClick = () => diff --git a/packages/eui/src-docs/src/views/color_picker/color_palette_picker.js b/packages/eui/src-docs/src/views/color_picker/color_palette_picker.js index 471f80ca085..83be7c66030 100644 --- a/packages/eui/src-docs/src/views/color_picker/color_palette_picker.js +++ b/packages/eui/src-docs/src/views/color_picker/color_palette_picker.js @@ -1,5 +1,7 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; +import { VIS_COLOR_STORE_EVENTS } from '@elastic/eui-theme-common'; import { + EUI_VIS_COLOR_STORE, euiPaletteColorBlind, euiPaletteForStatus, euiPaletteForTemperature, @@ -14,27 +16,27 @@ import { import { DisplayToggles } from '../form_controls/display_toggles'; -const palettes = [ +const getPalettes = () => [ { - value: 'pallette_1', + value: 'palette_1', title: 'EUI color blind (fixed)', palette: euiPaletteColorBlind(), type: 'fixed', }, { - value: 'pallette_2', + value: 'palette_2', title: 'EUI palette for status (gradient)', palette: euiPaletteForStatus(5), type: 'gradient', }, { - value: 'pallette_3', + value: 'palette_3', title: 'EUI palette for temperature (fixed)', palette: euiPaletteForTemperature(5), type: 'fixed', }, { - value: 'pallette_4', + value: 'palette_4', title: 'Grayscale (gradient with stops)', palette: [ { @@ -88,7 +90,21 @@ const palettes = [ export default () => { const [selectionDisplay, setSelectionDisplay] = useState(false); - const [pallette, setPallette] = useState('pallette_1'); + const [palettes, setPalettes] = useState(getPalettes()); + const [selectedPalette, setSelectedPalette] = useState('palette_1'); + + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + () => { + setPalettes(getPalettes()); + } + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, []); return ( <> @@ -105,8 +121,8 @@ export default () => { diff --git a/packages/eui/src-docs/src/views/elastic_charts/theming.tsx b/packages/eui/src-docs/src/views/elastic_charts/theming.tsx index 1e696b4362a..8b1d619648a 100644 --- a/packages/eui/src-docs/src/views/elastic_charts/theming.tsx +++ b/packages/eui/src-docs/src/views/elastic_charts/theming.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Chart, @@ -26,10 +26,12 @@ import { euiPaletteRed, euiPaletteGreen, euiPaletteGray, + EUI_VIS_COLOR_STORE, } from '../../../../src/services'; import { useChartBaseTheme } from './utils/use_chart_base_theme'; +import { VIS_COLOR_STORE_EVENTS } from '@elastic/eui-theme-common'; -const paletteData = { +const getPaletteData = () => ({ euiPaletteColorBlind, euiPaletteForStatus, euiPaletteForTemperature, @@ -39,25 +41,46 @@ const paletteData = { euiPaletteCool, euiPaletteWarm, euiPaletteGray, -}; - -const palettes = Object.entries(paletteData).map(([paletteName, palette]) => { - return { - value: paletteName, - title: paletteName, - palette: - palette === euiPaletteColorBlind - ? euiPaletteColorBlind({ sortBy: 'natural' }) - : palette(10), - type: 'fixed' as const, - }; }); +const getPalettes = () => + Object.entries(getPaletteData()).map(([paletteName, palette]) => { + return { + value: paletteName, + title: paletteName, + palette: + palette === euiPaletteColorBlind + ? euiPaletteColorBlind({ sortBy: 'natural' }) + : palette(10), + type: 'fixed' as const, + }; + }); + export default () => { const chartBaseTheme = useChartBaseTheme(); + const [palettes, setPalettes] = useState(getPalettes()); const [barPalette, setBarPalette] = useState('euiPaletteColorBlind'); + const paletteData = useMemo( + () => getPaletteData(), + // eslint-disable-next-line react-hooks/exhaustive-deps + [palettes] + ); + + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + () => { + setPalettes(getPalettes()); + } + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, []); + /** * Create data */ diff --git a/packages/eui/src/components/avatar/avatar.tsx b/packages/eui/src/components/avatar/avatar.tsx index fc410e87abc..fef7d0e633f 100644 --- a/packages/eui/src/components/avatar/avatar.tsx +++ b/packages/eui/src/components/avatar/avatar.tsx @@ -10,16 +10,16 @@ import React, { HTMLAttributes, FunctionComponent, useMemo } from 'react'; import { CommonProps, ExclusiveUnion } from '../common'; import classNames from 'classnames'; -import { isColorDark, hexToRgb, isValidHex } from '../../services/color'; import { + isColorDark, + hexToRgb, + isValidHex, euiPaletteColorBlindBehindText, - toInitials, - useEuiMemoizedStyles, -} from '../../services'; +} from '../../services/color'; +import { toInitials, useEuiMemoizedStyles, useEuiTheme } from '../../services'; import { IconType, EuiIcon, IconSize, IconColor } from '../icon'; import { euiAvatarStyles } from './avatar.styles'; -const visColors = euiPaletteColorBlindBehindText(); export const SIZES = ['s', 'm', 'l', 'xl'] as const; export type EuiAvatarSize = (typeof SIZES)[number]; @@ -127,6 +127,14 @@ export const EuiAvatar: FunctionComponent = ({ checkValidInitials(initials); const { casing = type === 'space' ? 'none' : 'uppercase', ...rest } = props; + const { euiTheme } = useEuiTheme(); + + const visColors = useMemo( + () => euiPaletteColorBlindBehindText(), + // eslint-disable-next-line react-hooks/exhaustive-deps + [euiTheme] + ); + const isPlain = color === 'plain'; const isSubdued = color === 'subdued'; const isNamedColor = isPlain || isSubdued || color === null; @@ -172,7 +180,7 @@ export const EuiAvatar: FunctionComponent = ({ color: textColor, }; } - }, [imageUrl, color, isNamedColor, name.length]); + }, [imageUrl, color, isNamedColor, name.length, visColors]); const iconCustomColor = useMemo(() => { // `null` allows icons to keep their default color (e.g. app icons) diff --git a/packages/eui/src/components/code/code_syntax.styles.ts b/packages/eui/src/components/code/code_syntax.styles.ts index 4a7b9e374bb..de320fe2ce8 100644 --- a/packages/eui/src/components/code/code_syntax.styles.ts +++ b/packages/eui/src/components/code/code_syntax.styles.ts @@ -6,48 +6,34 @@ * Side Public License, v 1. */ -import { - UseEuiTheme, - makeHighContrastColor, - euiPaletteColorBlind, -} from '../../services'; - -const visColors = euiPaletteColorBlind(); +import { UseEuiTheme } from '../../services'; // These variables are computationally expensive - do not call them outside `useEuiMemoizedStyles` export const euiCodeSyntaxVariables = ({ euiTheme }: UseEuiTheme) => { - const backgroundColor = euiTheme.colors.lightestShade; - return { - backgroundColor: backgroundColor, - color: makeHighContrastColor(euiTheme.colors.text)(backgroundColor), - inlineCodeColor: makeHighContrastColor(visColors[3])(backgroundColor), - selectedBackgroundColor: 'inherit', - commentColor: makeHighContrastColor(euiTheme.colors.subduedText)( - backgroundColor - ), - selectorTagColor: 'inherit', - stringColor: makeHighContrastColor(visColors[2])(backgroundColor), - tagColor: makeHighContrastColor(visColors[1])(backgroundColor), - nameColor: makeHighContrastColor(visColors[1])(backgroundColor), - numberColor: makeHighContrastColor(visColors[0])(backgroundColor), - keywordColor: makeHighContrastColor(visColors[3])(backgroundColor), - functionTitleColor: 'inherit', - typeColor: makeHighContrastColor(visColors[1])(backgroundColor), - attributeColor: 'inherit', - symbolColor: makeHighContrastColor(visColors[9])(backgroundColor), - paramsColor: 'inherit', - metaColor: makeHighContrastColor(euiTheme.colors.subduedText)( - backgroundColor - ), - titleColor: makeHighContrastColor(visColors[7])(backgroundColor), - sectionColor: makeHighContrastColor(visColors[9])(backgroundColor), - additionColor: makeHighContrastColor(visColors[0])(backgroundColor), - deletionColor: makeHighContrastColor(euiTheme.colors.danger)( - backgroundColor - ), - selectorClassColor: 'inherit', - selectorIdColor: 'inherit', + backgroundColor: euiTheme.components.codeBackground, + color: euiTheme.components.codeColor, + inlineCodeColor: euiTheme.components.codeInlineColor, + selectedBackgroundColor: euiTheme.components.codeBackgroundSelected, + commentColor: euiTheme.components.codeCommentColor, + selectorTagColor: euiTheme.components.codeSelectorColor, + stringColor: euiTheme.components.codeStringColor, + tagColor: euiTheme.components.codeTagColor, + nameColor: euiTheme.components.codeNameColor, + numberColor: euiTheme.components.codeNumberColor, + keywordColor: euiTheme.components.codeKeywordColor, + functionTitleColor: euiTheme.components.codeFunctionTitleColor, + typeColor: euiTheme.components.codeTypeColor, + attributeColor: euiTheme.components.codeAttributeColor, + symbolColor: euiTheme.components.codeSymbolColor, + paramsColor: euiTheme.components.codeParamsColor, + metaColor: euiTheme.components.codeMetaColor, + titleColor: euiTheme.components.codeTitleColor, + sectionColor: euiTheme.components.codeSectionColor, + additionColor: euiTheme.components.codeAdditionColor, + deletionColor: euiTheme.components.codeDeletionColor, + selectorClassColor: euiTheme.components.codeSelectorClassColor, + selectorIdColor: euiTheme.components.codeSelectorIdColor, get tokensCss() { return ` diff --git a/packages/eui/src/components/color_picker/color_palette_display/color_palette_display.stories.tsx b/packages/eui/src/components/color_picker/color_palette_display/color_palette_display.stories.tsx index 001024b5f5f..c06306cd8bf 100644 --- a/packages/eui/src/components/color_picker/color_palette_display/color_palette_display.stories.tsx +++ b/packages/eui/src/components/color_picker/color_palette_display/color_palette_display.stories.tsx @@ -6,14 +6,20 @@ * Side Public License, v 1. */ +import React, { useEffect, useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { euiPaletteColorBlind } from '../../../services'; +import { + EUI_VIS_COLOR_STORE, + euiPaletteColorBlind, + useUpdateEffect, +} from '../../../services'; import { EuiColorPaletteDisplay, EuiColorPaletteDisplayProps, } from './color_palette_display'; +import { VIS_COLOR_STORE_EVENTS } from '@elastic/eui-theme-common'; const meta: Meta = { title: 'Forms/EuiColorPalettePicker/EuiColorPaletteDisplay', @@ -30,6 +36,29 @@ type Story = StoryObj; export const Playground: Story = { args: { - palette: euiPaletteColorBlind(), + palette: euiPaletteColorBlind(), // static input + }, + render: function Render({ palette, ...rest }: EuiColorPaletteDisplayProps) { + const [_palette, setPalette] = useState(palette); + + // subscribe to theme-related vis_color changes + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + () => { + setPalette(euiPaletteColorBlind()); + } + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, []); + + useUpdateEffect(() => { + setPalette(palette); + }, [palette]); + + return ; }, }; diff --git a/packages/eui/src/components/color_picker/color_palette_picker/color_palette_picker.stories.tsx b/packages/eui/src/components/color_picker/color_palette_picker/color_palette_picker.stories.tsx index d71640fb0a5..fe56d0afac7 100644 --- a/packages/eui/src/components/color_picker/color_palette_picker/color_palette_picker.stories.tsx +++ b/packages/eui/src/components/color_picker/color_palette_picker/color_palette_picker.stories.tsx @@ -6,14 +6,21 @@ * Side Public License, v 1. */ +import React, { useEffect, useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { euiPaletteColorBlind } from '../../../services'; +import { + EUI_VIS_COLOR_STORE, + euiPaletteColorBlind, + useUpdateEffect, +} from '../../../services'; import { EuiColorPalettePicker, + EuiColorPalettePickerPaletteProps, EuiColorPalettePickerProps, } from './color_palette_picker'; +import { VIS_COLOR_STORE_EVENTS } from '@elastic/eui-theme-common'; const meta: Meta> = { title: 'Forms/EuiColorPalettePicker/EuiColorPalettePicker', @@ -39,16 +46,44 @@ const meta: Meta> = { export default meta; type Story = StoryObj>; +const getInitialPalettes = (): EuiColorPalettePickerPaletteProps[] => [ + { + value: 'palette1', + title: 'Palette 1', + palette: euiPaletteColorBlind(), + type: 'fixed', + }, +]; + export const Playground: Story = { args: { - palettes: [ - { - value: 'palette1', - title: 'Palette 1', - palette: euiPaletteColorBlind(), - type: 'fixed', - }, - ], + palettes: getInitialPalettes(), valueOfSelected: 'palette1', }, + render: function Render({ + palettes, + ...rest + }: EuiColorPalettePickerProps) { + const [_palettes, setPalettes] = useState(palettes); + + // subscribe to theme-related vis_color changes + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + () => { + setPalettes(getInitialPalettes()); + } + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, []); + + useUpdateEffect(() => { + setPalettes(palettes); + }, [palettes]); + + return ; + }, }; diff --git a/packages/eui/src/components/color_picker/color_picker.tsx b/packages/eui/src/components/color_picker/color_picker.tsx index 931ac3055ef..b1f12f1af20 100644 --- a/packages/eui/src/components/color_picker/color_picker.tsx +++ b/packages/eui/src/components/color_picker/color_picker.tsx @@ -21,8 +21,9 @@ import chroma, { ColorSpaces } from 'chroma-js'; import { useEuiMemoizedStyles, - VISUALIZATION_COLORS, keys, + euiPaletteColorBlind, + useEuiTheme, } from '../../services'; import { CommonProps } from '../common'; import { @@ -198,7 +199,7 @@ export const EuiColorPicker: FunctionComponent = ({ onChange, onFocus, readOnly = false, - swatches = VISUALIZATION_COLORS, + swatches: _swatches, popoverZIndex, prepend, append, @@ -238,6 +239,14 @@ export const EuiColorPicker: FunctionComponent = ({ ] ); + const { euiTheme } = useEuiTheme(); + + const swatches = useMemo( + () => _swatches ?? euiPaletteColorBlind(), + // eslint-disable-next-line react-hooks/exhaustive-deps + [_swatches, euiTheme] + ); + const preferredFormat = useMemo(() => { if (format) return format; const parsed = parseColor(color); diff --git a/packages/eui/src/components/loading/loading_chart.styles.ts b/packages/eui/src/components/loading/loading_chart.styles.ts index d179393d20f..33059a5aa2b 100644 --- a/packages/eui/src/components/loading/loading_chart.styles.ts +++ b/packages/eui/src/components/loading/loading_chart.styles.ts @@ -7,15 +7,18 @@ */ import { css, keyframes } from '@emotion/react'; -import { euiPaletteColorBlind, shadeOrTint, UseEuiTheme } from '../../services'; +import { + _EuiThemeComponentColors, + _EuiThemeVisColors, +} from '@elastic/eui-theme-common'; + +import { UseEuiTheme } from '../../services'; import { euiCanAnimate, euiCantAnimate, logicalCSS, } from '../../global_styling'; -const nonMonoColors = euiPaletteColorBlind(); - export const euiLoadingChartStyles = ({ euiTheme }: UseEuiTheme) => ({ euiLoadingChart: css` overflow: hidden; @@ -37,49 +40,66 @@ export const euiLoadingChartStyles = ({ euiTheme }: UseEuiTheme) => ({ export const BARS_COUNT = 4; -export const euiLoadingChartBarStyles = ({ - euiTheme, - colorMode, -}: UseEuiTheme) => ({ - euiLoadingChart__bar: css` - ${logicalCSS('height', '100%')} - display: inline-block; +export const euiLoadingChartBarStyles = ({ euiTheme }: UseEuiTheme) => { + const nonMonoColors = Object.keys(euiTheme.colors.vis).reduce( + (colors, cur) => { + const isVisColor = cur.match(/euiColorVis[0-9]/); - ${euiCanAnimate} { - animation: ${barAnimation} 1s infinite; + if (isVisColor) { + const color = euiTheme.colors.vis[cur as keyof _EuiThemeVisColors]; + return [...colors, color]; + } - ${outputNthChildCss((index) => `animation-delay: 0.${index}s;`)} - } - ${euiCantAnimate} { - ${outputNthChildCss((index) => `transform: translateY(${22 * index}%);`)} - } - `, - nonmono: css` - ${outputNthChildCss((index) => `background-color: ${nonMonoColors[index]}`)} - `, - mono: css` - ${outputNthChildCss( - (index) => - `background-color: ${shadeOrTint( - euiTheme.colors.lightShade, - index * 0.04, - colorMode - )}` - )} - `, - m: css` - ${logicalCSS('width', euiTheme.size.xxs)} - ${logicalCSS('margin-bottom', euiTheme.size.s)} - `, - l: css` - ${logicalCSS('width', euiTheme.size.xs)} - ${logicalCSS('margin-bottom', euiTheme.size.m)} - `, - xl: css` - ${logicalCSS('width', euiTheme.size.s)} - ${logicalCSS('margin-bottom', euiTheme.size.base)} - `, -}); + return colors; + }, + [] as string[] + ); + + return { + euiLoadingChart__bar: css` + ${logicalCSS('height', '100%')} + display: inline-block; + + ${euiCanAnimate} { + animation: ${barAnimation} 1s infinite; + + ${outputNthChildCss((index) => `animation-delay: 0.${index}s;`)} + } + ${euiCantAnimate} { + ${outputNthChildCss( + (index) => `transform: translateY(${22 * index}%);` + )} + } + `, + nonmono: css` + ${outputNthChildCss( + (index) => `background-color: ${nonMonoColors[index]}` + )} + `, + mono: css` + /* stylelint-disable no-extra-semicolons */ + ${outputNthChildCss((index) => { + const token = + `loadingChartMonoBackground${index}` as keyof _EuiThemeComponentColors; + const color = euiTheme.components[token]; + + return `background-color: ${color}`; + })} + `, + m: css` + ${logicalCSS('width', euiTheme.size.xxs)} + ${logicalCSS('margin-bottom', euiTheme.size.s)} + `, + l: css` + ${logicalCSS('width', euiTheme.size.xs)} + ${logicalCSS('margin-bottom', euiTheme.size.m)} + `, + xl: css` + ${logicalCSS('width', euiTheme.size.s)} + ${logicalCSS('margin-bottom', euiTheme.size.base)} + `, + }; +}; /** * Small utility helper for generating nth-child CSS for each bar diff --git a/packages/eui/src/components/progress/progress.styles.ts b/packages/eui/src/components/progress/progress.styles.ts index 191ce2f71be..02a6a73239f 100644 --- a/packages/eui/src/components/progress/progress.styles.ts +++ b/packages/eui/src/components/progress/progress.styles.ts @@ -15,11 +15,7 @@ import { euiFontSize, euiTextTruncate, } from '../../global_styling'; -import { - UseEuiTheme, - euiPaletteColorBlind, - makeHighContrastColor, -} from '../../services'; +import { UseEuiTheme, makeHighContrastColor } from '../../services'; import { euiText } from '../text/text.styles'; /** @@ -42,7 +38,6 @@ const indeterminateProgressValue = (cssProperties: string) => ` /** * Color utilities */ -const visColors = euiPaletteColorBlind(); const nativeVsIndeterminateColor = (color: string, isNative: boolean) => { const selectors = isNative @@ -184,34 +179,34 @@ export const euiProgressStyles = ( ${nativeVsIndeterminateColor(euiTheme.colors.accentSecondary, isNative)} `, vis0: css` - ${nativeVsIndeterminateColor(visColors[0], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis0, isNative)} `, vis1: css` - ${nativeVsIndeterminateColor(visColors[1], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis1, isNative)} `, vis2: css` - ${nativeVsIndeterminateColor(visColors[2], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis2, isNative)} `, vis3: css` - ${nativeVsIndeterminateColor(visColors[3], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis3, isNative)} `, vis4: css` - ${nativeVsIndeterminateColor(visColors[4], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis4, isNative)} `, vis5: css` - ${nativeVsIndeterminateColor(visColors[5], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis5, isNative)} `, vis6: css` - ${nativeVsIndeterminateColor(visColors[6], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis6, isNative)} `, vis7: css` - ${nativeVsIndeterminateColor(visColors[7], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis7, isNative)} `, vis8: css` - ${nativeVsIndeterminateColor(visColors[8], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis8, isNative)} `, vis9: css` - ${nativeVsIndeterminateColor(visColors[9], isNative)} + ${nativeVsIndeterminateColor(euiTheme.colors.vis.euiColorVis9, isNative)} `, customColor: css` ${nativeVsIndeterminateColor('currentColor', isNative)} @@ -273,34 +268,34 @@ export const euiProgressValueTextStyles = ({ euiTheme }: UseEuiTheme) => ({ color: ${euiTheme.colors.textAccentSecondary}; `, vis0: css` - color: ${makeHighContrastColor(visColors[0])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis0)(euiTheme)}; `, vis1: css` - color: ${makeHighContrastColor(visColors[1])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis1)(euiTheme)}; `, vis2: css` - color: ${makeHighContrastColor(visColors[2])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis2)(euiTheme)}; `, vis3: css` - color: ${makeHighContrastColor(visColors[3])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis3)(euiTheme)}; `, vis4: css` - color: ${makeHighContrastColor(visColors[4])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis4)(euiTheme)}; `, vis5: css` - color: ${makeHighContrastColor(visColors[5])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis5)(euiTheme)}; `, vis6: css` - color: ${makeHighContrastColor(visColors[6])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis6)(euiTheme)}; `, vis7: css` - color: ${makeHighContrastColor(visColors[7])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis7)(euiTheme)}; `, vis8: css` - color: ${makeHighContrastColor(visColors[8])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis8)(euiTheme)}; `, vis9: css` - color: ${makeHighContrastColor(visColors[9])(euiTheme)}; + color: ${makeHighContrastColor(euiTheme.colors.vis.euiColorVis9)(euiTheme)}; `, customColor: css` color: currentColor; diff --git a/packages/eui/src/components/token/token.styles.ts b/packages/eui/src/components/token/token.styles.ts index 01189ca24b4..4fdba34b1fd 100644 --- a/packages/eui/src/components/token/token.styles.ts +++ b/packages/eui/src/components/token/token.styles.ts @@ -23,9 +23,8 @@ import { shade, } from '../../services'; import type { TokenFill } from './token_types'; - -const visColors = euiPaletteColorBlind(); -const visColorsBehindText = euiPaletteColorBlindBehindText(); +import { TOKEN_COLOR_TO_ICON_COLOR_MAP } from './token_map'; +import { _EuiThemeVisColors } from '@elastic/eui-theme-common'; const getTokenColor = ( euiTheme: UseEuiTheme['euiTheme'], @@ -33,6 +32,12 @@ const getTokenColor = ( fill: TokenFill, color: number | string ) => { + const { hasVisColorAdjustment } = euiTheme.flags; + // use inside function as they are not returning constants, + // but internally depend on EUI_VIS_COLOR_STORE to update per theme + const visColors = euiPaletteColorBlind(); + const visColorsBehindText = euiPaletteColorBlindBehindText(); + const isVizColor = typeof color === 'number'; const iconColor = isVizColor ? visColors[color] : euiTheme.colors.darkShade; @@ -47,7 +52,24 @@ const getTokenColor = ( ? shade(iconColor, 0.7) : tint(iconColor, 0.9); - const lightColor = makeHighContrastColor(iconColor)(backgroundLightColor); + const getIconVisColor = ( + euiTheme: UseEuiTheme['euiTheme'], + color: number + ) => { + const iconColorKey = + `euiColorVis${color}` as keyof typeof TOKEN_COLOR_TO_ICON_COLOR_MAP; + const iconColorToken = TOKEN_COLOR_TO_ICON_COLOR_MAP[ + iconColorKey + ] as keyof _EuiThemeVisColors; + + return euiTheme.colors.vis[iconColorToken]; + }; + + const lightColor = hasVisColorAdjustment + ? makeHighContrastColor(iconColor)(backgroundLightColor) + : isVizColor + ? getIconVisColor(euiTheme, color) + : iconColor; const boxShadowColor = isDarkMode ? shade(iconColor, 0.6) diff --git a/packages/eui/src/components/token/token.test.tsx b/packages/eui/src/components/token/token.test.tsx index 115cc2e49e9..4f7da701bcf 100644 --- a/packages/eui/src/components/token/token.test.tsx +++ b/packages/eui/src/components/token/token.test.tsx @@ -13,10 +13,10 @@ import { render } from '../../test/rtl'; import { EuiToken } from './token'; import { COLORS, SHAPES, SIZES, FILLS } from './token_types'; -import { TOKEN_MAP } from './token_map'; +import { TOKEN_MAP_AMSTERDAM } from './token_map'; import { keysOf } from '../common'; -const tokenTypes = keysOf(TOKEN_MAP); +const tokenTypes = keysOf(TOKEN_MAP_AMSTERDAM); const tokenColors = COLORS; describe('EuiToken', () => { diff --git a/packages/eui/src/components/token/token.tsx b/packages/eui/src/components/token/token.tsx index 95ce82eba31..48d7cdbd38b 100644 --- a/packages/eui/src/components/token/token.tsx +++ b/packages/eui/src/components/token/token.tsx @@ -6,12 +6,16 @@ * Side Public License, v 1. */ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useMemo } from 'react'; import classNames from 'classnames'; import { useEuiTheme, isColorDark, hexToRgb } from '../../services'; import { EuiIcon, IconSize } from '../icon'; -import { EuiTokenMapType, TOKEN_MAP } from './token_map'; +import { + EuiTokenMapType, + TOKEN_MAP_AMSTERDAM, + TOKEN_MAP_BOREALIS, +} from './token_map'; import { COLORS } from './token_types'; import type { EuiTokenProps, @@ -53,6 +57,13 @@ export const EuiToken: FunctionComponent = ({ finalSize = 'm'; } + const euiTheme = useEuiTheme(); + const { hasVisColorAdjustment } = euiTheme.euiTheme.flags; + + const TOKEN_MAP = hasVisColorAdjustment + ? TOKEN_MAP_AMSTERDAM + : TOKEN_MAP_BOREALIS; + // If the iconType passed is one of the prefab token types, // grab its properties const tokenDefaults = @@ -64,8 +75,10 @@ export const EuiToken: FunctionComponent = ({ const finalShape = shape || tokenDefaults.shape || 'circle'; let finalFill = fill || 'light'; - const euiTheme = useEuiTheme(); - const styles = euiTokenStyles(euiTheme, finalFill); + // memoize styles to reduce executing contained color calculations + const styles = useMemo(() => { + return euiTokenStyles(euiTheme, finalFill); + }, [euiTheme, finalFill]); let cssStyles = [ styles.euiToken, diff --git a/packages/eui/src/components/token/token_map.ts b/packages/eui/src/components/token/token_map.ts index 36bf8d1b2b1..6ef5bd49e62 100644 --- a/packages/eui/src/components/token/token_map.ts +++ b/packages/eui/src/components/token/token_map.ts @@ -73,7 +73,7 @@ export type EuiTokenMapType = * circle shape for more uncommon token types so they grab attention. */ -export const TOKEN_MAP: { +export const TOKEN_MAP_AMSTERDAM: { [mapType in EuiTokenMapType]: Omit; } = { tokenAlias: { @@ -305,3 +305,249 @@ export const TOKEN_MAP: { color: 'euiColorVis1', }, }; + +export const TOKEN_MAP_BOREALIS: { + [mapType in EuiTokenMapType]: Omit; +} = { + tokenAlias: { + shape: 'circle', + color: 'euiColorVis1', + }, + tokenAnnotation: { + shape: 'square', + color: 'euiColorVis8', + }, + tokenArray: { + shape: 'square', + color: 'euiColorVis3', + }, + tokenBinary: { + shape: 'square', + color: 'euiColorVis5', + }, + tokenBoolean: { + shape: 'square', + color: 'euiColorVis3', + }, + tokenClass: { + shape: 'circle', + color: 'euiColorVis2', + }, + tokenCompletionSuggester: { + shape: 'square', + color: 'euiColorVis2', + }, + tokenConstant: { + shape: 'circle', + color: 'euiColorVis0', + }, + tokenDate: { + shape: 'square', + color: 'euiColorVis9', + }, + tokenDimension: { + shape: 'square', + color: 'euiColorVis2', + }, + tokenElement: { + shape: 'square', + color: 'euiColorVis1', + }, + tokenEnum: { + shape: 'circle', + color: 'euiColorVis1', + }, + tokenEnumMember: { + shape: 'square', + color: 'euiColorVis3', + }, + tokenEvent: { + shape: 'circle', + color: 'euiColorVis5', + }, + tokenException: { + shape: 'circle', + color: 'euiColorVis0', + }, + tokenField: { + shape: 'circle', + color: 'euiColorVis0', + }, + tokenFile: { + shape: 'square', + color: 'euiColorVis4', + }, + tokenFlattened: { + shape: 'square', + color: 'euiColorVis3', + }, + tokenFunction: { + shape: 'circle', + color: 'euiColorVis4', + }, + tokenGeo: { + shape: 'square', + color: 'euiColorVis8', + }, + tokenHistogram: { + shape: 'square', + color: 'euiColorVis9', + }, + tokenInterface: { + shape: 'circle', + color: 'euiColorVis6', + }, + tokenIP: { + shape: 'square', + color: 'euiColorVis6', + }, + tokenJoin: { + shape: 'square', + color: 'euiColorVis8', + }, + tokenKey: { + shape: 'circle', + color: 'euiColorVis8', + }, + tokenKeyword: { + shape: 'square', + color: 'euiColorVis2', + }, + tokenMethod: { + shape: 'square', + color: 'euiColorVis4', + }, + tokenMetricCounter: { + shape: 'square', + color: 'euiColorVis0', + }, + tokenMetricGauge: { + shape: 'square', + color: 'euiColorVis0', + }, + tokenModule: { + shape: 'square', + color: 'euiColorVis5', + }, + tokenNamespace: { + shape: 'square', + color: 'euiColorVis2', + }, + tokenNested: { + shape: 'circle', + color: 'euiColorVis4', + }, + tokenNull: { + shape: 'square', + color: 'euiColorVis4', + }, + tokenNumber: { + shape: 'square', + color: 'euiColorVis0', + }, + tokenObject: { + shape: 'circle', + color: 'euiColorVis1', + }, + tokenOperator: { + shape: 'circle', + color: 'euiColorVis5', + }, + tokenPackage: { + shape: 'square', + color: 'euiColorVis0', + }, + tokenParameter: { + shape: 'square', + color: 'euiColorVis5', + }, + tokenPercolator: { + shape: 'square', + color: 'euiColorVis9', + }, + tokenProperty: { + shape: 'circle', + color: 'euiColorVis4', + }, + tokenRange: { + shape: 'circle', + color: 'euiColorVis5', + }, + tokenRankFeature: { + shape: 'square', + color: 'euiColorVis7', + }, + tokenRankFeatures: { + shape: 'square', + color: 'euiColorVis1', + }, + tokenRepo: { + shape: 'square', + color: 'euiColorVis2', + }, + tokenSearchType: { + shape: 'square', + color: 'euiColorVis8', + }, + tokenSemanticText: { + shape: 'square', + color: 'euiColorVis0', + }, + tokenShape: { + shape: 'circle', + color: 'euiColorVis7', + }, + tokenString: { + shape: 'square', + color: 'euiColorVis2', + }, + tokenStruct: { + shape: 'square', + color: 'euiColorVis0', + }, + tokenSymbol: { + shape: 'square', + color: 'euiColorVis0', + }, + tokenTag: { + shape: 'square', + color: 'euiColorVis6', + }, + tokenText: { + shape: 'square', + color: 'euiColorVis1', + }, + tokenTokenCount: { + shape: 'square', + color: 'euiColorVis5', + }, + tokenVariable: { + shape: 'circle', + color: 'euiColorVis3', + }, + tokenVectorDense: { + shape: 'square', + color: 'euiColorVis8', + }, + tokenDenseVector: { + shape: 'square', + color: 'euiColorVis8', + }, + tokenVectorSparse: { + shape: 'square', + color: 'euiColorVis2', + }, +}; + +export const TOKEN_COLOR_TO_ICON_COLOR_MAP = { + euiColorVis0: 'euiColorVis0', + euiColorVis1: 'euiColorVis0', + euiColorVis2: 'euiColorVis2', + euiColorVis3: 'euiColorVis2', + euiColorVis4: 'euiColorVis4', + euiColorVis5: 'euiColorVis4', + euiColorVis6: 'euiColorVis6', + euiColorVis7: 'euiColorVis6', + euiColorVis8: 'euiColorVis8', + euiColorVis9: 'euiColorVis8', +}; diff --git a/packages/eui/src/global_styling/variables/_colors_vis.scss b/packages/eui/src/global_styling/variables/_colors_vis.scss index d935ffa7ebe..8b34cc14038 100644 --- a/packages/eui/src/global_styling/variables/_colors_vis.scss +++ b/packages/eui/src/global_styling/variables/_colors_vis.scss @@ -1,3 +1,3 @@ // Visualization colors -@import 'node_modules/@elastic/eui-theme-common/src/global_styling/variables/colors_vis' +@import '../../themes/amsterdam/global_styling/variables/colors_vis' diff --git a/packages/eui/src/global_styling/variables/_colors_vis.ts b/packages/eui/src/global_styling/variables/_colors_vis.ts index 64dcfd86633..a4e932e3896 100644 --- a/packages/eui/src/global_styling/variables/_colors_vis.ts +++ b/packages/eui/src/global_styling/variables/_colors_vis.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { colorVis } from '@elastic/eui-theme-common'; +export { colorVis } from '../../themes/amsterdam/global_styling/variables/_colors_vis'; diff --git a/packages/eui/src/services/color/eui_palettes.ts b/packages/eui/src/services/color/eui_palettes.ts index c44a3bbfb0a..360cdb20684 100644 --- a/packages/eui/src/services/color/eui_palettes.ts +++ b/packages/eui/src/services/color/eui_palettes.ts @@ -7,8 +7,9 @@ */ import chroma from 'chroma-js'; -import { HEX } from './color_types'; import { colorPalette } from './color_palette'; +import { EUI_VIS_COLOR_STORE } from './vis_color_store'; +import { _EuiThemeVisColors } from '@elastic/eui-theme-common'; export type EuiPalette = string[]; @@ -54,6 +55,14 @@ export interface EuiPaletteColorBlindProps { sortShift?: string; } +/** + * NOTE: These functions rely on base vis colors of the theme which are provided via a global + * singleton instance `EUI_VIS_COLOR_STORE` that's updated by the EuiProvider on theme change. + * Make sure the function is recalled on theme change to retrieve theme-related colors. + * + * Outside of a react component you can use the `subscibe()` method on the `EUI_VIS_COLOR_STORE` + * to subscribe to updates and update your usages to ensure latest colors are loaded. + */ export const euiPaletteColorBlind = ({ rotations = 1, order = 'append', @@ -63,17 +72,19 @@ export const euiPaletteColorBlind = ({ }: EuiPaletteColorBlindProps = {}): EuiPalette => { let colors: string[] = []; + const { visColors } = EUI_VIS_COLOR_STORE; + let base = [ - '#54B399', // 0 green - '#6092C0', // 1 blue - '#D36086', // 2 dark pink - '#9170B8', // 3 purple - '#CA8EAE', // 4 light pink - '#D6BF57', // 5 yellow - '#B9A888', // 6 tan - '#DA8B45', // 7 orange - '#AA6556', // 8 brown - '#E7664C', // 9 red + visColors.euiColorVis0, + visColors.euiColorVis1, + visColors.euiColorVis2, + visColors.euiColorVis3, + visColors.euiColorVis4, + visColors.euiColorVis5, + visColors.euiColorVis6, + visColors.euiColorVis7, + visColors.euiColorVis8, + visColors.euiColorVis9, ]; if (sortBy === 'natural') { @@ -122,119 +133,256 @@ export const euiPaletteColorBlind = ({ /** * Color blind palette with text is meant for use when text is applied on top of the color. * It increases the brightness of the color to give the text more contrast. + * + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. */ export const euiPaletteColorBlindBehindText = ( paletteProps: EuiPaletteColorBlindProps = {} ) => { + const { hasVisColorAdjustment } = EUI_VIS_COLOR_STORE; + const originalPalette = euiPaletteColorBlind(paletteProps); + + // new theme palette has required contrast, we don't need to adjust them + if (!hasVisColorAdjustment) return originalPalette; + const newPalette = originalPalette.map((color) => chroma(color).brighten(0.5).hex() ); return newPalette; }; +const _getVisColorsAsText = ( + visColors: _EuiThemeVisColors, + keys: Array +) => + keys.reduce((colors, curr) => { + return [...colors, visColors[curr]]; + }, [] as EuiPalette); + +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteForLightBackground = function (): EuiPalette { - return ['#006BB4', '#017D73', '#F5A700', '#BD271E', '#DD0A73']; + const { visColors } = EUI_VIS_COLOR_STORE; + + const visColorsAsTextKeys = Object.keys(visColors).filter((color) => + color.includes('AsTextLight') + ) as Array; + + return _getVisColorsAsText(visColors, visColorsAsTextKeys); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteForDarkBackground = function (): EuiPalette { - return ['#1BA9F5', '#7DE2D1', '#F990C0', '#F66', '#FFCE7A']; -}; + const { visColors } = EUI_VIS_COLOR_STORE; -const greenColor: HEX = '#209280'; -const redColor: HEX = '#CC5642'; -const lightRedColor: HEX = euiPaletteColorBlind()[9]; -const coolArray: HEX[] = [euiPaletteColorBlind()[1], '#6092C0']; -const warmArray: HEX[] = [euiPaletteColorBlind()[7], euiPaletteColorBlind()[9]]; + const visColorsAsTextKeys = Object.keys(visColors).filter((color) => + color.includes('AsTextDark') + ) as Array; + return _getVisColorsAsText(visColors, visColorsAsTextKeys); +}; + +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteForStatus = function (steps: number): EuiPalette { + const { visColors } = EUI_VIS_COLOR_STORE; + if (steps === 1) { - return [greenColor]; + return [visColors.euiColorVisSuccess0]; } if (steps <= 3) { return euiPalette( - [greenColor, euiPaletteColorBlind()[5], redColor], + [ + visColors.euiColorVisSuccess0, + visColors.euiColorVisWarning0, + visColors.euiColorVisDanger0, + ], steps, true ); } return euiPalette( [ - greenColor, - euiPaletteColorBlind()[0], - euiPaletteColorBlind()[5], - lightRedColor, - redColor, + visColors.euiColorVisSuccess0, + visColors.euiColorVisSuccess1, + visColors.euiColorVisWarning0, + visColors.euiColorVisDanger1, + visColors.euiColorVisDanger0, ], steps, true ); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteForTemperature = function (steps: number): EuiPalette { - const cools = colorPalette([...coolArray.slice().reverse(), '#EBEFF5'], 3); - const warms = colorPalette(['#F4F3DB', ...warmArray], 3); + const { hasVisColorAdjustment, visColors } = EUI_VIS_COLOR_STORE; if (steps === 1) { - return [cools[0]]; + return [visColors.euiColorVisCool2]; } else if (steps <= 3) { - return euiPalette([cools[0], lightRedColor], steps, true); + return euiPalette( + [visColors.euiColorVisCool2, visColors.euiColorVisWarm2], + steps, + true + ); } - return euiPalette([...cools, ...warms], steps, true); + const cools = colorPalette( + [ + visColors.euiColorVisCool2, + visColors.euiColorVisCool1, + visColors.euiColorVisCool0, + ], + 3 + ); + const warms = colorPalette( + [ + visColors.euiColorVisWarm0, + visColors.euiColorVisWarm1, + visColors.euiColorVisWarm2, + ], + 3 + ); + + const paletteColors = hasVisColorAdjustment + ? [...cools, ...warms] + : [...cools, visColors.euiColorVisNeutral0, ...warms]; + + return euiPalette(paletteColors, steps, true); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteComplementary = function (steps: number): EuiPalette { + const { hasVisColorAdjustment, visColors } = EUI_VIS_COLOR_STORE; + if (steps === 1) { - return [euiPaletteColorBlind()[1]]; + return [visColors.euiColorVisComplementary0]; } - return euiPalette( - [euiPaletteColorBlind()[1], euiPaletteColorBlind()[7]], - steps, - true - ); + const paletteColors = hasVisColorAdjustment + ? [visColors.euiColorVisComplementary0, visColors.euiColorVisComplementary1] + : [ + visColors.euiColorVisComplementary0, + visColors.euiColorVisNeutral0, + visColors.euiColorVisComplementary1, + ]; + + return euiPalette(paletteColors, steps, true); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteRed = function (steps: number): EuiPalette { + const { visColors } = EUI_VIS_COLOR_STORE; + if (steps === 1) { - return [lightRedColor]; + return [visColors.euiColorVisDanger1]; } - return euiPalette(['white', redColor], steps); + return euiPalette( + [visColors.euiColorVisNeutral0, visColors.euiColorVisDanger0], + steps + ); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteGreen = function (steps: number): EuiPalette { + const { visColors } = EUI_VIS_COLOR_STORE; + if (steps === 1) { - return [euiPaletteColorBlind()[0]]; + return [visColors.euiColorVisSuccess1]; } - return euiPalette(['white', greenColor], steps); + return euiPalette( + [visColors.euiColorVisNeutral0, visColors.euiColorVisSuccess0], + steps + ); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteCool = function (steps: number): EuiPalette { + const { hasVisColorAdjustment, visColors } = EUI_VIS_COLOR_STORE; + if (steps === 1) { - return [coolArray[1]]; + return [visColors.euiColorVisCool1]; } - return euiPalette(['white', ...coolArray], steps); + return euiPalette( + [ + hasVisColorAdjustment + ? visColors.euiColorVisNeutral0 + : visColors.euiColorVisCool0, + visColors.euiColorVisCool1, + visColors.euiColorVisCool2, + ], + steps + ); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteWarm = function (steps: number): EuiPalette { + const { visColors } = EUI_VIS_COLOR_STORE; + if (steps === 1) { - return [lightRedColor]; + return [visColors.euiColorVisWarm2]; } - return euiPalette(['#FBFBDC', ...warmArray], steps); + return euiPalette( + [ + visColors.euiColorVisWarm0, + visColors.euiColorVisWarm1, + visColors.euiColorVisWarm2, + ], + steps + ); }; +/** + * NOTE: This function is not pure. It relies on `EUI_VIS_COLOR_STORE` which is updated by the + * EuiProvider on theme change. Ensure to recall the function on theme change or subscribe to the store. + */ export const euiPaletteGray = function (steps: number): EuiPalette { + const { visColors } = EUI_VIS_COLOR_STORE; + if (steps === 1) { - return ['#98a2b3']; + return [visColors.euiColorVisGrey1]; } return euiPalette( - ['white', '#d3dae6', '#98a2b3', '#69707d', '#343741'], + [ + visColors.euiColorVisNeutral0, + visColors.euiColorVisGrey0, + visColors.euiColorVisGrey1, + visColors.euiColorVisGrey2, + visColors.euiColorVisGrey3, + ], steps, false ); diff --git a/packages/eui/src/services/color/index.ts b/packages/eui/src/services/color/index.ts index 574c5ffb0b7..5f827ba40c5 100644 --- a/packages/eui/src/services/color/index.ts +++ b/packages/eui/src/services/color/index.ts @@ -22,6 +22,7 @@ export { VISUALIZATION_COLORS, DEFAULT_VISUALIZATION_COLOR, } from './visualization_colors'; +export { EUI_VIS_COLOR_STORE } from './vis_color_store'; export { colorPalette } from './color_palette'; export { euiPaletteForLightBackground, diff --git a/packages/eui/src/services/color/vis_color_store.ts b/packages/eui/src/services/color/vis_color_store.ts new file mode 100644 index 00000000000..8c13834912a --- /dev/null +++ b/packages/eui/src/services/color/vis_color_store.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { _EuiVisColorStore, EuiVisColorStore } from '@elastic/eui-theme-common'; + +import { colorVis } from '../../global_styling/variables/_colors_vis'; + +// initialsetup of Vis color storage with default colors +export const EUI_VIS_COLOR_STORE: _EuiVisColorStore = + EuiVisColorStore.getInstance(colorVis, true); diff --git a/packages/eui/src/services/index.ts b/packages/eui/src/services/index.ts index 20a9091c7f5..db80c9223e5 100644 --- a/packages/eui/src/services/index.ts +++ b/packages/eui/src/services/index.ts @@ -60,6 +60,7 @@ export { tintOrShade, transparentize, VISUALIZATION_COLORS, + EUI_VIS_COLOR_STORE, wcagContrastMin, } from './color'; export type { HSV } from './color'; diff --git a/packages/eui/src/services/theme/provider.tsx b/packages/eui/src/services/theme/provider.tsx index e0678603074..8ae4e5040d5 100644 --- a/packages/eui/src/services/theme/provider.tsx +++ b/packages/eui/src/services/theme/provider.tsx @@ -41,7 +41,9 @@ import { EuiThemeColorModeStandard, EuiThemeSystem, EuiThemeModifications, + EuiThemeComputed, } from './types'; +import { EUI_VIS_COLOR_STORE } from '../color'; export interface EuiThemeProviderProps extends PropsWithChildren { theme?: EuiThemeSystem; @@ -108,14 +110,31 @@ export const EuiThemeProvider = ({ isEqual(parentModifications, modifications) ); + const updateVisColorStore = useCallback((theme: EuiThemeComputed) => { + EUI_VIS_COLOR_STORE.setVisColors( + theme.colors.vis, + theme.flags?.hasVisColorAdjustment ?? true + ); + }, []); + + const getInitialTheme = () => { + const theme = getComputed( + system, + buildTheme(modifications, `_${system.key}`) as typeof system, + colorMode + ); + + setTimeout(() => { + updateVisColorStore(theme); + }); + + return theme; + }; + const [theme, setTheme] = useState( isParentTheme.current && Object.keys(parentTheme).length ? { ...parentTheme } // Intentionally create a new object to break referential equality - : getComputed( - system, - buildTheme(modifications, `_${system.key}`) as typeof system, - colorMode - ) + : getInitialTheme() ); // TODO: temp. testing code only, remove once obsolete @@ -136,8 +155,10 @@ export const EuiThemeProvider = ({ setSystem(newSystem); prevSystemKey.current = newSystem.key; isParentTheme.current = false; + + updateVisColorStore(theme); } - }, [_system, parentSystem]); + }, [_system, parentSystem, theme, updateVisColorStore]); useEffect(() => { const newModifications = mergeDeep(parentModifications, _modifications); diff --git a/packages/eui/src/themes/amsterdam/global_styling/variables/_colors.ts b/packages/eui/src/themes/amsterdam/global_styling/variables/_colors.ts index 4db263b88f8..241487279e0 100644 --- a/packages/eui/src/themes/amsterdam/global_styling/variables/_colors.ts +++ b/packages/eui/src/themes/amsterdam/global_styling/variables/_colors.ts @@ -30,6 +30,7 @@ import { makeHighContrastColor, makeDisabledContrastColor, } from '../../../../services/color/contrast'; +import { colorVis } from './_colors_vis'; /* * LIGHT THEME @@ -579,4 +580,5 @@ export const colors: _EuiThemeColors = { ink: '#000', LIGHT: light_colors, DARK: dark_colors_ams, + vis: colorVis, }; diff --git a/packages/eui-theme-common/src/global_styling/variables/_colors_vis.scss b/packages/eui/src/themes/amsterdam/global_styling/variables/_colors_vis.scss similarity index 100% rename from packages/eui-theme-common/src/global_styling/variables/_colors_vis.scss rename to packages/eui/src/themes/amsterdam/global_styling/variables/_colors_vis.scss diff --git a/packages/eui-theme-common/src/global_styling/variables/_colors_vis.ts b/packages/eui/src/themes/amsterdam/global_styling/variables/_colors_vis.ts similarity index 55% rename from packages/eui-theme-common/src/global_styling/variables/_colors_vis.ts rename to packages/eui/src/themes/amsterdam/global_styling/variables/_colors_vis.ts index 4459b04ff8c..1c925364c40 100644 --- a/packages/eui-theme-common/src/global_styling/variables/_colors_vis.ts +++ b/packages/eui/src/themes/amsterdam/global_styling/variables/_colors_vis.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { _EuiThemeVisColors } from '@elastic/eui-theme-common'; + /** * NOTE: These were quick conversions of their Sass counterparts. * They have yet to be used/tested. @@ -48,7 +50,7 @@ const euiPaletteColorBlind = { }, }; -export const colorVis = { +export const colorVis: _EuiThemeVisColors = { euiColorVis0: euiPaletteColorBlind.euiColorVis0.graphic, euiColorVis1: euiPaletteColorBlind.euiColorVis1.graphic, euiColorVis2: euiPaletteColorBlind.euiColorVis2.graphic, @@ -59,4 +61,44 @@ export const colorVis = { euiColorVis7: euiPaletteColorBlind.euiColorVis7.graphic, euiColorVis8: euiPaletteColorBlind.euiColorVis8.graphic, euiColorVis9: euiPaletteColorBlind.euiColorVis9.graphic, + + euiColorVisAsTextLight0: '#006BB4', + euiColorVisAsTextLight1: '#017D73', + euiColorVisAsTextLight2: '#F5A700', + euiColorVisAsTextLight3: '#BD271E', + euiColorVisAsTextLight4: '#DD0A73', + euiColorVisAsTextLight5: '#006BB4', // duplicated to handle color amount difference between themes + euiColorVisAsTextLight6: '#017D73', + + euiColorVisAsTextDark0: '#1BA9F5', + euiColorVisAsTextDark1: '#7DE2D1', + euiColorVisAsTextDark2: '#F990C0', + euiColorVisAsTextDark3: '#F66', + euiColorVisAsTextDark4: '#FFCE7A', + euiColorVisAsTextDark5: '#1BA9F5', + euiColorVisAsTextDark6: '#7DE2D1', + + euiColorVisSuccess0: '#209280', + euiColorVisSuccess1: euiPaletteColorBlind.euiColorVis0.graphic, + euiColorVisWarning0: '#D6BF57', + euiColorVisDanger0: '#CC5642', + euiColorVisDanger1: euiPaletteColorBlind.euiColorVis9.graphic, + + euiColorVisNeutral0: '#FFFFFF', + + euiColorVisGrey0: '#d3dae6', + euiColorVisGrey1: '#98a2b3', + euiColorVisGrey2: '#69707d', + euiColorVisGrey3: '#343741', + + euiColorVisWarm0: '#FBFBDC', + euiColorVisWarm1: euiPaletteColorBlind.euiColorVis7.graphic, + euiColorVisWarm2: euiPaletteColorBlind.euiColorVis9.graphic, + + euiColorVisCool0: '#EBEFF5', + euiColorVisCool1: euiPaletteColorBlind.euiColorVis1.graphic, + euiColorVisCool2: '#6092C0', + + euiColorVisComplementary0: euiPaletteColorBlind.euiColorVis1.graphic, + euiColorVisComplementary1: euiPaletteColorBlind.euiColorVis7.graphic, }; diff --git a/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts b/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts index ba2ec5dbcf6..25947b1a521 100644 --- a/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts +++ b/packages/eui/src/themes/amsterdam/global_styling/variables/_components.ts @@ -21,6 +21,7 @@ import { makeHighContrastColor } from '../../../../services/color/contrast'; import { buttons } from './_buttons'; import { forms } from './_forms'; +import { colorVis } from './_colors_vis'; const component_colors: _EuiThemeComponentColors = { badgeBackgroundSubdued: computed( @@ -60,6 +61,91 @@ const component_colors: _EuiThemeComponentColors = { ['colors.lightestShade'] ), + codeBackground: computed( + ([lightestShade]) => lightestShade, + ['colors.lightestShade'] + ), + codeBackgroundSelected: 'inherit', + codeColor: computed( + ([lightestShade, text]) => makeHighContrastColor(text)(lightestShade), + ['colors.lightestShade', 'colors.text'] + ), + codeInlineColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis3)(lightestShade), + ['colors.lightestShade'] + ), + codeCommentColor: computed( + ([lightestShade, subduedText]) => + makeHighContrastColor(subduedText)(lightestShade), + ['colors.lightestShade', 'colors.subduedText'] + ), + codeSelectorColor: 'inherit', + codeStringColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis2)(lightestShade), + ['colors.lightestShade'] + ), + codeTagColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis1)(lightestShade), + ['colors.lightestShade'] + ), + codeNameColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis1)(lightestShade), + ['colors.lightestShade'] + ), + codeNumberColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis0)(lightestShade), + ['colors.lightestShade'] + ), + codeKeywordColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis3)(lightestShade), + ['colors.lightestShade'] + ), + codeFunctionTitleColor: 'inherit', + codeTypeColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis1)(lightestShade), + ['colors.lightestShade'] + ), + codeAttributeColor: 'inherit', + codeSymbolColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis9)(lightestShade), + ['colors.lightestShade'] + ), + codeParamsColor: 'inherit', + codeMetaColor: computed( + ([lightestShade, subduedText]) => + makeHighContrastColor(subduedText)(lightestShade), + ['colors.lightestShade', 'colors.subduedText'] + ), + codeTitleColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis7)(lightestShade), + ['colors.lightestShade'] + ), + codeSectionColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis9)(lightestShade), + ['colors.lightestShade'] + ), + codeAdditionColor: computed( + ([lightestShade]) => + makeHighContrastColor(colorVis.euiColorVis0)(lightestShade), + ['colors.lightestShade'] + ), + codeDeletionColor: computed( + ([lightestShade, danger]) => makeHighContrastColor(danger)(lightestShade), + ['colors.lightestShade', 'colors.danger'] + ), + codeSelectorClassColor: 'inherit', + codeSelectorIdColor: 'inherit', + collapsibleNavGroupBackground: computed(([body]) => body, ['colors.body']), collapsibleNavGroupBackgroundDark: computed( ([darkestShade]) => shade(darkestShade, 0.2), @@ -137,6 +223,23 @@ const component_colors: _EuiThemeComponentColors = { ['colors.primary'] ), + loadingChartMonoBackground0: computed( + ([lightShade]) => lightShade, + ['colors.lightShade'] + ), + loadingChartMonoBackground1: computed( + ([lightShade]) => shade(lightShade, 0.04), + ['colors.lightShade'] + ), + loadingChartMonoBackground2: computed( + ([lightShade]) => shade(lightShade, 0.08), + ['colors.lightShade'] + ), + loadingChartMonoBackground3: computed( + ([lightShade]) => shade(lightShade, 0.12), + ['colors.lightShade'] + ), + markBackground: computed( ([primary]) => transparentize(primary, 0.1), ['colors.primary'] @@ -307,6 +410,23 @@ export const components: _EuiThemeComponents = { ['colors.lightShade'] ), + loadingChartMonoBackground0: computed( + ([lightShade]) => lightShade, + ['colors.lightShade'] + ), + loadingChartMonoBackground1: computed( + ([lightShade]) => tint(lightShade, 0.04), + ['colors.lightShade'] + ), + loadingChartMonoBackground2: computed( + ([lightShade]) => tint(lightShade, 0.08), + ['colors.lightShade'] + ), + loadingChartMonoBackground3: computed( + ([lightShade]) => tint(lightShade, 0.12), + ['colors.lightShade'] + ), + markBackground: computed( ([primary]) => transparentize(primary, 0.3), ['colors.primary'] diff --git a/packages/eui/src/themes/amsterdam/global_styling/variables/_index.scss b/packages/eui/src/themes/amsterdam/global_styling/variables/_index.scss index bb4d5ee97f8..5ed28331a3b 100644 --- a/packages/eui/src/themes/amsterdam/global_styling/variables/_index.scss +++ b/packages/eui/src/themes/amsterdam/global_styling/variables/_index.scss @@ -2,5 +2,6 @@ @import '../../../../global_styling/variables/index'; @import 'states'; +@import 'colors_vis'; @import 'typography'; @import 'forms'; diff --git a/packages/eui/src/themes/amsterdam/theme.ts b/packages/eui/src/themes/amsterdam/theme.ts index 6024cf412df..d0fc3b18adc 100644 --- a/packages/eui/src/themes/amsterdam/theme.ts +++ b/packages/eui/src/themes/amsterdam/theme.ts @@ -34,6 +34,7 @@ export const euiThemeAmsterdam: EuiThemeShape = { components, flags: { hasGlobalFocusColor: false, + hasVisColorAdjustment: true, }, }; diff --git a/packages/eui/src/themes/themes.ts b/packages/eui/src/themes/themes.ts index 540fac2e5fe..155499a790b 100644 --- a/packages/eui/src/themes/themes.ts +++ b/packages/eui/src/themes/themes.ts @@ -17,13 +17,8 @@ export interface EUI_THEME { export const EUI_THEMES: EUI_THEME[] = [ { - text: 'Light', - value: 'light', - provider: EuiThemeAmsterdam, - }, - { - text: 'Dark', - value: 'dark', + text: 'Amsterdam', + value: AMSTERDAM_NAME_KEY, provider: EuiThemeAmsterdam, }, ]; diff --git a/packages/website/docs/components/utilities/_category_.yml b/packages/website/docs/components/utilities/_category_.yml new file mode 100644 index 00000000000..f0ffe3e2a80 --- /dev/null +++ b/packages/website/docs/components/utilities/_category_.yml @@ -0,0 +1,3 @@ +label: Utilities +collapsed: false +position: 11 diff --git a/packages/website/docs/components/utilities/color_palettes/_category_.yml b/packages/website/docs/components/utilities/color_palettes/_category_.yml new file mode 100644 index 00000000000..02ae549f1be --- /dev/null +++ b/packages/website/docs/components/utilities/color_palettes/_category_.yml @@ -0,0 +1,3 @@ +link: + type: doc + id: component_color_palettes_overview diff --git a/packages/website/docs/components/utilities/color_palettes/overview.mdx b/packages/website/docs/components/utilities/color_palettes/overview.mdx new file mode 100644 index 00000000000..8385fe758e1 --- /dev/null +++ b/packages/website/docs/components/utilities/color_palettes/overview.mdx @@ -0,0 +1,459 @@ +--- +id: component_color_palettes_overview +title: Color Palettes +export_name: EuiColorPalettes +slug: /color_palettes +--- + +# Color palettes + +EUI provides a base set of color palettes that return an array of hexadecimal color for use in other EUI components or charts. + +## Preset qualitative palettes + +Qualitative palettes are best suited for communicating and comparing discrete data series. +EUI recommends using the `euiPaletteColorBlind()` for qualitative and categorical data. + +This palette is restricted to only 10 colors. However, you can add more groups of 10 which +are alternates of the original. This is better than allowing the initial set to loop. + +These colors are meant to be used as graphics and contrasted against the value of `euiColorEmptyShade` +for the current theme. When placing text on top of these colors, use the `euiPaletteColorBlindBehindText()` +variant. It is a brightened version of the base palette to create better contrast with text. + +```tsx interactive +import React, { Fragment } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiBadge, + EuiFlexGrid, + euiPaletteColorBlind, + euiPaletteColorBlindBehindText, + ColorPaletteFlexItem, + ColorPaletteCopyCode, + EUI_VIS_COLOR_STORE, +} from '@elastic/eui'; +import { VIS_COLOR_STORE_EVENTS } from '@elastic/eui-theme-common'; +import { css } from '@emotion/css'; + +const ColorPaletteFlexItem = ({ hexCode, className, ...rest }) => { + const { euiTheme } = useEuiTheme(); + + return ( + + + + ); +}; + +const ColorPaletteCopyCode = ({ textToCopy, code }) => { + return ( + + + {(copy) => ( + + {code} + + )} + + + ); +}; + +const getCustomPalettes = () => [ + { + title: 'Max 10 colors', + palette: euiPaletteColorBlind(), + code: 'euiPaletteColorBlind()', + }, + { + title: 'More than 10 colors are needed', + palette: euiPaletteColorBlind({ rotations: 2 }), + code: 'euiPaletteColorBlind({rotations: 2})', + }, + { + title: + 'Series may have multiple metrics and so the colors must coordinate but be distinguishable', + palette: euiPaletteColorBlind({ + rotations: 3, + order: 'group', + direction: 'both', + }), + code: "euiPaletteColorBlind({rotations: 3, order: 'group', direction: 'both'})", + }, + { + title: + "The default sort order is close but not exactly aligned with the color wheel. To sort this better add the 'natural' sort param.", + palette: euiPaletteColorBlind({ sortBy: 'natural' }), + code: "euiPaletteColorBlind({sortBy: 'natural'})", + }, +]; + +export default () => { + const { euiTheme } = useEuiTheme(); + + const [customPalettes, setCustomPalettes] = useState(getCustomPalettes); + + const handleVisColorThemeChange = () => { + const palette = getCustomPalettes(); + setCustomPalettes(palette); + }; + + useEffect(() => { + const storeId = EUI_VIS_COLOR_STORE.subscribe( + VIS_COLOR_STORE_EVENTS.UPDATE, + handleVisColorThemeChange + ); + + return () => { + EUI_VIS_COLOR_STORE.unsubscribe(VIS_COLOR_STORE_EVENTS.UPDATE, storeId); + }; + }, []); + + return ( + + {customPalettes.map((palette) => ( + + +

{palette.title}

+
+ + + + + {palette.palette.map((hexCode) => ( + + ))} + + + + + + + +
+ ))} + +

Behind text variant

+
+ + + + + {euiPaletteColorBlindBehindText({ sortBy: 'natural' }).map( + (color, i) => ( + + + Text + + + ) + )} + + + + + + +
+ ) +}; +``` + +## Recommended quantitative palettes + +Quantitative palettes are best suited for displaying data on a continuum, as in the case of health statuses +and large geographic or demographic-based data sets. +EUI provides the following common palettes for quantitative data and [charts](#/docs/elastic-charts/creating-charts/). +Just pass in the number of steps needed and the function will interpolate between the colors. + +:::warning +The palette for status is the only palette that has proper contrast ratios. When using the other palettes, consider adding another form of the data for screen readers. +::: + +```tsx interactive +import React, { Fragment, useState } from 'react'; +import { css } from '@emotion/css'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiRange, + EuiFormRow, + EuiSpacer, + euiPaletteComplementary, + euiPaletteForStatus, + euiPaletteForTemperature, + euiPaletteCool, + euiPaletteWarm, + euiPaletteRed, + euiPaletteGreen, + euiPaletteGray, +} from '@elastic/eui'; + +const ColorPaletteFlexItem = ({ hexCode, className, ...rest }) => { + const { euiTheme } = useEuiTheme(); + + return ( + + + + ); +}; + +const ColorPaletteCopyCode = ({ textToCopy, code }) => { + return ( + + + {(copy) => ( + + {code} + + )} + + + ); +}; + +const paletteData = { + euiPaletteForStatus, + euiPaletteForTemperature, + euiPaletteComplementary, + euiPaletteRed, + euiPaletteGreen, + euiPaletteCool, + euiPaletteWarm, + euiPaletteGray, +}; +const paletteNames = Object.keys(paletteData); + +export default () => { + const { euiTheme } = useEuiTheme(); + const [length, setLength] = useState(5); + + const onLengthChange = (e) => { + setLength(e.currentTarget.value); + }; + + return ( + + + + + + + + {paletteNames.map((paletteName) => ( + + + + {paletteData[paletteName](Number(length)).map((hexCode) => ( + + ))} + + + + + + + ))} + + ); +}; + +``` + +## Custom palettes + +Use the `colorPalette` service to generate a custom, gradiated palette array of any length from one or more +hexadecimal color codes. The third parameter divergent, will interpolate between the two halves of the +spectrums separately. If a middle point is not provided, it will graduate to light gray. + +```tsx interactive +import React, { Fragment, useState } from 'react'; +import { css } from '@emotion/css'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiRange, + EuiFormRow, + EuiSpacer, + euiPaletteColorBlind, + colorPalette +} from '@elastic/eui'; + +const ColorPaletteFlexItem = ({ hexCode, className, ...rest }) => { + const { euiTheme } = useEuiTheme(); + + return ( + + + + ); +}; + +const ColorPaletteCopyCode = ({ textToCopy, code }) => { + return ( + + + {(copy) => ( + + {code} + + )} + + + ); +}; + +const customPalettes = [ + [euiPaletteColorBlind()[3]], + [euiPaletteColorBlind()[3], euiPaletteColorBlind()[4]], + [euiPaletteColorBlind()[3], euiPaletteColorBlind()[4]], +]; + +export default () => { + const { euiTheme } = useEuiTheme(); + const [length, setLength] = useState(10); + + const onLengthChange = (e) => { + setLength(e.currentTarget.value); + }; + + return ( + + + + + + + + {customPalettes.map((palette, i) => ( + + + + {colorPalette(palette, Number(length), i > 1).map((hexCode) => ( + + ))} + + + + 1 ? ', true' : '' + });`} + code={`colorPalette([${palette}], ${length}${ + i > 1 ? ', true' : '' + });`} + /> + + + ))} + + ); +}; +``` \ No newline at end of file diff --git a/packages/website/package.json b/packages/website/package.json index fde2b3f951d..f1530e1bcf4 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -23,6 +23,7 @@ "@elastic/eui": "workspace:^", "@elastic/eui-docgen": "workspace:^", "@elastic/eui-docusaurus-theme": "workspace:^", + "@elastic/eui-theme-common": "workspace:^", "@emotion/css": "^11.11.2", "@emotion/react": "^11.11.4", "@faker-js/faker": "^8.4.1", diff --git a/packages/website/src/theme/Demo/default_scope.ts b/packages/website/src/theme/Demo/default_scope.ts index 64b2ee37217..07508c1962f 100644 --- a/packages/website/src/theme/Demo/default_scope.ts +++ b/packages/website/src/theme/Demo/default_scope.ts @@ -8,13 +8,15 @@ import * as EUI from '@elastic/eui'; import * as EmotionReact from '@emotion/react'; -import moment from 'moment' +import * as EuiThemeCommon from '@elastic/eui-theme-common'; +import moment from 'moment'; import { faker } from '@faker-js/faker'; import { DisplayToggles } from '@site/src/components'; export const demoDefaultScope: Record = { // EUI exports ...EUI, + ...EuiThemeCommon, // Emotion ...EmotionReact, @@ -25,4 +27,4 @@ export const demoDefaultScope: Record = { // Utilities DisplayToggles, -} +}; diff --git a/yarn.lock b/yarn.lock index 8a63617c1b3..e21076f9e0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5825,6 +5825,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.21.5" "@emotion/react": "npm:^11.11.0" "@types/jest": "npm:^29.5.12" + "@types/lodash": "npm:^4.14.202" "@types/prettier": "npm:2.7.3" "@types/react": "npm:^16.9 || ^17.0 || ^18.0" "@types/react-dom": "npm:^16.9 || ^17.0 || ^18.0" @@ -5837,6 +5838,7 @@ __metadata: eslint-plugin-local: "npm:^1.0.0" eslint-plugin-prettier: "npm:^4.2.1" jest: "npm:^29.7.0" + lodash: "npm:^4.17.21" prettier: "npm:^2.8.8" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" @@ -5868,6 +5870,7 @@ __metadata: "@elastic/eui": "workspace:^" "@elastic/eui-docgen": "workspace:^" "@elastic/eui-docusaurus-theme": "workspace:^" + "@elastic/eui-theme-common": "workspace:^" "@emotion/css": "npm:^11.11.2" "@emotion/react": "npm:^11.11.4" "@faker-js/faker": "npm:^8.4.1"