Skip to content

Commit

Permalink
Add examples for yew showing keymapping and button usage
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuataylor authored and siku2 committed Jan 11, 2023
1 parent cac3ea1 commit f913990
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 1 deletion.
2 changes: 1 addition & 1 deletion examples/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["yew"]
members = ["yew", "yew_events_external", "yew_events_keymapping"]
3 changes: 3 additions & 0 deletions examples/yew_events_external/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target
dist
dist/*
14 changes: 14 additions & 0 deletions examples/yew_events_external/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "yew_events"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
monaco = { path = "../..", features = ["yew-components"] }
wasm-bindgen = "0.2"
yew = { version = "0.20", features = ["csr"] }

# Used for the randomess, not needed usually.
getrandom = { version = "0.2", features = ["js"] }
rand = "0.8"
7 changes: 7 additions & 0 deletions examples/yew_events_external/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Rust Monaco - Yew Key Mapping Events Example

This is an example of how to use [Rust Monaco]() with Yew, and using a button to control the editor.

To use, run `trunk serve`, then access `http://127.0.0.1:8080`.

Press the button to see the editor change.
28 changes: 28 additions & 0 deletions examples/yew_events_external/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title>Yew Monaco</title>

<style>
.full-height {
height: 100%;
}
#code-editor, #event-log-wrapper {
float: left;
width: 50%;
height: 100%;
}
#event-log {
margin-left: 20px;
}
#code-wrapper {
height: 100%;
}
</style>
</head>

<body style="height: 100vh; margin: 0; overflow: hidden;"></body>

</html>
87 changes: 87 additions & 0 deletions examples/yew_events_external/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use monaco::{
api::{CodeEditorOptions, TextModel},
sys::editor::BuiltinTheme,
yew::CodeEditor,
};

use rand::{distributions::Alphanumeric, Rng};
use yew::prelude::*;

const CONTENT: &str = include_str!("main.rs");

fn get_options() -> CodeEditorOptions {
CodeEditorOptions::default()
.with_language("rust".to_owned())
.with_value(CONTENT.to_owned())
.with_builtin_theme(BuiltinTheme::VsDark)
.with_automatic_layout(true)
}

#[derive(PartialEq, Properties)]
pub struct CustomEditorProps {
text_model: TextModel,
}

/// This is really just a helper component, so we can pass in props easier.
/// It makes it much easier to use, as we can pass in what we need, and it
/// will only re-render if the props change.
#[function_component(CustomEditor)]
pub fn custom_editor(props: &CustomEditorProps) -> Html {
let CustomEditorProps { text_model } = props;

html! {
<CodeEditor classes={"full-height"} options={ get_options().to_sys_options() } model={text_model.clone()} />
}
}

// inefficient and silly, but fine for demo :-)
fn random_string() -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(50)
.map(char::from)
.collect()
}

#[function_component(App)]
fn app() -> Html {
// We need to create a new text model, so we can pass it to Monaco.
// We use use_state_eq, as this allows us to only use it when it changes.
let text_model =
use_state_eq(|| TextModel::create(&random_string(), Some("rust"), None).unwrap());

// Here we define what we want to do when we click the button, which is bound to
// the button below. We use the `use_callback` function in Yew to bind the
// function to the button. We can then clone the text model, and use it in
// the callback to set the value to whatever we want.
let on_run_clicked = {
let text_model = text_model.clone();
use_callback(
move |_, text_model| {
let s: String = random_string();
// Here we have full access to the text model. We can do whatever we want with
// it. For this example, we'll just set the value to a random
// string.
text_model.set_value(&s);
},
text_model,
)
};

html! {
<div id="code-wrapper">
<div id="code-editor">
<CustomEditor text_model={(*text_model).clone()} />
</div>
<div id="event-log-wrapper">
<div id="event-log">
<button onclick={on_run_clicked}>{ "Random code" }</button>
</div>
</div>
</div>
}
}

fn main() {
yew::Renderer::<App>::new().render();
}
10 changes: 10 additions & 0 deletions examples/yew_events_keymapping/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "yew_events"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
monaco = { path = "../..", features = ["yew-components"] }
wasm-bindgen = "0.2"
yew = { version = "0.20", features = ["csr"] }
8 changes: 8 additions & 0 deletions examples/yew_events_keymapping/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Rust Monaco - Yew Key Mapping Events Example

This is an example of how to use [Rust Monaco]() with Yew, and using `on_editor_created` to add key mappings.

To use, run `trunk serve`, then access `http://127.0.0.1:8080`.

Type some code, then press Ctrl+Enter (Linux/Windows) or Cmd+Enter (Mac) to have the code reflect on the right hand
side.
28 changes: 28 additions & 0 deletions examples/yew_events_keymapping/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title>Yew Monaco</title>

<style>
.full-height {
height: 100%;
}
#code-editor, #event-log-wrapper {
float: left;
width: 50%;
height: 100%;
}
#event-log {
margin-left: 20px;
}
#code-wrapper {
height: 100%;
}
</style>
</head>

<body style="height: 100vh; margin: 0; overflow: hidden;"></body>

</html>
107 changes: 107 additions & 0 deletions examples/yew_events_keymapping/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use monaco::{
api::{CodeEditorOptions, TextModel},
sys::editor::{BuiltinTheme, IStandaloneCodeEditor},
yew::{CodeEditor, CodeEditorLink},
};
use wasm_bindgen::closure::Closure;
use yew::prelude::*;

use wasm_bindgen::JsCast;

const CONTENT: &str = include_str!("main.rs");

fn get_options() -> CodeEditorOptions {
CodeEditorOptions::default()
.with_language("rust".to_owned())
.with_value(CONTENT.to_owned())
.with_builtin_theme(BuiltinTheme::VsDark)
.with_automatic_layout(true)
}

#[derive(PartialEq, Properties)]
pub struct CustomEditorProps {
on_editor_created: Callback<CodeEditorLink>,
text_model: TextModel,
}

///
/// This is really just a helper component, so we can pass in props easier.
/// It makes it much easier to use, as we can pass in what we need, and it
/// will only re-render if the props change.
///
#[function_component(CustomEditor)]
pub fn custom_editor(props: &CustomEditorProps) -> Html {
let CustomEditorProps {
on_editor_created,
text_model,
} = props;

html! {
<CodeEditor classes={"full-height"} options={ get_options().to_sys_options() } {on_editor_created} model={text_model.clone()} />
}
}

#[function_component(App)]
fn app() -> Html {
// We need to create a new text model, so we can pass it to Monaco.
// We use use_state_eq, as this allows us to only use it when it changes.
let text_model = use_state_eq(|| TextModel::create(CONTENT, Some("rust"), None).unwrap());

// This is the current code output. As it's static from the example, we set it to the content.
let code = use_state_eq(|| String::from(CONTENT));

// Here we setup the Callback for when the editor is created.
let on_editor_created = {
// We need to clone the text_model/code so we can use them.
let text_model = text_model.clone();
let code = code.clone();

// This is a javascript closure, used to pass to Monaco, using wasm-bindgen.
let js_closure = {
let text_model = text_model.clone();

// We update the code state when the Monaco model changes.
// See https://yew.rs/docs/0.20.0/concepts/function-components/pre-defined-hooks
Closure::<dyn Fn()>::new(move || {
code.set(text_model.get_value());
})
};

// Here we define our callback, we use use_callback as we want to re-render when dependencies change.
// See https://yew.rs/docs/concepts/function-components/state#general-view-of-how-to-store-state
use_callback(
move |editor_link: CodeEditorLink, _text_model| {
editor_link.with_editor(|editor| {
// Registers Ctrl/Cmd + Enter hotkey
let keycode = monaco::sys::KeyCode::Enter.to_value()
| (monaco::sys::KeyMod::ctrl_cmd() as u32);
let raw_editor: &IStandaloneCodeEditor = editor.as_ref();

raw_editor.add_command(
keycode.into(),
js_closure.as_ref().unchecked_ref(),
None,
);
});
},
text_model,
)
};
html! {
<div id="code-wrapper">
<div id="code-editor">
<CustomEditor {on_editor_created} text_model={(*text_model).clone()} />
</div>
<div id="event-log-wrapper">
<div id="event-log">
<h2>{"Code (press CTRL+Enter / Command+Enter to view)"}</h2>
<pre>{code.to_string()}</pre>
</div>
</div>
</div>
}
}

fn main() {
yew::Renderer::<App>::new().render();
}

0 comments on commit f913990

Please sign in to comment.