Skip to content

Commit

Permalink
feat: Theming - Inline svgs (#1651)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmingles authored Nov 30, 2023
1 parent 21131fe commit 1e40d3e
Show file tree
Hide file tree
Showing 106 changed files with 1,409 additions and 665 deletions.
57 changes: 57 additions & 0 deletions packages/code-studio/src/styleguide/GoldenLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
import classnames from 'classnames';
import { sampleSectionIdAndClasses } from './utils';

const tabs = ['Tab 1', 'Tab 2', 'Tab 3'];

function Tab({
isActive,
title,
}: {
isActive: boolean;
title: string;
}): JSX.Element {
return (
<li className={classnames('lm_tab', isActive && 'lm_active')}>
<span className="lm_title_before" />
<span className="lm_title">{title}</span>
<span className="lm_close_tab" />
</li>
);
}

export function GoldenLayout(): JSX.Element {
return (
<div {...sampleSectionIdAndClasses('golden-layout')}>
<h2 className="ui-title">Golden Layout</h2>
{[false, true].map(isMaximised => (
<React.Fragment key={String(isMaximised)}>
<h5>{isMaximised ? 'Minimized' : 'Maximised'}</h5>
<div
style={{ position: 'relative', border: 'none' }}
className={isMaximised ? 'lm_maximised' : undefined}
>
<div className="lm_header">
<ul className="lm_tabs">
{tabs.map((tab, i) => (
<Tab key={tab} isActive={i === 0} title={tab} />
))}
</ul>
<ul className="lm_controls">
<li className="lm_tabpreviousbutton" />
<li className="lm_tabnextbutton" />
<li className="lm_maximise" />
<li className="lm_popout" />
<li className="lm_tabdropdown" />
<li className="lm_close" />
</ul>
</div>
</div>
</React.Fragment>
))}
</div>
);
}

export default GoldenLayout;
6 changes: 4 additions & 2 deletions packages/code-studio/src/styleguide/Inputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,11 @@ function Inputs(): React.ReactElement {
validationError={!validateValue ? 'Value not set' : undefined}
isModified={!!validateValue}
id="validateInput1"
labelText="Input Field"
labelText={`Input Field${on ? ' (disabled)' : ''}`}
hintText="Hint text"
>
<input
disabled={on}
type="text"
className="form-control"
aria-describedby="emailHelp"
Expand All @@ -320,9 +321,10 @@ function Inputs(): React.ReactElement {
validateOption === 'Invalid' ? 'Invalid value' : undefined
}
id="validateLabelInput2"
labelText="Dropdown"
labelText={`Dropdown${on ? ' (disabled)' : ''}`}
>
<Select
disabled={on}
onChange={eventTargetValue =>
setValidateOption(eventTargetValue)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/code-studio/src/styleguide/StyleGuide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import SpectrumComponents from './SpectrumComponents';
import SamplesMenu, { SampleMenuCategory } from './SamplesMenu';
import GotoTopButton from './GotoTopButton';
import { HIDE_FROM_E2E_TESTS_CLASS } from './utils';
import { GoldenLayout } from './GoldenLayout';

const stickyProps = {
position: 'sticky',
Expand Down Expand Up @@ -85,6 +86,9 @@ function StyleGuide(): React.ReactElement {
<Colors />
<ThemeColors />

<SampleMenuCategory data-menu-category="Layout" />
<GoldenLayout />

<SampleMenuCategory data-menu-category="Components" />
<Buttons />
<Progress />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

.label {
display: flex;
align-items: end;
align-items: flex-end;
justify-content: space-between;
gap: 4px;
height: var(--swatch-height);
Expand Down
176 changes: 15 additions & 161 deletions packages/code-studio/src/styleguide/ThemeColors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,78 +10,19 @@ import semanticGrid from '@deephaven/components/src/theme/theme-dark/theme-dark-
import components from '@deephaven/components/src/theme/theme-dark/theme-dark-components.css?inline';
import styles from './ThemeColors.module.scss';
import { sampleSectionIdAndClasses } from './utils';

// Group names are extracted from var names via a regex capture group. Most of
// them work pretty well, but some need to be remapped to a more appropriate
// group.
const reassignVarGroups: Record<string, string> = {
'--dh-color-black': 'gray',
'--dh-color-white': 'gray',
// Semantic
'--dh-color-visual-positive': 'Visual Status',
'--dh-color-visual-negative': 'Visual Status',
'--dh-color-visual-notice': 'Visual Status',
'--dh-color-visual-info': 'Visual Status',
// Editor
'--dh-color-editor-bg': 'editor',
'--dh-color-editor-fg': 'editor',
'--dh-color-editor-context-menu-bg': 'menus',
'--dh-color-editor-context-menu-fg': 'menus',
'--dh-color-editor-menu-selection-bg': 'menus',
// Grid
'--dh-color-grid-bg': 'grid',
'--dh-color-grid-number-positive': 'Data Types',
'--dh-color-grid-number-negative': 'Data Types',
'--dh-color-grid-number-zero': 'Data Types',
'--dh-color-grid-date': 'Data Types',
'--dh-color-grid-string-null': 'Data Types',
};

// Mappings of variable groups to rename
const renameGroups = {
editor: {
line: 'editor',
comment: 'code',
string: 'code',
number: 'code',
delimiter: 'code',
identifier: 'code',
keyword: 'code',
operator: 'code',
storage: 'code',
predefined: 'code',
selection: 'state',
focus: 'state',
},
chart: {
axis: 'Chart',
bg: 'Chart',
grid: 'Chart',
plot: 'Chart',
title: 'Chart',
active: 'Data',
trend: 'Data',
area: 'Data',
range: 'Data',
line: 'Deprecated',
},
grid: { data: 'Data Bars', context: 'Context Menu' },
semantic: {
positive: 'status',
negative: 'status',
notice: 'status',
info: 'status',
well: 'wells',
},
};
import {
buildColorGroups,
contrastColor,
INVALID_COLOR_BORDER_STYLE,
} from './colorUtils';

const swatchDataGroups = {
'Theme Color Palette': buildColorGroups(palette, 1),
'Semantic Colors': buildColorGroups(semantic, 1, renameGroups.semantic),
'Chart Colors': buildColorGroups(chart, 2, renameGroups.chart),
'Editor Colors': buildColorGroups(semanticEditor, 2, renameGroups.editor),
'Grid Colors': buildColorGroups(semanticGrid, 2, renameGroups.grid),
'Component Colors': buildColorGroups(components, 1),
'Theme Color Palette': buildColorGroups('palette', palette, 1),
'Semantic Colors': buildColorGroups('semantic', semantic, 1),
'Chart Colors': buildColorGroups('chart', chart, 2),
'Editor Colors': buildColorGroups('editor', semanticEditor, 2),
'Grid Colors': buildColorGroups('grid', semanticGrid, 2),
'Component Colors': buildColorGroups('component', components, 1),
};

export function ThemeColors(): JSX.Element {
Expand Down Expand Up @@ -110,6 +51,10 @@ export function ThemeColors(): JSX.Element {
className={styles.swatch}
style={{
backgroundColor: value,
border:
value === '' && name.length > 0
? INVALID_COLOR_BORDER_STYLE
: undefined,
color: `var(--dh-color-${contrastColor(value)})`,
}}
>
Expand Down Expand Up @@ -137,94 +82,3 @@ export function ThemeColors(): JSX.Element {
}

export default ThemeColors;

/** Return black or white contrast color */
function contrastColor(color: string): 'black' | 'white' {
const rgba = ColorUtils.parseRgba(ColorUtils.asRgbOrRgbaString(color) ?? '');
if (rgba == null || rgba.a < 0.5) {
return 'white';
}

const { r, g, b } = rgba;
const y = (299 * r + 587 * g + 114 * b) / 1000;
return y >= 128 ? 'black' : 'white';
}

/** Extract an array of { name, value } pairs for css variables in a given string */
function extractColorVars(
styleText: string
): { name: string; value: string }[] {
const computedStyle = getComputedStyle(document.documentElement);
const varNames = styleText
.split(/[\n;]/g)
// Non-minified css will have leading 2 spaces in front of each css variable
// declaration. Minified has no prefix except for the first line which will
// have ':root{' prefix.
.map(line => /^(?:\s{2}|:root\{)?(--dh-color-(?:[^:]+))/.exec(line)?.[1])
.filter((match): match is string => Boolean(match));

return varNames
.map(varName => {
const value = computedStyle.getPropertyValue(varName);

// Chart colorway consists of multiple colors, so split into separate
// swatches for illustration. Note that this assumes the colors are hsl
// values. We'll need to make this more robust if we ever change the
// default themes to use non-hsl.
if (varName === '--dh-color-chart-colorway') {
const colorwayColors = value
.split('hsl')
.filter(Boolean)
.map(v => `hsl${v.trim()}`);

return colorwayColors.map((varExp, i) => ({
name: `${varName}-${i}`,
value: varExp,
}));
}

return {
name: varName,
value,
};
})
.flat();
}

/** Group color data based on capture group value */
function buildColorGroups(
styleText: string,
captureGroupI: number,
groupRemap: Record<string, string> = {}
): Record<string, { name: string; value: string }[]> {
const swatchData = extractColorVars(styleText);

const groupData = swatchData.reduce(
(acc, { name, value }) => {
const match = /^--dh-color-([^-]+)(?:-([^-]+))?/.exec(name);
let group =
reassignVarGroups[name] ??
match?.[captureGroupI] ??
match?.[1] ??
'???';

group = groupRemap[group] ?? group;

if (acc[group] == null) {
acc[group] = [];
}

// Add a spacer for black / white
if (name === '--dh-color-black') {
acc[group].push({ name: '', value: '' });
}

acc[group].push({ name, value });

return acc;
},
{} as Record<string, { name: string; value: string }[]>
);

return groupData;
}
Loading

0 comments on commit 1e40d3e

Please sign in to comment.