Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 52e539d

Browse files
authored
chore(website): update config editor and add tabs to playground (typescript-eslint#5088)
1 parent 13833b0 commit 52e539d

24 files changed

+827
-577
lines changed

packages/website/src/components/ASTViewerESTree.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useMemo } from 'react';
22

33
import ASTViewer from './ast/ASTViewer';
4-
import type { ASTViewerBaseProps, ASTViewerModelMap } from './ast/types';
4+
import type { ASTViewerBaseProps } from './ast/types';
55
import type { TSESTree } from '@typescript-eslint/utils';
6+
67
import { serialize } from './ast/serializer/serializer';
78
import { createESTreeSerializer } from './ast/serializer/serializerESTree';
89

910
export interface ASTESTreeViewerProps extends ASTViewerBaseProps {
10-
readonly value: TSESTree.BaseNode | string;
11+
readonly value: TSESTree.BaseNode;
1112
}
1213

1314
export default function ASTViewerESTree({
1415
value,
1516
position,
1617
onSelectNode,
1718
}: ASTESTreeViewerProps): JSX.Element {
18-
const [model, setModel] = useState<string | ASTViewerModelMap>('');
19-
20-
useEffect(() => {
21-
if (typeof value === 'string') {
22-
setModel(value);
23-
} else {
24-
const astSerializer = createESTreeSerializer();
25-
setModel(serialize(value, astSerializer));
26-
}
27-
}, [value]);
19+
const model = useMemo(
20+
() => serialize(value, createESTreeSerializer()),
21+
[value],
22+
);
2823

2924
return (
3025
<ASTViewer value={model} position={position} onSelectNode={onSelectNode} />
Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,23 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { useMemo } from 'react';
22

33
import ASTViewer from './ast/ASTViewer';
4-
import type { ASTViewerBaseProps, ASTViewerModelMap } from './ast/types';
4+
import type { ASTViewerBaseProps } from './ast/types';
55

66
import { serialize } from './ast/serializer/serializer';
77
import { createScopeSerializer } from './ast/serializer/serializerScope';
88

99
export interface ASTScopeViewerProps extends ASTViewerBaseProps {
10-
readonly value: Record<string, unknown> | string;
10+
readonly value: Record<string, unknown>;
1111
}
1212

1313
export default function ASTViewerScope({
1414
value,
1515
onSelectNode,
1616
}: ASTScopeViewerProps): JSX.Element {
17-
const [model, setModel] = useState<string | ASTViewerModelMap>('');
18-
19-
useEffect(() => {
20-
if (typeof value === 'string') {
21-
setModel(value);
22-
} else {
23-
const scopeSerializer = createScopeSerializer();
24-
setModel(serialize(value, scopeSerializer));
25-
}
26-
}, [value]);
17+
const model = useMemo(
18+
() => serialize(value, createScopeSerializer()),
19+
[value],
20+
);
2721

2822
return <ASTViewer value={model} onSelectNode={onSelectNode} />;
2923
}

packages/website/src/components/ASTViewerTS.tsx

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { serialize } from './ast/serializer/serializer';
77
import { createTsSerializer } from './ast/serializer/serializerTS';
88

99
export interface ASTTsViewerProps extends ASTViewerBaseProps {
10-
readonly value: SourceFile | string;
10+
readonly value: SourceFile;
1111
}
1212

1313
function extractEnum(
@@ -41,22 +41,18 @@ export default function ASTViewerTS({
4141
const [typeFlags] = useState(() => extractEnum(window.ts.TypeFlags));
4242

4343
useEffect(() => {
44-
if (typeof value === 'string') {
45-
setModel(value);
46-
} else {
47-
const scopeSerializer = createTsSerializer(
48-
value,
49-
syntaxKind,
50-
['NodeFlags', nodeFlags],
51-
['TokenFlags', tokenFlags],
52-
['ModifierFlags', modifierFlags],
53-
['ObjectFlags', objectFlags],
54-
['SymbolFlags', symbolFlags],
55-
['FlowFlags', flowFlags],
56-
['TypeFlags', typeFlags],
57-
);
58-
setModel(serialize(value, scopeSerializer));
59-
}
44+
const scopeSerializer = createTsSerializer(
45+
value,
46+
syntaxKind,
47+
['NodeFlags', nodeFlags],
48+
['TokenFlags', tokenFlags],
49+
['ModifierFlags', modifierFlags],
50+
['ObjectFlags', objectFlags],
51+
['SymbolFlags', symbolFlags],
52+
['FlowFlags', flowFlags],
53+
['TypeFlags', typeFlags],
54+
);
55+
setModel(serialize(value, scopeSerializer));
6056
}, [value, syntaxKind]);
6157

6258
return (
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import type { TabType } from './types';
3+
import styles from './Playground.module.css';
4+
import EditIcon from '@site/src/icons/edit.svg';
5+
6+
export interface FileTabsProps {
7+
readonly tabs: TabType[];
8+
readonly activeTab: TabType;
9+
readonly change: (tab: TabType) => void;
10+
readonly showModal: () => void;
11+
}
12+
13+
export default function EditorTabs({
14+
tabs,
15+
activeTab,
16+
change,
17+
showModal,
18+
}: FileTabsProps): JSX.Element {
19+
return (
20+
<div className={styles.tabContainer}>
21+
<div role="tablist">
22+
{tabs.map(item => {
23+
return (
24+
<button
25+
role="tab"
26+
className={styles.tabStyle}
27+
key={item}
28+
aria-selected={activeTab === item}
29+
disabled={activeTab === item}
30+
onClick={(): void => change(item)}
31+
>
32+
{item}
33+
</button>
34+
);
35+
})}
36+
</div>
37+
{activeTab !== 'code' && (
38+
<button className={styles.tabStyle} onClick={showModal}>
39+
Visual Editor
40+
<EditIcon width={12} height={12} />
41+
</button>
42+
)}
43+
</div>
44+
);
45+
}

packages/website/src/components/ErrorsViewer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function severityClass(severity: Monaco.MarkerSeverity): string {
1919
case 8:
2020
return 'danger';
2121
case 4:
22-
return 'caution';
22+
return 'warning';
2323
case 2:
2424
return 'note';
2525
}

packages/website/src/components/OptionsSelector.tsx

Lines changed: 2 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
/* eslint-disable jsx-a11y/label-has-associated-control */
2-
import React, { useCallback, useState } from 'react';
2+
import React, { useCallback } from 'react';
33

4-
import ConfigEslint from './config/ConfigEslint';
5-
import ConfigTypeScript from './config/ConfigTypeScript';
64
import Expander from './layout/Expander';
75
import Dropdown from './inputs/Dropdown';
86
import Checkbox from './inputs/Checkbox';
97
import Tooltip from './inputs/Tooltip';
10-
import EditIcon from '@site/src/icons/edit.svg';
118
import CopyIcon from '@site/src/icons/copy.svg';
129

1310
import useDebouncedToggle from './hooks/useDebouncedToggle';
1411

1512
import { createMarkdown, createMarkdownParams } from './lib/markdown';
1613

17-
import type { RuleDetails } from './types';
18-
1914
import styles from './OptionsSelector.module.css';
2015

21-
import type { CompilerFlags, ConfigModel, RulesRecord } from './types';
16+
import type { ConfigModel } from './types';
2217

2318
export interface OptionsSelectorParams {
24-
readonly ruleOptions: RuleDetails[];
2519
readonly state: ConfigModel;
2620
readonly setState: (cfg: Partial<ConfigModel>) => void;
2721
readonly tsVersions: readonly string[];
@@ -36,14 +30,11 @@ const ASTOptions = [
3630
] as const;
3731

3832
function OptionsSelector({
39-
ruleOptions,
4033
state,
4134
setState,
4235
tsVersions,
4336
isLoading,
4437
}: OptionsSelectorParams): JSX.Element {
45-
const [eslintModal, setEslintModal] = useState<boolean>(false);
46-
const [typeScriptModal, setTypeScriptModal] = useState<boolean>(false);
4738
const [copyLink, setCopyLink] = useDebouncedToggle<boolean>(false);
4839
const [copyMarkdown, setCopyMarkdown] = useDebouncedToggle<boolean>(false);
4940

@@ -54,26 +45,6 @@ function OptionsSelector({
5445
[setState],
5546
);
5647

57-
const updateRules = useCallback(
58-
(rules?: RulesRecord) => {
59-
if (rules) {
60-
setState({ rules: rules });
61-
}
62-
setEslintModal(false);
63-
},
64-
[setState],
65-
);
66-
67-
const updateTsConfig = useCallback(
68-
(config?: CompilerFlags) => {
69-
if (config) {
70-
setState({ tsConfig: config });
71-
}
72-
setTypeScriptModal(false);
73-
},
74-
[setState],
75-
);
76-
7748
const copyLinkToClipboard = useCallback(() => {
7849
void navigator.clipboard
7950
.writeText(document.location.toString())
@@ -107,19 +78,6 @@ function OptionsSelector({
10778

10879
return (
10980
<>
110-
{state.rules && ruleOptions.length > 0 && (
111-
<ConfigEslint
112-
isOpen={eslintModal}
113-
ruleOptions={ruleOptions}
114-
rules={state.rules}
115-
onClose={updateRules}
116-
/>
117-
)}
118-
<ConfigTypeScript
119-
isOpen={typeScriptModal}
120-
config={state.tsConfig}
121-
onClose={updateTsConfig}
122-
/>
12381
<Expander label="Info">
12482
<label className={styles.optionLabel}>
12583
TypeScript
@@ -170,20 +128,6 @@ function OptionsSelector({
170128
options={['script', 'module']}
171129
/>
172130
</label>
173-
<button
174-
className={styles.optionLabel}
175-
onClick={(): void => setEslintModal(true)}
176-
>
177-
Eslint Config
178-
<EditIcon />
179-
</button>
180-
<button
181-
className={styles.optionLabel}
182-
onClick={(): void => setTypeScriptModal(true)}
183-
>
184-
TypeScript Config
185-
<EditIcon />
186-
</button>
187131
</Expander>
188132
<Expander label="Actions">
189133
<button className={styles.optionLabel} onClick={copyLinkToClipboard}>

packages/website/src/components/Playground.module.css

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
:root {
2+
--playground-main-color: var(--ifm-background-surface-color);
3+
--playground-secondary-color: var(--ifm-color-emphasis-100);
4+
}
5+
16
.options {
27
width: 20rem;
3-
background: var(--ifm-background-surface-color);
8+
background: var(--playground-main-color);
49
overflow: auto;
510
}
611

712
.sourceCode {
813
height: 100%;
914
width: 50%;
10-
border: 1px solid var(--ifm-color-emphasis-100);
15+
border: 1px solid var(--playground-secondary-color);
1116
}
1217

1318
.codeBlocks {
@@ -20,7 +25,7 @@
2025
.astViewer {
2126
height: 100%;
2227
width: 50%;
23-
border: 1px solid var(--ifm-color-emphasis-100);
28+
border: 1px solid var(--playground-secondary-color);
2429
padding: 0;
2530
overflow: auto;
2631
word-wrap: initial;
@@ -37,3 +42,67 @@
3742
top: var(--ifm-navbar-height);
3843
z-index: calc(var(--ifm-z-index-fixed) - 1);
3944
}
45+
46+
.tabContainer {
47+
display: flex;
48+
justify-content: space-between;
49+
background: var(--playground-main-color);
50+
border-bottom: 1px solid var(--playground-secondary-color);
51+
}
52+
53+
.tabCode {
54+
height: calc(100%);
55+
}
56+
57+
.tabStyle {
58+
border: none;
59+
border-right: 1px solid var(--playground-secondary-color);
60+
background: var(--playground-main-color);
61+
color: var(--ifm-color-emphasis-700);
62+
padding: 0.5rem 1rem;
63+
cursor: pointer;
64+
}
65+
66+
.tabStyle svg {
67+
margin-left: 0.3rem;
68+
}
69+
70+
.tabStyle:hover {
71+
background: var(--playground-secondary-color);
72+
}
73+
74+
.tabStyle:disabled {
75+
background: var(--playground-secondary-color);
76+
color: var(--ifm-color-emphasis-900);
77+
}
78+
79+
@media only screen and (max-width: 996px) {
80+
.codeContainer {
81+
display: block;
82+
width: 100%;
83+
position: static;
84+
}
85+
.codeBlocks {
86+
display: block;
87+
width: 100%;
88+
}
89+
90+
.options {
91+
width: 100%;
92+
}
93+
94+
.tabCode {
95+
height: calc(30rem - 3.2rem);
96+
}
97+
98+
.astViewer,
99+
.sourceCode {
100+
height: 30rem;
101+
width: 100%;
102+
}
103+
104+
.astViewer {
105+
height: auto;
106+
max-height: 30rem;
107+
}
108+
}

0 commit comments

Comments
 (0)