prettier/website/playground/Playground.js

271 lines
8.3 KiB
JavaScript
Raw Normal View History

2018-04-12 00:22:03 +03:00
import React from "react";
2018-04-12 21:09:04 +03:00
import { Button, ClipboardButton, LinkButton } from "./buttons";
2018-04-12 00:22:03 +03:00
import EditorState from "./EditorState";
import { DebugPanel, InputPanel, OutputPanel } from "./panels";
2018-04-12 04:28:50 +03:00
import PrettierFormat from "./PrettierFormat";
2018-04-12 21:09:04 +03:00
import VersionLink from "./VersionLink";
2018-04-12 19:27:34 +03:00
import { shallowEqual } from "./helpers";
import * as urlHash from "./urlHash";
import formatMarkdown from "./markdown";
2018-04-12 18:16:16 +03:00
import { Sidebar, SidebarCategory } from "./sidebar/components";
import Option from "./sidebar/options";
import { Checkbox } from "./sidebar/inputs";
2018-04-12 00:22:03 +03:00
import WorkerApi from "./WorkerApi";
2018-04-12 18:16:16 +03:00
const CATEGORIES_ORDER = ["Global", "JavaScript", "Markdown", "Special"];
2018-04-12 00:22:03 +03:00
const ENABLED_OPTIONS = [
"parser",
"printWidth",
"tabWidth",
"useTabs",
{ name: "semi", inverted: true },
"singleQuote",
{ name: "bracketSpacing", inverted: true },
"jsxBracketSameLine",
"arrowParens",
"trailingComma",
"proseWrap",
"insertPragma",
"requirePragma"
].map(option => (typeof option === "string" ? { name: option } : option));
class Playground extends React.Component {
constructor() {
super();
2018-04-12 19:27:34 +03:00
this.state = Object.assign(
{
content: "",
loaded: false,
options: null
},
urlHash.read()
);
2018-04-12 00:22:03 +03:00
this.handleOptionValueChange = this.handleOptionValueChange.bind(this);
this.setContent = content => this.setState({ content });
2018-04-12 17:51:49 +03:00
this.clearContent = this.setContent.bind(this, "");
this.resetOptions = () =>
this.setState(state => ({
options: getDefaultOptions(state.availableOptions)
}));
2018-04-12 00:22:03 +03:00
}
componentDidMount() {
this._worker = new WorkerApi("/worker.js");
this._worker
.postMessage({ type: "meta" })
.then(({ supportInfo, version }) => {
const availableOptions = parsePrettierOptions(supportInfo);
2018-04-12 17:51:49 +03:00
const options = Object.assign(
getDefaultOptions(availableOptions),
this.state.options
);
2018-04-12 00:22:03 +03:00
this.setState({ loaded: true, availableOptions, options, version });
});
}
2018-04-12 19:27:34 +03:00
componentDidUpdate(_, prevState) {
const { content, options } = this.state;
if (
!shallowEqual(prevState.options, this.state.options) ||
prevState.content !== content
) {
urlHash.replace({ content, options });
}
}
2018-04-12 00:22:03 +03:00
handleOptionValueChange(option, value) {
this.setState(state => ({
options: Object.assign({}, state.options, { [option.name]: value })
}));
}
render() {
2018-04-12 19:27:34 +03:00
const { availableOptions, content, loaded, options } = this.state;
2018-04-12 00:22:03 +03:00
if (!loaded) return "Loading...";
return (
2018-04-12 21:09:04 +03:00
<React.Fragment>
<VersionLink version={this.state.version} />
<EditorState>
{editorState => (
<div className="playground-container">
<div className="editors-container">
<Sidebar visible={editorState.showSidebar}>
{categorizeOptions(
availableOptions,
(category, categoryOptions) => (
<SidebarCategory key={category} title={category}>
{categoryOptions.map(option => (
<Option
key={option.name}
option={option}
value={options[option.name]}
onChange={this.handleOptionValueChange}
/>
))}
</SidebarCategory>
)
)}
<SidebarCategory title="Debug">
<Checkbox
label="show AST"
checked={editorState.showAst}
onChange={editorState.toggleAst}
2018-04-12 04:28:50 +03:00
/>
2018-04-12 21:09:04 +03:00
<Checkbox
label="show doc"
checked={editorState.showDoc}
onChange={editorState.toggleDoc}
2018-04-12 04:28:50 +03:00
/>
2018-04-12 21:09:04 +03:00
</SidebarCategory>
<div className="sub-options">
<Button onClick={this.resetOptions}>
Reset to defaults
</Button>
2018-04-12 04:28:50 +03:00
</div>
2018-04-12 21:09:04 +03:00
</Sidebar>
<PrettierFormat
worker={this._worker}
code={content}
options={options}
debugAst={editorState.showAst}
debugDoc={editorState.showDoc}
>
{({ formatted, debugAst, debugDoc }) => (
<div className="editors">
<InputPanel
mode={getCodemirrorMode(options.parser)}
rulerColumn={options.printWidth}
value={content}
onChange={this.setContent}
/>
{editorState.showAst ? (
<DebugPanel value={debugAst} />
) : null}
{editorState.showDoc ? (
<DebugPanel value={debugDoc} />
) : null}
<OutputPanel
mode={getCodemirrorMode(options.parser)}
value={formatted}
rulerColumn={options.printWidth}
/>
</div>
)}
</PrettierFormat>
2018-04-12 00:22:03 +03:00
</div>
2018-04-12 21:09:04 +03:00
<div className="bottom-bar">
<div className="bottom-bar-buttons">
<Button onClick={editorState.toggleSidebar}>
{editorState.showSidebar ? "Hide" : "Show"} options
</Button>
<Button onClick={this.clearContent}>Clear</Button>
</div>
<div className="bottom-bar-buttons bottom-bar-buttons-right">
<ClipboardButton clipboardValue={window.location.href}>
Copy link
</ClipboardButton>
<Button>Copy markdown</Button>
<LinkButton href="#" target="_blank" rel="noopener">
Report issue
</LinkButton>
</div>
2018-04-12 00:22:03 +03:00
</div>
</div>
2018-04-12 21:09:04 +03:00
)}
</EditorState>
</React.Fragment>
2018-04-12 00:22:03 +03:00
);
}
}
2018-04-12 21:09:04 +03:00
function createVersionLink(version) {
const link = document.createElement("a");
const match = version.match(/^\d+\.\d+\.\d+-pr.(\d+)$/);
if (match) {
link.href = "https://github.com/prettier/prettier/pull/" + match[1];
link.textContent = `PR #${match[1]}`;
} else {
if (version.match(/\.0$/)) {
link.href =
"https://github.com/prettier/prettier/releases/tag/" + version;
} else {
link.href =
"https://github.com/prettier/prettier/blob/master/CHANGELOG.md#" +
version.replace(/\./g, "");
}
link.textContent = `v${version}`;
}
return link;
}
2018-04-12 18:16:16 +03:00
function categorizeOptions(availableOptions, render) {
const optionsByCategory = availableOptions.reduce((acc, option) => {
let options;
acc[option.category] = options = acc[option.category] || [];
options.push(option);
return acc;
}, {});
return CATEGORIES_ORDER.filter(c => optionsByCategory[c]).map(category =>
render(category, optionsByCategory[category])
);
}
2018-04-12 17:51:49 +03:00
function getDefaultOptions(availableOptions) {
return availableOptions.reduce((acc, option) => {
acc[option.name] = option.default;
return acc;
}, {});
}
2018-04-12 03:01:36 +03:00
function getCodemirrorMode(parser) {
switch (parser) {
case "css":
case "less":
case "scss":
return "css";
case "graphql":
return "graphql";
case "markdown":
return "markdown";
default:
return "jsx";
}
}
2018-04-12 00:22:03 +03:00
function parsePrettierOptions(supportInfo) {
const supportedOptions = supportInfo.options.reduce((acc, option) => {
acc[option.name] = option;
return acc;
}, {});
return ENABLED_OPTIONS.reduce((optionsList, optionConfig) => {
if (!supportedOptions[optionConfig.name]) return optionsList;
const option = Object.assign(
{},
optionConfig,
supportedOptions[optionConfig.name]
);
option.cliName =
"--" +
(option.inverted ? "no-" : "") +
option.name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
optionsList.push(option);
return optionsList;
}, []);
}
export default Playground;