Skip to content

Commit 695bf2e

Browse files
Justin WilkersonJustin Wilkerson
authored andcommitted
Large refactor, updated README to reflect changes.
1 parent c7ff27e commit 695bf2e

File tree

14 files changed

+165
-80
lines changed

14 files changed

+165
-80
lines changed

README.md

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ Easily create TypeScript [Playground Plugins](https://www.typescriptlang.org/v2/
99
1. [Features](#features)
1010
1. [About](#about)
1111
2. [Getting Started](#getting-started)
12-
3. [usePlayground Hook](#useplayground-hook)
12+
3. [usePlugin Hook](#usePlugin-hook)
1313
4. [Styling Your Plugin](#styling-your-plugin)
1414
5. [More about TypeScript Playground Plugins](#more-about-typescript-playground-plugins)
1515

1616
## Features
17-
* Write your TypeScript Playground plugin in React and TypeScript.
18-
* Interact with the Playground using a strongly-typed React hook.
19-
* Create styles with stylesheets or CSS-in-JS with Goober.
17+
✔️ Write your TypeScript Playground plugin in React and TypeScript.
18+
19+
✔️ Interact with the Playground using a strongly-typed React hook.
20+
21+
✔️ Create styles with stylesheets or CSS-in-JS with Goober.
2022

2123
## About
2224

23-
The TypeScript Playground V2 comes packed with lots of new features, including the ability to create plugins. Per the TypeScript docs:
25+
The TypeScript Playground V3 beta comes packed with lots of new features, including the ability to create plugins. Per the TypeScript docs:
2426

2527
> The new TypeScript Playground allows people to hook into the Playground and extend it in ways in which the TypeScript team don't expect.
2628
>
@@ -74,36 +76,71 @@ Now, **refresh the browser**. When the playground reloads, a new tab with your p
7476

7577
<img src="./screenshots/screenshot2.png" style="max-width: 80%;"/>
7678

77-
You can make customizations to your plugin by modifying the `customPlugin` object in `src/index.tsx`. For instance, you can change the `displayName` property to change the tab label text for your plugin. See the `PlaygroundPlugin` interface in `src/types/playground.d.ts` for all of the available options.
79+
You can make customizations to your plugin by modifying the `customPlugin` object in `src/index.tsx`. For instance, you can change the `displayName` property to change the tab label text for your plugin. See the `PlaygroundPlugin` interface in `src/plugin/vendor/playground.d.ts` for all of the available options.
7880

79-
## `usePlayground` Hook
81+
## `usePlugin` Hook
8082

8183
This hooks provides all of the method and properties provided by the Plugin API. It accepts a optional config object and returns an object with these properties:
8284

8385
| Name | Description |
8486
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
85-
| **container** | `HTMLDivElement` <br /><br /> This is the container `div` element that wraps the entire sidebar. The React app is mounted to this element. Any style changes to this element will affect the entire sidebar. |
86-
| **sandbox** | `object` <br /><br /> A DOM library for interacting with TypeScript and JavaScript code, which powers the heart of the TypeScript playground. This object provides several properties and methods to interact with the playground. See all of the available types in `src/types/sandbox.d.ts` and read more about the sandbox at [http://www.typescriptlang.org/v2/dev/sandbox/](http://www.typescriptlang.org/v2/dev/sandbox/). |
87+
| **code** | `string` <br /><br /> The current code in the Monaco editor. This value updates on change to the Monaco editor with optional debouncing. Alias for `sandbox.getText()` |
88+
| **setCode** | `(code: string) => void` <br /><br /> Set the code in the Monaco editor. Alias for `sandbox.setText()` |
89+
| **formatCode** | `() => void` <br /><br /> Format the code in the Monaco editor. Alias for `sandbox.editor.getAction("editor.action.formatDocument").run()` |
90+
| **markers** | `IMarker[]` <br /><br /> Alias for `sandbox.monaco.editor.getModelMarkers({})` that is kept in sync via `sandbox.editor.onDidChangeModelDecorations`. |
91+
| **setDebounce** | `(debounce: boolean) => void` <br /><br /> Optionally debounce the `modelChange` event from the Plugin API. |
92+
| **sandbox** | `object` <br /><br /> A DOM library for interacting with TypeScript and JavaScript code, which powers the heart of the TypeScript playground. This object provides several properties and methods to interact with the playground. See all of the available types in `src/plugin/vendor/sandbox.d.ts` and read more about the sandbox at [http://www.typescriptlang.org/v2/dev/sandbox/](http://www.typescriptlang.org/v2/dev/sandbox/). |
8793
| **modal** | `object` <br /><br /> The model is an object which monaco uses to keep track of text in the editor. You can find the full type definition at `node_modules/monaco-editor/esm/vs/editor/editor.api.d.ts`. |
88-
| **code** | `string` <br /><br /> The current code in the Monaco editor. This value updates on change to the Monaco editor with optional debouncing. Same as `sandbox.getText()` |
94+
| **container** | `HTMLDivElement` <br /><br /> This is the container `div` element that wraps the entire sidebar. The React app is mounted to this element. Any style changes to this element will affect the entire sidebar. |
8995
| **showModal** | `(code: string, subtitle?: string, links?: string[]) => void` <br /><br /> From `window.playground.ui` - This function accepts 3 arguments (code, subtitle, and links) and opens a model with the values you provide. |
9096
| **flashInfo** | `(message: string) => void` <br /><br /> From `window.playground.ui` - This function accepts 1 argument (message) and and flashes a quick message in the center of the screen. |
9197

98+
99+
100+
Here is the [type definition](https://github.com/Microsoft/monaco-editor/blob/master/monaco.d.ts#L875) for `IMarker`:
101+
102+
```typescript
103+
interface IMarker {
104+
owner: string;
105+
resource: Uri;
106+
severity: MarkerSeverity;
107+
code?: string | {
108+
value: string;
109+
link: Uri;
110+
};
111+
message: string;
112+
source?: string;
113+
startLineNumber: number;
114+
startColumn: number;
115+
endLineNumber: number;
116+
endColumn: number;
117+
relatedInformation?: IRelatedInformation[];
118+
tags?: MarkerTag[];
119+
}
120+
```
121+
122+
#### Example `usePlugin` Usage
123+
92124
```tsx
93125
const {
94126
code,
127+
setCode,
128+
formatCode,
129+
markers,
130+
setDebounce,
95131
sandbox,
96132
model,
97133
container,
98134
flashInfo,
99135
showModal
100-
} = usePlayground({ debounce: true });
136+
} = usePlugin();
101137

102138
// Here are some examples of things you can do:
103139

104140
// Set the code in the Monaco editor
105141
useEffect(() => {
106-
sandbox.setText(`const greet = (): string => "Hi👋";`);
142+
setCode(`const greet = (): string => "Hi👋";`);
143+
formatCode();
107144
}, []);
108145

109146
// Listen for changes to the code in the Monaco editor
@@ -131,7 +168,7 @@ npm init typescript-playground-plugin playground-my-plugin
131168

132169
For convenience, this repo contains the `CONTRIBUTING.md` file included in the official plugin template. This document contains useful information about how to work with the plugins.
133170

134-
The `src/types` directory contains all of the TypeScript type definitions for the TypeScript Playground Plugin API. This is the best place to find the various config options, properties, and methods that are available.
171+
The `src/plugin/vendor` directory contains all of the TypeScript type definitions for the TypeScript Playground Plugin API. This is the best place to find the various config options, properties, and methods that are available.
135172

136173
### Need inspiration?
137174

screenshots/screenshot2.png

1.57 KB
Loading

src/App.tsx

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,91 @@
11
import React from "react";
22
import { css } from "goober";
3-
import { usePlayground } from "./playground";
3+
import { usePlugin } from "./plugin";
44
import logo from "./assets/logo.svg";
55
import "./App.css";
66

77
const { useEffect } = React;
88

9+
const exampleCode = {
10+
start: "function echo(arg) {\n return arg;\n}",
11+
end: "function echo<T>(arg:T): T {\n return arg;\n}"
12+
};
13+
914
const App: React.FC = () => {
1015
const {
1116
code,
17+
setCode,
18+
formatCode,
19+
markers,
20+
setDebounce,
1221
sandbox,
1322
model,
1423
container,
1524
flashInfo,
1625
showModal
17-
} = usePlayground({ debounce: true });
26+
} = usePlugin();
27+
28+
setDebounce(true);
1829

1930
useEffect(() => {
20-
sandbox.setText('const greet = (): string => "Hi👋";');
31+
setCode(exampleCode.start);
2132
}, []);
2233

2334
useEffect(() => {
24-
console.log("The editor code changed to: ", code);
35+
console.log(`The editor code changed:`);
36+
console.log(code);
2537
}, [code]);
2638

2739
function handleClear() {
28-
sandbox.setText("");
40+
setCode("");
2941
flashInfo("Cleared!");
3042
}
3143

3244
function handleOpenModal() {
3345
showModal(code, "Here is your code.");
3446
}
3547

48+
function handleFixCode() {
49+
setCode(exampleCode.end);
50+
formatCode();
51+
}
52+
53+
const renderMarkers = markers
54+
.sort((a, b) => (a.startLineNumber >= b.startLineNumber ? 1 : -1))
55+
.map(marker => {
56+
return (
57+
<div
58+
key={marker.code?.toString()}
59+
className={css`
60+
margin-top: 20px;
61+
`}
62+
>
63+
<p className={markerClass}>
64+
Line {marker.startLineNumber}:&nbsp;
65+
{marker.message}
66+
</p>
67+
</div>
68+
);
69+
});
70+
3671
return (
3772
<div className={wrapperClass}>
3873
<header>
3974
<h1>TypeScript Playground Plugin</h1>
4075
<h3>with React</h3>
4176
<img src={logo} className="App-logo" alt="logo" />
4277
</header>
78+
<button className={buttonClass} onClick={handleFixCode}>
79+
Fix the Code
80+
</button>
4381
<button className={buttonClass} onClick={handleOpenModal}>
4482
Open the Modal
4583
</button>
4684
<button className={buttonClass} onClick={handleClear}>
4785
Clear the Editor
4886
</button>
87+
88+
{!!markers.length && renderMarkers}
4989
</div>
5090
);
5191
};
@@ -72,16 +112,24 @@ const wrapperClass = css`
72112
const buttonClass = css`
73113
display: inline-block;
74114
margin: 5px;
75-
border: none;
76-
background: transparent;
115+
padding: 5px;
116+
min-width: 120px;
77117
color: ${colors.blue};
78-
cursor: pointer;
118+
background: transparent;
79119
font-size: 0.9rem;
80-
border-radius: 20px;
120+
border: 1px solid ${colors.blue};
121+
border-radius: 4px;
122+
cursor: pointer;
81123
transition: background-color 0.3s;
82124
&:hover {
83125
background: ${colors.gray};
84126
}
85127
`;
86128

129+
const markerClass = css`
130+
margin: 5px;
131+
padding: 0px;
132+
font-size: 0.9rem;
133+
`;
134+
87135
export default App;

src/index.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,45 @@
11
import React from "react";
22
import ReactDOM from "react-dom";
33
import App from "./App";
4-
import { Provider } from "./playground";
4+
import { Provider } from "./plugin";
55

6-
const customPlugin: import("./types/playground").PlaygroundPlugin = {
7-
id: "react",
8-
displayName: "React",
6+
// Used internally
7+
const ID = "react";
8+
9+
// Sidebar tab label text
10+
const DISPLAY_NAME = "React";
11+
12+
const customPlugin: import("./plugin/vendor/playground").PlaygroundPlugin = {
13+
id: ID,
14+
displayName: DISPLAY_NAME,
915
didMount(sandbox, container) {
16+
// Mount the react app and pass the sandbox and container to the Provider wrapper to set up context.
1017
ReactDOM.render(
1118
<Provider sandbox={sandbox} container={container}>
1219
<App />
1320
</Provider>,
1421
container
1522
);
1623
},
24+
// Dispatch custom events for the modelChanges methods for the plugin context.
1725
modelChanged(_, model) {
18-
const modelChangedEvent = createCustomEvent("modelChanged", model);
19-
window.dispatchEvent(modelChangedEvent);
26+
createCustomEvent("modelChanged", model);
2027
},
2128
modelChangedDebounce(_, model) {
22-
const modelChangedDebounceEvent = createCustomEvent(
23-
"modelChangedDebounce",
24-
model
25-
);
26-
window.dispatchEvent(modelChangedDebounceEvent);
29+
createCustomEvent("modelChangedDebounce", model);
2730
}
28-
// Don't need these
29-
// willUnmount(sandbox, container) {}
30-
// didUnmount(sandbox, container) {}
3131
};
3232

3333
function createCustomEvent(
3434
name: string,
3535
model: import("monaco-editor").editor.ITextModel
3636
) {
37-
return new CustomEvent(name, {
37+
const event = new CustomEvent(name, {
3838
detail: {
3939
model
4040
}
4141
});
42+
window.dispatchEvent(event);
4243
}
4344

4445
export default customPlugin;

src/playground/index.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/playground/usePlayground.tsx

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)