Skip to content

Commit 438f37b

Browse files
committed
feat(CodeEditor): use custom PatternFly monaco theme
1 parent f4c999c commit 438f37b

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: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
import { Popover, PopoverProps } from '@patternfly/react-core/dist/esm/components/Popover';
1414
import { TooltipPosition } from '@patternfly/react-core/dist/esm/components/Tooltip';
1515
import { getResizeObserver } from '@patternfly/react-core/dist/esm/helpers/resizeObserver';
16-
import Editor, { EditorProps, Monaco } from '@monaco-editor/react';
16+
import Editor, { BeforeMount, EditorProps, Monaco } from '@monaco-editor/react';
1717
import type { editor } from 'monaco-editor';
1818
import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon';
1919
import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon';
@@ -23,6 +23,7 @@ import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
2323
import Dropzone, { FileRejection } from 'react-dropzone';
2424
import { CodeEditorContext } from './CodeEditorUtils';
2525
import { CodeEditorControl } from './CodeEditorControl';
26+
import { defineThemes } from './CodeEditorTheme';
2627

2728
export type ChangeHandler = (value: string, event: editor.IModelContentChangedEvent) => void;
2829
export type EditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => void;
@@ -366,6 +367,11 @@ export const CodeEditor = ({
366367
};
367368
}, []);
368369

370+
const editorBeforeMount: BeforeMount = (monaco) => {
371+
defineThemes(monaco.editor);
372+
editorProps?.beforeMount?.(monaco);
373+
};
374+
369375
const editorDidMount: EditorDidMount = (editor, monaco) => {
370376
// eslint-disable-next-line no-bitwise
371377
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Tab, () => wrapperRef.current.focus());
@@ -428,6 +434,7 @@ export const CodeEditor = ({
428434
};
429435

430436
const editorOptions: editor.IStandaloneEditorConstructionOptions = {
437+
fontFamily: 'var(--pf-t--global--font--family--mono)',
431438
scrollBeyondLastLine: height !== 'sizeToFit',
432439
readOnly: isReadOnly,
433440
cursorStyle: 'line',
@@ -571,8 +578,9 @@ export const CodeEditor = ({
571578
onChange={onModelChange}
572579
onMount={editorDidMount}
573580
loading={loading}
574-
theme={isDarkTheme ? 'vs-dark' : 'vs-light'}
581+
theme={isDarkTheme ? 'pf-v6-theme-dark' : 'pf-v6-theme-light'}
575582
{...editorProps}
583+
beforeMount={editorBeforeMount}
576584
/>
577585
</div>
578586
);
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { colours } 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') as monacoEditor.BuiltinTheme,
6+
inherit: true,
7+
colors: {
8+
'editor.background': colours.background[mode],
9+
'editor.foreground': colours.foreground[mode],
10+
'editorLineNumber.activeForeground': colours.foreground[mode],
11+
'editorLineNumber.foreground': colours.secondaryForeground[mode]
12+
},
13+
rules: [
14+
{ token: '', foreground: colours.foreground[mode], background: colours.background[mode] },
15+
{ token: 'invalid', foreground: colours.red[mode] },
16+
{ token: 'emphasis', fontStyle: 'italic' },
17+
{ token: 'strong', fontStyle: 'bold' },
18+
19+
{ token: 'variable', foreground: colours.blue[mode] },
20+
{ token: 'variable.predefined', foreground: colours.teal[mode] },
21+
{ token: 'constant', foreground: colours.orange[mode] },
22+
{ token: 'comment', foreground: colours.gray[mode] },
23+
{ token: 'number', foreground: colours.orange[mode] },
24+
{ token: 'number.hex', foreground: colours.blue[mode] },
25+
{ token: 'regexp', foreground: colours.red[mode] },
26+
{ token: 'annotation', foreground: colours.purple[mode] },
27+
{ token: 'type', foreground: colours.yellow[mode] },
28+
29+
{ token: 'delimiter', foreground: colours.foreground[mode] },
30+
{ token: 'delimiter.html', foreground: colours.gray[mode] },
31+
{ token: 'delimiter.xml', foreground: colours.blue[mode] },
32+
33+
{ token: 'tag', foreground: colours.red[mode] },
34+
{ token: 'tag.id.jade', foreground: colours.teal[mode] },
35+
{ token: 'tag.class.jade', foreground: colours.teal[mode] },
36+
{ token: 'meta.scss', foreground: colours.red[mode] },
37+
{ token: 'metatag', foreground: colours.orange[mode] },
38+
{ token: 'metatag.content.html', foreground: colours.red[mode] },
39+
{ token: 'metatag.html', foreground: colours.gray[mode] },
40+
{ token: 'metatag.xml', foreground: colours.gray[mode] },
41+
{ token: 'metatag.php', fontStyle: 'bold' },
42+
43+
{ token: 'key', foreground: colours.orange[mode] },
44+
{ token: 'string.key.json', foreground: colours.red[mode] },
45+
{ token: 'string.value.json', foreground: colours.blue[mode] },
46+
47+
{ token: 'attribute.name', foreground: colours.red[mode] },
48+
{ token: 'attribute.value', foreground: colours.blue[mode] },
49+
{ token: 'attribute.value.number', foreground: colours.orange[mode] },
50+
{ token: 'attribute.value.unit', foreground: colours.orange[mode] },
51+
{ token: 'attribute.value.html', foreground: colours.blue[mode] },
52+
{ token: 'attribute.value.xml', foreground: colours.blue[mode] },
53+
54+
{ token: 'string', foreground: colours.green[mode] },
55+
{ token: 'string.html', foreground: colours.green[mode] },
56+
{ token: 'string.sql', foreground: colours.green[mode] },
57+
{ token: 'string.yaml', foreground: colours.green[mode] },
58+
59+
{ token: 'keyword', foreground: colours.purple[mode] },
60+
{ token: 'keyword.json', foreground: colours.purple[mode] },
61+
{ token: 'keyword.flow', foreground: colours.purple[mode] },
62+
{ token: 'keyword.flow.scss', foreground: colours.purple[mode] },
63+
64+
{ token: 'operator.scss', foreground: colours.foreground[mode] },
65+
{ token: 'operator.sql', foreground: colours.foreground[mode] },
66+
{ token: 'operator.swift', foreground: colours.foreground[mode] },
67+
{ token: 'predefined.sql', foreground: colours.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+
* Note that base tokens must be used as Monaco will throw a runtime
76+
* error for CSS variables.
77+
*/
78+
export const defineThemes = (editor: typeof monacoEditor) => {
79+
editor.defineTheme('pf-v6-theme-light', createTheme('light'));
80+
editor.defineTheme('pf-v6-theme-dark', createTheme('dark'));
81+
};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
export const colours = {
33+
background: {
34+
dark: backgroundDark.value,
35+
light: backgroundLight.value
36+
},
37+
foreground: {
38+
dark: textColorDark.value,
39+
light: textColorLight.value
40+
},
41+
secondaryForeground: {
42+
dark: textColorSubtleDark.value,
43+
light: textColorSubtleLight.value
44+
},
45+
red: {
46+
dark: redDark.value,
47+
light: redLight.value
48+
},
49+
orangered: {
50+
dark: orangeredLight.value,
51+
light: orangeredDark.value
52+
},
53+
orange: {
54+
dark: orangeDark.value,
55+
light: orangeLight.value
56+
},
57+
yellow: {
58+
dark: yellowDark.value,
59+
light: yellowLight.value
60+
},
61+
green: {
62+
dark: greenDark.value,
63+
light: greenLight.value
64+
},
65+
teal: {
66+
dark: tealDark.value,
67+
light: tealLight.value
68+
},
69+
blue: {
70+
dark: blueDark.value,
71+
light: blueLight.value
72+
},
73+
purple: {
74+
dark: purpleDark.value,
75+
light: purpleLight.value
76+
},
77+
gray: {
78+
dark: grayDark.value,
79+
light: grayLight.value
80+
}
81+
};

0 commit comments

Comments
 (0)