Skip to content

Commit eed7e65

Browse files
authored
feat(CodeEditor): use custom PatternFly monaco theme (#11785)
* feat(CodeEditor): use custom PatternFly monaco theme * americanize the code
1 parent 0c3218f commit eed7e65

File tree

3 files changed

+172
-2
lines changed

3 files changed

+172
-2
lines changed

packages/react-code-editor/src/components/CodeEditor/CodeEditor.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { HTMLProps, ReactNode, useEffect, useRef, useState } from 'react';
22
import { css } from '@patternfly/react-styles';
33
import styles from '@patternfly/react-styles/css/components/CodeEditor/code-editor';
44
import fileUploadStyles from '@patternfly/react-styles/css/components/FileUpload/file-upload';
5+
import monoFont from '@patternfly/react-tokens/dist/esm/t_global_font_family_mono';
56
import { Button, ButtonVariant } from '@patternfly/react-core/dist/esm/components/Button';
67
import {
78
EmptyState,
@@ -13,7 +14,7 @@ import {
1314
import { Popover, PopoverProps } from '@patternfly/react-core/dist/esm/components/Popover';
1415
import { TooltipPosition } from '@patternfly/react-core/dist/esm/components/Tooltip';
1516
import { getResizeObserver } from '@patternfly/react-core/dist/esm/helpers/resizeObserver';
16-
import Editor, { EditorProps, Monaco } from '@monaco-editor/react';
17+
import Editor, { BeforeMount, EditorProps, Monaco } from '@monaco-editor/react';
1718
import type { editor } from 'monaco-editor';
1819
import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
1920
import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';
@@ -23,6 +24,7 @@ import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
2324
import Dropzone, { FileRejection } from 'react-dropzone';
2425
import { CodeEditorContext } from './CodeEditorUtils';
2526
import { CodeEditorControl } from './CodeEditorControl';
27+
import { defineThemes } from './CodeEditorTheme';
2628

2729
export type ChangeHandler = (value: string, event: editor.IModelContentChangedEvent) => void;
2830
export type EditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => void;
@@ -366,6 +368,11 @@ export const CodeEditor = ({
366368
};
367369
}, []);
368370

371+
const editorBeforeMount: BeforeMount = (monaco) => {
372+
defineThemes(monaco.editor);
373+
editorProps?.beforeMount?.(monaco);
374+
};
375+
369376
const editorDidMount: EditorDidMount = (editor, monaco) => {
370377
// eslint-disable-next-line no-bitwise
371378
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Tab, () => wrapperRef.current.focus());
@@ -428,6 +435,7 @@ export const CodeEditor = ({
428435
};
429436

430437
const editorOptions: editor.IStandaloneEditorConstructionOptions = {
438+
fontFamily: monoFont.var,
431439
scrollBeyondLastLine: height !== 'sizeToFit',
432440
readOnly: isReadOnly,
433441
cursorStyle: 'line',
@@ -571,8 +579,9 @@ export const CodeEditor = ({
571579
onChange={onModelChange}
572580
onMount={editorDidMount}
573581
loading={loading}
574-
theme={isDarkTheme ? 'vs-dark' : 'vs-light'}
582+
theme={isDarkTheme ? 'pf-v6-theme-dark' : 'pf-v6-theme-light'}
575583
{...editorProps}
584+
beforeMount={editorBeforeMount}
576585
/>
577586
</div>
578587
);
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { colors } from './themeTokenMapping';
2+
import type { editor as monacoEditor } from 'monaco-editor/esm/vs/editor/editor.api';
3+
4+
const createTheme = (mode: 'light' | 'dark'): monacoEditor.IStandaloneThemeData => ({
5+
base: mode === 'light' ? 'vs' : 'vs-dark',
6+
inherit: true,
7+
colors: {
8+
'editor.background': colors.background[mode],
9+
'editor.foreground': colors.foreground[mode],
10+
'editorLineNumber.activeForeground': colors.foreground[mode],
11+
'editorLineNumber.foreground': colors.secondaryForeground[mode]
12+
},
13+
rules: [
14+
{ token: '', foreground: colors.foreground[mode], background: colors.background[mode] },
15+
{ token: 'invalid', foreground: colors.red[mode] },
16+
{ token: 'emphasis', fontStyle: 'italic' },
17+
{ token: 'strong', fontStyle: 'bold' },
18+
19+
{ token: 'variable', foreground: colors.blue[mode] },
20+
{ token: 'variable.predefined', foreground: colors.teal[mode] },
21+
{ token: 'constant', foreground: colors.orange[mode] },
22+
{ token: 'comment', foreground: colors.gray[mode] },
23+
{ token: 'number', foreground: colors.orange[mode] },
24+
{ token: 'number.hex', foreground: colors.blue[mode] },
25+
{ token: 'regexp', foreground: colors.red[mode] },
26+
{ token: 'annotation', foreground: colors.purple[mode] },
27+
{ token: 'type', foreground: colors.yellow[mode] },
28+
29+
{ token: 'delimiter', foreground: colors.foreground[mode] },
30+
{ token: 'delimiter.html', foreground: colors.gray[mode] },
31+
{ token: 'delimiter.xml', foreground: colors.blue[mode] },
32+
33+
{ token: 'tag', foreground: colors.red[mode] },
34+
{ token: 'tag.id.jade', foreground: colors.teal[mode] },
35+
{ token: 'tag.class.jade', foreground: colors.teal[mode] },
36+
{ token: 'meta.scss', foreground: colors.red[mode] },
37+
{ token: 'metatag', foreground: colors.orange[mode] },
38+
{ token: 'metatag.content.html', foreground: colors.red[mode] },
39+
{ token: 'metatag.html', foreground: colors.gray[mode] },
40+
{ token: 'metatag.xml', foreground: colors.gray[mode] },
41+
{ token: 'metatag.php', fontStyle: 'bold' },
42+
43+
{ token: 'key', foreground: colors.orange[mode] },
44+
{ token: 'string.key.json', foreground: colors.red[mode] },
45+
{ token: 'string.value.json', foreground: colors.blue[mode] },
46+
47+
{ token: 'attribute.name', foreground: colors.red[mode] },
48+
{ token: 'attribute.value', foreground: colors.blue[mode] },
49+
{ token: 'attribute.value.number', foreground: colors.orange[mode] },
50+
{ token: 'attribute.value.unit', foreground: colors.orange[mode] },
51+
{ token: 'attribute.value.html', foreground: colors.blue[mode] },
52+
{ token: 'attribute.value.xml', foreground: colors.blue[mode] },
53+
54+
{ token: 'string', foreground: colors.green[mode] },
55+
{ token: 'string.html', foreground: colors.green[mode] },
56+
{ token: 'string.sql', foreground: colors.green[mode] },
57+
{ token: 'string.yaml', foreground: colors.green[mode] },
58+
59+
{ token: 'keyword', foreground: colors.purple[mode] },
60+
{ token: 'keyword.json', foreground: colors.purple[mode] },
61+
{ token: 'keyword.flow', foreground: colors.purple[mode] },
62+
{ token: 'keyword.flow.scss', foreground: colors.purple[mode] },
63+
64+
{ token: 'operator.scss', foreground: colors.foreground[mode] },
65+
{ token: 'operator.sql', foreground: colors.foreground[mode] },
66+
{ token: 'operator.swift', foreground: colors.foreground[mode] },
67+
{ token: 'predefined.sql', foreground: colors.purple[mode] }
68+
]
69+
});
70+
71+
/**
72+
* Define the themes `pf-v6-theme-light` and
73+
* `pf-v6-theme-dark` for an instance of Monaco editor.
74+
*/
75+
export const defineThemes = (editor: typeof monacoEditor) => {
76+
editor.defineTheme('pf-v6-theme-light', createTheme('light'));
77+
editor.defineTheme('pf-v6-theme-dark', createTheme('dark'));
78+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import backgroundDark from '@patternfly/react-tokens/dist/esm/t_color_gray_90';
2+
import textColorDark from '@patternfly/react-tokens/dist/esm/t_color_white';
3+
import textColorSubtleDark from '@patternfly/react-tokens/dist/esm/t_color_gray_30';
4+
5+
import redDark from '@patternfly/react-tokens/dist/esm/t_color_red_30';
6+
import orangeredDark from '@patternfly/react-tokens/dist/esm/t_color_red_orange_30';
7+
import orangeDark from '@patternfly/react-tokens/dist/esm/t_color_orange_30';
8+
import yellowDark from '@patternfly/react-tokens/dist/esm/t_color_yellow_30';
9+
import greenDark from '@patternfly/react-tokens/dist/esm/t_color_green_30';
10+
import tealDark from '@patternfly/react-tokens/dist/esm/t_color_teal_30';
11+
import blueDark from '@patternfly/react-tokens/dist/esm/t_color_blue_30';
12+
import purpleDark from '@patternfly/react-tokens/dist/esm/t_color_purple_30';
13+
import grayDark from '@patternfly/react-tokens/dist/esm/t_color_gray_30';
14+
15+
import backgroundLight from '@patternfly/react-tokens/dist/esm/t_color_white';
16+
import textColorLight from '@patternfly/react-tokens/dist/esm/t_global_text_color_100';
17+
import textColorSubtleLight from '@patternfly/react-tokens/dist/esm/t_global_text_color_200';
18+
19+
import redLight from '@patternfly/react-tokens/dist/esm/t_color_red_50';
20+
import orangeredLight from '@patternfly/react-tokens/dist/esm/t_color_red_orange_50';
21+
import orangeLight from '@patternfly/react-tokens/dist/esm/t_color_orange_50';
22+
import yellowLight from '@patternfly/react-tokens/dist/esm/t_color_yellow_50';
23+
import greenLight from '@patternfly/react-tokens/dist/esm/t_color_green_50';
24+
import tealLight from '@patternfly/react-tokens/dist/esm/t_color_teal_50';
25+
import blueLight from '@patternfly/react-tokens/dist/esm/t_color_blue_50';
26+
import purpleLight from '@patternfly/react-tokens/dist/esm/t_color_purple_50';
27+
import grayLight from '@patternfly/react-tokens/dist/esm/t_color_gray_50';
28+
29+
/**
30+
* This file maps the theme tokens from PatternFly to a format that can be used in the Monaco editor.
31+
*
32+
* @see https://github.com/microsoft/monaco-editor/issues/2427
33+
*/
34+
export const colors = {
35+
background: {
36+
dark: backgroundDark.value,
37+
light: backgroundLight.value
38+
},
39+
foreground: {
40+
dark: textColorDark.value,
41+
light: textColorLight.value
42+
},
43+
secondaryForeground: {
44+
dark: textColorSubtleDark.value,
45+
light: textColorSubtleLight.value
46+
},
47+
red: {
48+
dark: redDark.value,
49+
light: redLight.value
50+
},
51+
orangered: {
52+
dark: orangeredLight.value,
53+
light: orangeredDark.value
54+
},
55+
orange: {
56+
dark: orangeDark.value,
57+
light: orangeLight.value
58+
},
59+
yellow: {
60+
dark: yellowDark.value,
61+
light: yellowLight.value
62+
},
63+
green: {
64+
dark: greenDark.value,
65+
light: greenLight.value
66+
},
67+
teal: {
68+
dark: tealDark.value,
69+
light: tealLight.value
70+
},
71+
blue: {
72+
dark: blueDark.value,
73+
light: blueLight.value
74+
},
75+
purple: {
76+
dark: purpleDark.value,
77+
light: purpleLight.value
78+
},
79+
gray: {
80+
dark: grayDark.value,
81+
light: grayLight.value
82+
}
83+
};

0 commit comments

Comments
 (0)