Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions packages/code-studio/src/styleguide/Colors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@ import classNames from 'classnames';

function Colors(): React.ReactElement {
const graySwatches = [
'100',
'200',
'300',
'400',
'500',
'600',
'700',
'800',
'900',
].map(swatch => (
['100', '900'],
['200', '800'],
['300', '700'],
['400', '600'],
['500', '500'],
['600', '500'],
['700', '400'],
['800', '300'],
['850', '200'],
['900', '75'],
].map(([swatch, dh]) => (
<div
key={swatch}
className={classNames('swatch', 'gray-swatch', `gray-swatch-${swatch}`)}
>
Gray-
{swatch}
<span>
Gray-
{swatch}
</span>
<span style={{ backgroundColor: `var(--dh-color-gray-${dh})` }}>
--dh-gray-{dh}
</span>
</div>
));

Expand Down
7 changes: 7 additions & 0 deletions packages/code-studio/src/styleguide/StyleGuide.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ pre {
line-height: 2.5rem;
}

.gray-swatch {
display: flex;
span {
flex: 1 0 50%;
}
}

.swatch-content-bg {
border: 1px solid $gray-600;
margin-top: 2.5rem;
Expand Down
3 changes: 3 additions & 0 deletions packages/code-studio/src/styleguide/StyleGuide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Typograpy from './Typography';
import './StyleGuide.scss';
import DraggableLists from './DraggableLists';
import Navigations from './Navigations';
import ThemeColors from './ThemeColors';

function StyleGuide(): React.ReactElement {
return (
Expand All @@ -33,6 +34,8 @@ function StyleGuide(): React.ReactElement {

<Colors />

<ThemeColors />

<Buttons />

<Progress />
Expand Down
35 changes: 35 additions & 0 deletions packages/code-studio/src/styleguide/ThemeColors.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.themeColors {
--swatch-height: 35px;
--column-gap: 14px;

display: grid;
column-gap: var(--column-gap);
// Add as many columns as will fit in the container each 210px wide.
// Row height is set to the swatch height (35px) by dynamic `grid-row` style
// attributes set in ThemeColors.tsx.
grid-template-columns: repeat(auto-fit, 210px);

.label {
display: flex;
align-items: end;
justify-content: space-between;
gap: 4px;
height: var(--swatch-height);
text-transform: capitalize;
white-space: nowrap;
}

.swatch {
display: flex;
align-items: center;
height: var(--swatch-height);
justify-content: space-between;
padding: 0 10px;

span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
178 changes: 178 additions & 0 deletions packages/code-studio/src/styleguide/ThemeColors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import React, { useMemo } from 'react';
import { Tooltip } from '@deephaven/components';
import { ColorUtils } from '@deephaven/utils';
import palette from '@deephaven/components/src/theme/theme-dark/theme-dark-palette.css?inline';
import semantic from '@deephaven/components/src/theme/theme-dark/theme-dark-semantic.css?inline';
import semanticEditor from '@deephaven/components/src/theme/theme-dark/theme-dark-semantic-editor.css?inline';
import semanticGrid from '@deephaven/components/src/theme/theme-dark/theme-dark-semantic-grid.css?inline';
import styles from './ThemeColors.module.scss';

// 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',
// 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',
},
grid: { data: 'Data Bars', context: 'Context Menu' },
};

export function ThemeColors(): JSX.Element {
const swatchDataGroups = useMemo(
() => ({
'Theme Color Palette': buildColorGroups(palette, 1),
'Semantic Colors': buildColorGroups(semantic, 1),
'Editor Colors': buildColorGroups(semanticEditor, 2, renameGroups.editor),
'Grid Colors': buildColorGroups(semanticGrid, 2, renameGroups.grid),
}),
[]
);

return (
<>
{Object.entries(swatchDataGroups).map(([label, data]) => (
<div key={label}>
<h2 className="ui-title">{label}</h2>
<div className={styles.themeColors}>
{Object.entries(data).map(([group, swatchData]) => (
<div
key={group}
// This is the secret sauce for filling columns. The height of
// each swatch group spans multiple rows (the number of swatches
// + 1 for the label). This causes the grid to create rows
// based on the swatch height (35px), and each swatch (also the
// group label) neatly fits in a grid cell. The grid will put a
// group in each column and then wrap back around to the first
// until all groups are placed.
style={{ gridRow: `span ${swatchData.length + 1}` }}
>
<span className={styles.label}>{group}</span>
{swatchData.map(({ name, value }) => (
<div
key={name}
className={styles.swatch}
style={{
backgroundColor: value,
color: `var(--dh-color-${contrastColor(value)})`,
}}
>
<Tooltip>
<div>{name}</div>
<div>{value}</div>
<div>
{ColorUtils.normalizeCssColor(value).replace(
/^(#[a-f0-9]{6})ff$/,
'$1'
)}
</div>
</Tooltip>
<span>{name.replace('--dh-color-', '')}</span>
{name.endsWith('-hue') ? <span>{value}</span> : null}
</div>
))}
</div>
))}
</div>
</div>
))}
</>
);
}

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);

return styleText
.split('\n')
.map(line => /^\s{2}(--dh-color-(?:[^:]+))/.exec(line)?.[1])
.filter(Boolean)
.map(varName =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
({ name: varName!, value: computedStyle.getPropertyValue(varName!)! })
);
}

/** 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;
}
2 changes: 2 additions & 0 deletions packages/components/src/theme/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from './theme-dark';
export * from './theme-light';
export * from './ThemeModel';
export * from './ThemeProvider';
export * from './ThemeUtils';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
--dh-color-gray-hue: 0deg;
--dh-color-gray-50: hsl(var(--dh-color-gray-hue) 6% 10%);
--dh-color-gray-75: hsl(var(--dh-color-gray-hue) 5% 13%);
--dh-color-gray-100: hsl(var(--dh-color-gray-hue), 5%, 17%);
--dh-color-gray-100: hsl(var(--dh-color-gray-hue) 5% 17%);
--dh-color-gray-200: hsl(var(--dh-color-gray-hue) 4% 19%);
--dh-color-gray-300: hsl(var(--dh-color-gray-hue) 4% 21%);
--dh-color-gray-400: hsl(var(--dh-color-gray-hue) 2% 25%);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
:root {
/* Editor */
--dh-color-editor-background: var(--dh-color-content-background);
--dh-color-editor-foreground: var(--dh-color-gray-900);
--dh-color-editor-error-foreground: var(--dh-color-visual-red);
--dh-color-editor-line-number-foreground: var(--dh-color-gray-700);
--dh-color-editor-bg: var(--dh-color-content-background);
--dh-color-editor-fg: var(--dh-color-gray-900);
--dh-color-editor-error-fg: var(--dh-color-visual-red);
--dh-color-editor-line-number-fg: var(--dh-color-gray-700);
--dh-color-editor-line-highlight-bg: var(--dh-color-gray-200);
--dh-color-editor-selection-background: var(--dh-color-text-highlight);
--dh-color-editor-selection-bg: var(--dh-color-text-highlight);

/* Code rules */
--dh-color-editor-string: var(--dh-color-visual-yellow);
--dh-color-editor-string-delim: var(--dh-color-gray-700);
--dh-color-editor-comment: var(--dh-color-gray-700);
--dh-color-editor-delimiter: var(--dh-color-gray-700);
--dh-color-editor-predefined: var(--dh-color-visual-green);
--dh-color-editor-identifier-js: var(--dh-color-visual-yellow);
--dh-color-editor-identifier-namespace: var(--dh-color-visual-red);
--dh-color-editor-identifier: var(--dh-color-gray-900);
--dh-color-editor-keyword: var(--dh-color-visual-cyan);
--dh-color-editor-storage: var(--dh-color-visual-red);
--dh-color-editor-number: var(--dh-color-visual-purple);
--dh-color-editor-operator: var(--dh-color-visual-red);
--dh-color-editor-identifier: var(--dh-color-gray-900);
--dh-color-editor-identifier-namespace: var(--dh-color-visual-red);
--dh-color-editor-identifier-js: var(--dh-color-visual-yellow);
--dh-color-editor-comment: var(--dh-color-gray-700);
--dh-color-editor-predefined: var(--dh-color-visual-green);
--dh-color-editor-storage: var(--dh-color-visual-red);
--dh-color-editor-string-delim: var(--dh-color-gray-700);
--dh-color-editor-string: var(--dh-color-visual-yellow);

/* Input */
--dh-color-editor-focus-border: var(--dh-color-focus-border);
--dh-color-editor-input-option-active-border: var(--dh-color-focus-ring);
--dh-color-editor-input-background: var(--dh-color-background);
--dh-color-editor-input-foreground: var(--dh-color-text);
--dh-color-editor-input-bg: var(--dh-color-background);
--dh-color-editor-input-fg: var(--dh-color-text);
--dh-color-editor-input-border: var(--dh-color-border);

/* Menus */
--dh-color-editor-context-menu-background: var(--dh-color-gray-300);
--dh-color-editor-context-menu-foreground: var(--dh-color-gray-900);
--dh-color-editor-menu-selection-background: var(--dh-color-highlight-hover);
--dh-color-editor-context-menu-bg: var(--dh-color-gray-300);
--dh-color-editor-context-menu-fg: var(--dh-color-gray-900);
--dh-color-editor-menu-selection-bg: var(--dh-color-highlight-hover);

/* Logging */
--dh-color-editor-log-date: var(--dh-color-gray-700);
Expand All @@ -43,25 +43,23 @@
--dh-color-editor-log-trace: var(--dh-color-visual-green);

/* Find */
--dh-color-editor-find-background: var(--dh-color-gray-200);
--dh-color-editor-find-match-background: var(--dh-color-highlight-selected);
--dh-color-editor-find-match-highlight-background: var(
--dh-color-editor-find-bg: var(--dh-color-gray-200);
--dh-color-editor-find-match-bg: var(--dh-color-highlight-selected);
--dh-color-editor-find-match-highlight-bg: var(
--dh-color-highlight-selected-hover
);
--dh-color-editor-find-option-active-background: var(--dh-color-accent-700);
--dh-color-editor-find-option-active-foreground: var(--dh-color-gray-900);
--dh-color-editor-find-option-active-bg: var(--dh-color-accent-700);
--dh-color-editor-find-option-active-fg: var(--dh-color-gray-900);

/* Suggest */
--dh-color-editor-suggest-background: var(--dh-color-gray-200);
--dh-color-editor-suggest-bg: var(--dh-color-gray-200);
--dh-color-editor-suggest-border: var(--dh-color-gray-400);
--dh-color-editor-suggest-foreground: var(--dh-color-gray-100);
--dh-color-editor-suggest-selected-background: var(
--dh-color-highlight-selected
);
--dh-color-editor-suggest-highlight-foreground: var(--dh-color-accent-700);
--dh-color-editor-suggest-hover-background: var(--dh-color-highlight-hover);
--dh-color-editor-suggest-fg: var(--dh-color-gray-100);
--dh-color-editor-suggest-selected-bg: var(--dh-color-highlight-selected);
--dh-color-editor-suggest-highlight-fg: var(--dh-color-accent-700);
--dh-color-editor-suggest-hover-bg: var(--dh-color-highlight-hover);

/* Links */
--dh-color-editor-link-foreground: var(--dh-color-accent-1000);
--dh-color-editor-link-active-foreground: var(--dh-color-accent-1100);
--dh-color-editor-link-active-fg: var(--dh-color-accent-1100);
}
Loading