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 19:27:34 +03:00
|
|
|
import { shallowEqual } from "./helpers";
|
|
|
|
import * as urlHash from "./urlHash";
|
|
|
|
import formatMarkdown from "./markdown";
|
2018-04-19 20:26:18 +03:00
|
|
|
import * as util from "./util";
|
|
|
|
import getCodeSample from "./codeSamples";
|
2018-04-12 18:16:16 +03:00
|
|
|
|
|
|
|
import { Sidebar, SidebarCategory } from "./sidebar/components";
|
2018-04-13 00:09:37 +03:00
|
|
|
import SidebarOptions from "./sidebar/SidebarOptions";
|
2018-04-20 17:13:09 +03:00
|
|
|
import Option from "./sidebar/options";
|
2018-04-12 18:16:16 +03:00
|
|
|
import { Checkbox } from "./sidebar/inputs";
|
|
|
|
|
2018-05-15 23:14:50 +03:00
|
|
|
const CATEGORIES_ORDER = [
|
|
|
|
"Global",
|
|
|
|
"Common",
|
|
|
|
"JavaScript",
|
|
|
|
"Markdown",
|
2018-10-13 08:55:38 +03:00
|
|
|
"HTML",
|
2018-05-15 23:14:50 +03:00
|
|
|
"Special"
|
|
|
|
];
|
2018-04-20 17:13:09 +03:00
|
|
|
const ENABLED_OPTIONS = [
|
|
|
|
"parser",
|
|
|
|
"printWidth",
|
|
|
|
"tabWidth",
|
|
|
|
"useTabs",
|
|
|
|
"semi",
|
|
|
|
"singleQuote",
|
|
|
|
"bracketSpacing",
|
2018-11-06 10:38:50 +03:00
|
|
|
"jsxSingleQuote",
|
2018-04-20 17:13:09 +03:00
|
|
|
"jsxBracketSameLine",
|
2019-03-06 12:33:08 +03:00
|
|
|
"quoteProps",
|
2018-04-20 17:13:09 +03:00
|
|
|
"arrowParens",
|
|
|
|
"trailingComma",
|
|
|
|
"proseWrap",
|
2018-10-13 08:55:38 +03:00
|
|
|
"htmlWhitespaceSensitivity",
|
2018-04-20 17:13:09 +03:00
|
|
|
"insertPragma",
|
|
|
|
"requirePragma"
|
|
|
|
];
|
2018-04-12 18:16:16 +03:00
|
|
|
|
2018-04-12 00:22:03 +03:00
|
|
|
class Playground extends React.Component {
|
2018-04-17 22:42:03 +03:00
|
|
|
constructor(props) {
|
2018-04-12 00:22:03 +03:00
|
|
|
super();
|
2018-04-12 19:27:34 +03:00
|
|
|
|
2018-07-01 11:17:25 +03:00
|
|
|
const original = urlHash.read();
|
2018-04-17 22:42:03 +03:00
|
|
|
|
2018-04-20 17:13:09 +03:00
|
|
|
const defaultOptions = util.getDefaults(
|
|
|
|
props.availableOptions,
|
|
|
|
ENABLED_OPTIONS
|
|
|
|
);
|
2018-04-17 22:42:03 +03:00
|
|
|
|
2018-07-01 11:17:25 +03:00
|
|
|
const options = Object.assign(defaultOptions, original.options);
|
|
|
|
const content = original.content || getCodeSample(options.parser);
|
|
|
|
|
|
|
|
this.state = { content, options };
|
2018-04-17 22:42:03 +03:00
|
|
|
|
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, "");
|
2018-04-17 22:42:03 +03:00
|
|
|
this.resetOptions = () => this.setState({ options: defaultOptions });
|
2018-04-20 17:13:09 +03:00
|
|
|
|
2018-04-20 17:50:36 +03:00
|
|
|
this.enabledOptions = orderOptions(props.availableOptions, ENABLED_OPTIONS);
|
2018-04-20 17:13:09 +03:00
|
|
|
this.rangeStartOption = props.availableOptions.find(
|
|
|
|
opt => opt.name === "rangeStart"
|
|
|
|
);
|
|
|
|
this.rangeEndOption = props.availableOptions.find(
|
|
|
|
opt => opt.name === "rangeEnd"
|
|
|
|
);
|
2018-04-12 00:22:03 +03:00
|
|
|
}
|
|
|
|
|
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) {
|
2018-04-19 20:46:29 +03:00
|
|
|
this.setState(state => {
|
2018-04-20 17:13:09 +03:00
|
|
|
const options = Object.assign({}, state.options);
|
2018-07-01 11:17:25 +03:00
|
|
|
|
2018-04-20 17:13:09 +03:00
|
|
|
if (option.type === "int" && isNaN(value)) {
|
|
|
|
delete options[option.name];
|
|
|
|
} else {
|
|
|
|
options[option.name] = value;
|
|
|
|
}
|
2018-07-01 11:17:25 +03:00
|
|
|
|
|
|
|
const content =
|
|
|
|
state.content === "" ||
|
|
|
|
state.content === getCodeSample(state.options.parser)
|
|
|
|
? getCodeSample(options.parser)
|
|
|
|
: state.content;
|
|
|
|
|
|
|
|
return { options, content };
|
2018-04-19 20:46:29 +03:00
|
|
|
});
|
2018-04-12 00:22:03 +03:00
|
|
|
}
|
|
|
|
|
2018-04-17 19:40:55 +03:00
|
|
|
getMarkdown(formatted, reformatted, full) {
|
2018-04-17 22:42:03 +03:00
|
|
|
const { content, options } = this.state;
|
|
|
|
const { availableOptions, version } = this.props;
|
|
|
|
|
2018-04-17 19:40:55 +03:00
|
|
|
return formatMarkdown(
|
2018-07-01 11:17:25 +03:00
|
|
|
content,
|
2018-04-17 19:40:55 +03:00
|
|
|
formatted,
|
|
|
|
reformatted || "",
|
|
|
|
version,
|
|
|
|
window.location.href,
|
|
|
|
options,
|
2018-04-19 20:26:18 +03:00
|
|
|
util.buildCliArgs(availableOptions, options),
|
2018-04-17 19:40:55 +03:00
|
|
|
full
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-04-12 00:22:03 +03:00
|
|
|
render() {
|
2018-04-20 17:50:36 +03:00
|
|
|
const { worker } = this.props;
|
2018-04-17 22:42:03 +03:00
|
|
|
const { content, options } = this.state;
|
2018-04-12 00:22:03 +03:00
|
|
|
|
|
|
|
return (
|
2018-04-17 22:42:03 +03:00
|
|
|
<EditorState>
|
|
|
|
{editorState => (
|
|
|
|
<PrettierFormat
|
2018-04-17 23:29:44 +03:00
|
|
|
worker={worker}
|
2018-07-01 11:17:25 +03:00
|
|
|
code={content}
|
2018-04-17 22:42:03 +03:00
|
|
|
options={options}
|
|
|
|
debugAst={editorState.showAst}
|
|
|
|
debugDoc={editorState.showDoc}
|
2018-04-20 15:54:15 +03:00
|
|
|
reformat={editorState.showSecondFormat}
|
2018-04-17 22:42:03 +03:00
|
|
|
>
|
2018-04-19 20:46:29 +03:00
|
|
|
{({ formatted, debug }) => (
|
2018-04-17 22:42:03 +03:00
|
|
|
<React.Fragment>
|
|
|
|
<div className="editors-container">
|
|
|
|
<Sidebar visible={editorState.showSidebar}>
|
|
|
|
<SidebarOptions
|
|
|
|
categories={CATEGORIES_ORDER}
|
2018-04-20 17:50:36 +03:00
|
|
|
availableOptions={this.enabledOptions}
|
2018-04-17 22:42:03 +03:00
|
|
|
optionValues={options}
|
|
|
|
onOptionValueChange={this.handleOptionValueChange}
|
|
|
|
/>
|
2018-04-20 17:13:09 +03:00
|
|
|
<SidebarCategory title="Range">
|
|
|
|
<label>
|
|
|
|
The selected range will be highlighted in yellow in the
|
|
|
|
input editor
|
|
|
|
</label>
|
|
|
|
<Option
|
|
|
|
option={this.rangeStartOption}
|
|
|
|
value={options.rangeStart}
|
|
|
|
onChange={this.handleOptionValueChange}
|
|
|
|
/>
|
|
|
|
<Option
|
|
|
|
option={this.rangeEndOption}
|
|
|
|
value={options.rangeEnd}
|
|
|
|
overrideMax={content.length}
|
|
|
|
onChange={this.handleOptionValueChange}
|
|
|
|
/>
|
|
|
|
</SidebarCategory>
|
2018-04-17 22:42:03 +03:00
|
|
|
<SidebarCategory title="Debug">
|
|
|
|
<Checkbox
|
|
|
|
label="show AST"
|
|
|
|
checked={editorState.showAst}
|
|
|
|
onChange={editorState.toggleAst}
|
2018-04-17 16:28:35 +03:00
|
|
|
/>
|
2018-04-17 22:42:03 +03:00
|
|
|
<Checkbox
|
|
|
|
label="show doc"
|
|
|
|
checked={editorState.showDoc}
|
|
|
|
onChange={editorState.toggleDoc}
|
|
|
|
/>
|
|
|
|
<Checkbox
|
|
|
|
label="show second format"
|
|
|
|
checked={editorState.showSecondFormat}
|
|
|
|
onChange={editorState.toggleSecondFormat}
|
2018-04-12 21:09:04 +03:00
|
|
|
/>
|
2018-04-17 22:42:03 +03:00
|
|
|
</SidebarCategory>
|
|
|
|
<div className="sub-options">
|
|
|
|
<Button onClick={this.resetOptions}>
|
|
|
|
Reset to defaults
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</Sidebar>
|
|
|
|
<div className="editors">
|
|
|
|
<InputPanel
|
2018-04-19 20:26:18 +03:00
|
|
|
mode={util.getCodemirrorMode(options.parser)}
|
2018-04-20 00:57:11 +03:00
|
|
|
ruler={options.printWidth}
|
2018-04-17 22:42:03 +03:00
|
|
|
value={content}
|
2018-07-01 11:17:25 +03:00
|
|
|
codeSample={getCodeSample(options.parser)}
|
2018-04-20 17:13:09 +03:00
|
|
|
overlayStart={options.rangeStart}
|
|
|
|
overlayEnd={options.rangeEnd}
|
2018-04-17 22:42:03 +03:00
|
|
|
onChange={this.setContent}
|
|
|
|
/>
|
|
|
|
{editorState.showAst ? (
|
2018-04-19 20:46:29 +03:00
|
|
|
<DebugPanel value={debug.ast || ""} />
|
2018-04-17 22:42:03 +03:00
|
|
|
) : null}
|
|
|
|
{editorState.showDoc ? (
|
2018-04-19 20:46:29 +03:00
|
|
|
<DebugPanel value={debug.doc || ""} />
|
2018-04-17 22:42:03 +03:00
|
|
|
) : null}
|
|
|
|
<OutputPanel
|
2018-04-19 20:26:18 +03:00
|
|
|
mode={util.getCodemirrorMode(options.parser)}
|
2018-04-17 22:42:03 +03:00
|
|
|
value={formatted}
|
2018-04-20 00:57:11 +03:00
|
|
|
ruler={options.printWidth}
|
2018-04-17 22:42:03 +03:00
|
|
|
/>
|
|
|
|
{editorState.showSecondFormat ? (
|
2018-04-12 21:09:04 +03:00
|
|
|
<OutputPanel
|
2018-04-19 20:26:18 +03:00
|
|
|
mode={util.getCodemirrorMode(options.parser)}
|
2018-04-19 20:46:29 +03:00
|
|
|
value={getSecondFormat(formatted, debug.reformatted)}
|
2018-04-20 00:57:11 +03:00
|
|
|
ruler={options.printWidth}
|
2018-04-12 21:09:04 +03:00
|
|
|
/>
|
2018-04-17 22:42:03 +03:00
|
|
|
) : null}
|
2018-04-17 16:28:35 +03:00
|
|
|
</div>
|
2018-04-17 22:42:03 +03:00
|
|
|
</div>
|
|
|
|
<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>
|
2019-05-25 15:31:10 +03:00
|
|
|
<ClipboardButton
|
|
|
|
copy={JSON.stringify(
|
|
|
|
// Remove `parser` since people usually paste this
|
|
|
|
// into their .prettierrc and specifying a toplevel
|
|
|
|
// parser there is an anti-pattern. Note:
|
|
|
|
// `JSON.stringify` omits keys whose values are
|
|
|
|
// `undefined`.
|
|
|
|
Object.assign({}, options, { parser: undefined }),
|
|
|
|
null,
|
|
|
|
2
|
|
|
|
)}
|
|
|
|
>
|
2018-10-26 00:41:08 +03:00
|
|
|
Copy config JSON
|
|
|
|
</ClipboardButton>
|
2018-04-17 16:28:35 +03:00
|
|
|
</div>
|
2018-04-17 22:42:03 +03:00
|
|
|
<div className="bottom-bar-buttons bottom-bar-buttons-right">
|
2018-04-17 23:29:44 +03:00
|
|
|
<ClipboardButton copy={window.location.href}>
|
2018-04-17 22:42:03 +03:00
|
|
|
Copy link
|
|
|
|
</ClipboardButton>
|
|
|
|
<ClipboardButton
|
2018-04-19 20:46:29 +03:00
|
|
|
copy={() =>
|
|
|
|
this.getMarkdown(formatted, debug.reformatted)
|
|
|
|
}
|
2018-04-17 22:42:03 +03:00
|
|
|
>
|
|
|
|
Copy markdown
|
|
|
|
</ClipboardButton>
|
|
|
|
<LinkButton
|
|
|
|
href={getReportLink(
|
2018-04-19 20:46:29 +03:00
|
|
|
this.getMarkdown(formatted, debug.reformatted, true)
|
2018-04-17 22:42:03 +03:00
|
|
|
)}
|
|
|
|
target="_blank"
|
|
|
|
rel="noopener"
|
|
|
|
>
|
|
|
|
Report issue
|
|
|
|
</LinkButton>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</React.Fragment>
|
|
|
|
)}
|
|
|
|
</PrettierFormat>
|
|
|
|
)}
|
|
|
|
</EditorState>
|
2018-04-12 00:22:03 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 17:50:36 +03:00
|
|
|
function orderOptions(availableOptions, order) {
|
|
|
|
const optionsByName = {};
|
|
|
|
for (const option of availableOptions) {
|
|
|
|
optionsByName[option.name] = option;
|
|
|
|
}
|
|
|
|
|
|
|
|
return order.map(name => optionsByName[name]);
|
|
|
|
}
|
|
|
|
|
2018-04-17 19:40:55 +03:00
|
|
|
function getReportLink(reportBody) {
|
|
|
|
return `https://github.com/prettier/prettier/issues/new?body=${encodeURIComponent(
|
|
|
|
reportBody
|
|
|
|
)}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSecondFormat(formatted, reformatted) {
|
|
|
|
return formatted === ""
|
|
|
|
? ""
|
|
|
|
: formatted === reformatted
|
2018-11-07 04:12:25 +03:00
|
|
|
? "✓ Second format is unchanged."
|
|
|
|
: reformatted;
|
2018-04-17 19:40:55 +03:00
|
|
|
}
|
|
|
|
|
2018-04-12 00:22:03 +03:00
|
|
|
export default Playground;
|