Skip to content

Playground output result (#420) #704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 55 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@rescript/tools": "^0.5.0",
"codemirror": "^5.54.0",
"docson": "^2.1.0",
"escodegen": "^2.1.0",
"eslint-config-next": "^13.1.1",
"fuse.js": "^6.4.3",
"gentype": "^3.44.0",
Expand Down
160 changes: 103 additions & 57 deletions src/Playground.res
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ open CompilerManagerHook
module Api = RescriptCompilerApi

type layout = Column | Row
type tab = JavaScript | Problems | Settings
type tab = RenderOutput | JavaScript | Problems | Settings | Console
let breakingPoint = 1024

module DropdownSelect = {
Expand Down Expand Up @@ -1054,6 +1054,20 @@ module Settings = {
}

module ControlPanel = {
let codeFromResult = (result: FinalResult.t): string => {
open Api
switch result {
| FinalResult.Comp(comp) =>
switch comp {
| CompilationResult.Success({js_code}) => js_code
| UnexpectedError(_)
| Unknown(_, _)
| Fail(_) => "/* No JS code generated */"
}
| Nothing
| Conv(_) => "/* No JS code generated */"
}
}
module Button = {
@react.component
let make = (~children, ~onClick=?) =>
Expand Down Expand Up @@ -1168,6 +1182,7 @@ module ControlPanel = {
Next.Router.replace(router, url)
url
}

<>
<div className="mr-2">
<Button onClick=onFormatClick> {React.string("Format")} </Button>
Expand All @@ -1193,22 +1208,21 @@ let locMsgToCmError = (~kind: CodeMirror.Error.kind, locMsg: Api.LocMsg.t): Code
}
}

module OutputPanel = {
let codeFromResult = (result: FinalResult.t): string => {
open Api
switch result {
| FinalResult.Comp(comp) =>
switch comp {
| CompilationResult.Success({js_code}) => js_code
| UnexpectedError(_)
| Unknown(_, _)
| Fail(_) => "/* No JS code generated */"
}
| Nothing
| Conv(_) => "/* No JS code generated */"
}
module RenderOutput = {
@react.component
let make = () => {
<div className={""}>
<iframe
width="100%"
id="iframe-eval"
className="relative w-full text-gray-20"
srcDoc=RenderOutputManager.Frame.srcdoc
/>
</div>
}
}

module OutputPanel = {
@react.component
let make = (
~compilerDispatch,
Expand All @@ -1226,23 +1240,46 @@ module OutputPanel = {
*/
let prevState = React.useRef(None)

let (logs, setLogs) = React.useState(_ => None)

React.useEffect(() => {
Webapi.Window.addEventListener("message", e => {
let data = e["data"]
let type_: string = data["type"]

if type_ === "log" {
let args: array<string> = data["args"]

setLogs(
_ =>
logs
->Belt.Option.getWithDefault([])
->Js.Array2.concat([args])
->Some,
)
}
})
None
}, [])

let cmCode = switch prevState.current {
| Some(prev) =>
switch (prev, compilerState) {
| (_, Ready({result: Nothing})) => None
| (Ready(prevReady), Ready(ready)) =>
switch (prevReady.result, ready.result) {
| (_, Comp(Success(_))) => codeFromResult(ready.result)->Some
| (_, Comp(Success(_))) => ControlPanel.codeFromResult(ready.result)->Some
| _ => None
}
| (_, Ready({result: Comp(Success(_)) as result})) => codeFromResult(result)->Some
| (_, Ready({result: Comp(Success(_)) as result})) =>
ControlPanel.codeFromResult(result)->Some
| (Ready({result: Comp(Success(_)) as result}), Compiling(_, _)) =>
codeFromResult(result)->Some
ControlPanel.codeFromResult(result)->Some
| _ => None
}
| None =>
switch compilerState {
| Ready(ready) => codeFromResult(ready.result)->Some
| Ready(ready) => ControlPanel.codeFromResult(ready.result)->Some
| _ => None
}
}
Expand Down Expand Up @@ -1276,6 +1313,24 @@ module OutputPanel = {
{HighlightJs.renderHLJS(~code, ~darkmode=true, ~lang="js", ())}
</pre>

let consolePanel = switch logs {
| Some(logs) =>
let content =
logs
->Js.Array2.map(log =>
<pre>
{log
->Js.Array2.map(item => <span> {`${item} `->React.string} </span>)
->React.array}
</pre>
)
->React.array

<div className="whitespace-pre-wrap p-4 block"> content </div>

| None => React.null
}

let output =
<div className="text-gray-20">
resultPane
Expand Down Expand Up @@ -1322,7 +1377,13 @@ module OutputPanel = {

prevSelected.current = selected

let tabs = [(JavaScript, output), (Problems, errorPane), (Settings, settingsPane)]
let tabs = [
(RenderOutput, <RenderOutput />),
(Console, consolePanel),
(JavaScript, output),
(Problems, errorPane),
(Settings, settingsPane),
]

let body = Belt.Array.mapWithIndex(tabs, (i, (tab, content)) => {
let className = currentTab == tab ? "block h-inherit" : "hidden"
Expand All @@ -1344,68 +1405,51 @@ and the different jsx modes (classic and automatic).
module InitialContent = {
let original = `module Button = {
@react.component
let make = (~count) => {
let make = () => {
let (count, setCount) = React.useState(_ => 0)
let times = switch count {
| 1 => "once"
| 2 => "twice"
| n => n->Belt.Int.toString ++ " times"
}
let text = \`Click me $\{times\}\`

<button> {text->React.string} </button>
<button onClick={_ => setCount(c => c + 1)}> {msg->React.string} </button>
}
}

module App = {
@react.component
let make = () => {
<Button />
}
}
`

let since_10_1 = `@@jsxConfig({ version: 4, mode: "automatic" })
let since_10_1 = `@@jsxConfig({version: 4, mode: "classic"})

module CounterMessage = {
module Button = {
@react.component
let make = (~count, ~username=?) => {
let make = () => {
let (count, setCount) = React.useState(_ => 0)
let times = switch count {
| 1 => "once"
| 2 => "twice"
| n => Belt.Int.toString(n) ++ " times"
| n => n->Int.toString ++ " times"
}
let msg = \`Click me $\{times\}\`

let name = switch username {
| Some("") => "Anonymous"
| Some(name) => name
| None => "Anonymous"
}

<div> {React.string(\`Hello \$\{name\}, you clicked me \` ++ times)} </div>
<button onClick={_ => setCount(c => c + 1)}> {msg->React.string} </button>
}
}

module App = {
@react.component
let make = () => {
let (count, setCount) = React.useState(() => 0)
let (username, setUsername) = React.useState(() => "Anonymous")

<div>
{React.string("Username: ")}
<input
type_="text"
value={username}
onChange={evt => {
evt->ReactEvent.Form.preventDefault
let username = (evt->ReactEvent.Form.target)["value"]
setUsername(_prev => username)
}}
/>
<button
onClick={_evt => {
setCount(prev => prev + 1)
}}>
{React.string("Click me")}
</button>
<button onClick={_evt => setCount(_ => 0)}> {React.string("Reset")} </button>
<CounterMessage count username />
</div>
<Button />
}
}

`
}

Expand Down Expand Up @@ -1700,10 +1744,12 @@ let make = (~versions: array<string>) => {
"flex-1 items-center p-4 border-t-4 border-transparent " ++ activeClass
}

let tabs = [JavaScript, Problems, Settings]
let tabs = [JavaScript, RenderOutput, Console, Problems, Settings]

let headers = Belt.Array.mapWithIndex(tabs, (i, tab) => {
let title = switch tab {
| RenderOutput => "Render Output"
| Console => "Console"
| JavaScript => "JavaScript"
| Problems => "Problems"
| Settings => "Settings"
Expand Down
9 changes: 9 additions & 0 deletions src/bindings/Webapi.res
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module Document = {
@val external document: Dom.element = "document"
@scope("document") @val external createElement: string => Dom.element = "createElement"
@scope("document") @val external createTextNode: string => Dom.element = "createTextNode"
}
Expand All @@ -15,6 +16,14 @@ module Element = {
@get external classList: Dom.element => ClassList.t = "classList"
@send external getBoundingClientRect: Dom.element => {..} = "getBoundingClientRect"

@send
external getElementById: (Dom.element, string) => Js.nullable<Dom.element> = "getElementById"

type contentWindow
@get external contentWindow: Dom.element => option<contentWindow> = "contentWindow"

@send external postMessage: (contentWindow, string, string) => unit = "postMessage"

module Style = {
@scope("style") @set external width: (Dom.element, string) => unit = "width"
@scope("style") @set external height: (Dom.element, string) => unit = "height"
Expand Down
Loading
Loading