View Source LiveMonacoEditor (LiveMonacoEditor v0.1.2)
Monaco Editor component for Phoenix LiveView.
installation
Installation
Add :live_monaco_editor
dependency:
def deps do
[
{:live_monaco_editor, "~> 0.1"}
]
end
Once installed, change your assets/js/app.js
file to load the code editor hook in the live socket:
import { CodeEditorHook } from "../../deps/live_monaco_editor/priv/static/live_monaco_editor.esm"
let Hooks = {}
Hooks.CodeEditorHook = CodeEditorHook
let liveSocket = new LiveSocket("/live", Socket, { hooks: Hooks, params: { _csrf_token: csrfToken } })
And change your assets/css/app.css
file to load styling:
@import "../../deps/live_monaco_editor/priv/static/live_monaco_editor.min.css";
usage
Usage
A new editor using the default options can be created as:
<LiveMonacoEditor.code_editor value="# My Code Editor" />
Or you can customize it:
<LiveMonacoEditor.code_editor
style="min-height: 250px; width: 100%;"
value={~S{
defmodule Math do
def sum_list([head | tail], accumulator) do
sum_list(tail, head + accumulator)
end
def sum_list([], accumulator) do
accumulator
end
end
IO.puts Math.sum_list([1, 2, 3], 0)
}}
opts={
Map.merge(
LiveMonacoEditor.default_opts(),
%{"language" => "elixir"}
)
}
/>
features
Features
set-editor-options
Set editor options
All monaco editor options are supported by passing a map to opts
, for example to change the initial language and some other visual options:
<LiveMonacoEditor.code_editor
value="<h1>My Code Editor</h1>"
opts={
%{
"language" => "html",
"fontSize" => 10,
"minimap" => %{
"autohide" => true,
"showSlider" => "always"
}
}
}
/>
merge-with-default-options
Merge with default options
The code editor is created with default options to provide a better UX out-of-the-box, which may not suit your needs, but you can keep the defaults and overwrite some options as you wish:
<LiveMonacoEditor.code_editor
opts={
Map.merge(
LiveMonacoEditor.default_opts(),
%{"wordWrap" => "on"}
)
}
/>
fetching-the-editor-value
Fetching the editor value
You can listen to events emitted by the code editor to fetch its current value and send it back to the parent LiveView where the component is used. Firstly, add a event listener:
window.addEventListener("lme:editor_mounted", (ev) => {
const hook = ev.detail.hook
// https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneCodeEditor.html
const editor = ev.detail.editor.standalone_code_editor
// push an event to the parent liveview containing the editor current value
// when the editor loses focus
editor.onDidBlurEditorWidget(() => {
hook.pushEvent("code-editor-lost-focus", { value: editor.getValue() })
})
})
Then you can handle that event on the LiveView to save the editor content or perform any sort of operation you need:
def handle_event("code-editor-lost-focus", %{"value" => value}, socket) do
{:noreply, assign(socket, :source, value)}
end
multiple-editors
Multiple editors
Set an unique id
and path
for each one:
<LiveMonacoEditor.code_editor id="html" path="my_file.html" />
<LiveMonacoEditor.code_editor id="css" path="my_file.css" />
inside-forms-with-phx-change
Inside forms with phx-change
Monaco Editor will create a textarea
element that will get pushed back to the server with the path
value:
<form phx-change="validate">
<LiveMonacoEditor.code_editor path="my_file.html" value="<h1>Title</h1>" />
</form>
Which you can pattern match to either ignore or process the value:
def handle_event(
"validate",
%{
"_target" => ["live_monaco_editor", "my_file.html"],
"live_monaco_editor" => %{"my_file.html" => content}
},
socket
) do
# do something with `content`
# or just ignore the event
{:noreply, socket}
end
Note that only adding new content into the editor will trigger this event. For example hitting "backspace" won't trigger this event.
change-language-and-value
Change language and value
<button phx-click="create-file">my_file.html</button>
def handle_event("create-file", _params, socket) do
{:noreply,
socket
|> LiveMonacoEditor.change_language("html")
|> LiveMonacoEditor.set_value("<h1>New File</h1>")}
end
More operations will be supported in new releases.
styling
Styling
The component does not depend on any CSS framework but its parent container has to be large enough to be visible. The default style can be changed and/or classes can be applied:
<LiveMonacoEditor.code_editor
style="height: 100%; width: 100%; min-height: 1000px; min-width: 600px;"
class="my-2"
/>
status
Status
Early-stage, you can expect incomplete features and breaking changes.
contributing
Contributing
You can use the file dev.exs
which is a self-contained Phoenix application running LiveMonacoEditor. Execute:
mix setup
iex -S mix dev
Visit http://localhost:4002
acknowledgements
Acknowledgements
Jonatan Kłosko for his amazing work with Livebook Editor
Link to this section Summary
Link to this section Functions
@spec change_language(Phoenix.LiveView.Socket.t(), String.t(), keyword()) :: Phoenix.LiveView.Socket.t()
Change the editor's language.
examples
Examples
LiveMonacoEditor.change_language(socket, "markdown", to: "my_file.md")
options
Options
:to
- the editor'spath
name that will get the language changed. Defaults to "file".
See https://microsoft.github.io/monaco-editor/docs.html#functions/editor.setModelLanguage.html for more info.
Renders a monaco editor model.
examples
Examples
Render a simple editor using default options:
<LiveMonacoEditor.code_editor value="# My Code Editor" />
Or merge with custom options:
<LiveMonacoEditor.code_editor
opts={
Map.merge(
LiveMonacoEditor.default_opts(),
%{"wordWrap" => "on"}
)
}
/>
attributes
Attributes
path
(:string
) - file identifier, pass unique names to render multiple editors. Defaults to"file"
.value
(:string
) - initial content. Defaults to""
.opts
(:map
) - options for the monaco editor instanceexample
Example%{ "language" => "markdown", "fontSize" => 12, "wordWrap" => "on" }
See all available options at https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneEditorConstructionOptions.html
Defaults to
%{"automaticLayout" => true, "fontFamily" => "JetBrains Mono, monospace", "fontSize" => 14, "formatOnPaste" => true, "formatOnType" => true, "language" => "markdown", "minimap" => %{"enabled" => false}, "occurrencesHighlight" => false, "renderLineHighlight" => "none", "scrollBeyondLastLine" => false, "suggestSelection" => "first", "tabCompletion" => "on", "tabSize" => 2, "theme" => "default"}
.style
(:string
) - Defaults to"min-height: 100px; width: 100%;"
. Global attributes are accepted.
The default Monaco Editor opts passed to <.code_editor>
@spec set_value(Phoenix.LiveView.Socket.t(), String.t(), keyword()) :: Phoenix.LiveView.Socket.t()
Change the editor's value
(content).
examples
Examples
LiveMonacoEditor.set_value(socket, "Enum.all?([1, 2, 3])", to: "my_script.exs")
options
Options
:to
- the editor'spath
name that will get the value updated. Defaults to "file".
See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneCodeEditor.html#setValue for more info.