Opções para contruir seu próprio componente de UI
Mix.install([
{:kino, "~> 0.18.0"},
{:jason, "~> 1.4.0"}
])
O módulo Kino.HTML
Kino.HTML.new("""
Olha!
Eu escrevi esse HTML com Kino!
""")
Kino.HTML.new("""
#button {
width: 5em;
transition: width 0.5s ease;
font-size: 1em;
}
Clique
const button = document.querySelector("#button");
button.addEventListener("click", (event) => {
button.textContent = "Clicado!";
button.style.width = "18em";
});
""")
Kino.HTML + CSS
defmodule KinoSpinner do
def new(dimensions \\ "30px") do
Kino.HTML.new("""
.loader {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: #{dimensions};
height: #{dimensions};
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
""")
end
end
KinoSpinner.new()
import Kino.Shorts
Podemos usar esse KinoSpinner com um formulário:
form =
Kino.Control.form(
[
name: Kino.Input.text("Dados", default: "dados para processar")
],
submit: "Submit"
)
output_frame = frame()
Kino.listen(form, fn _event ->
Kino.Frame.render(output_frame, grid([text("Processando..."), KinoSpinner.new()]))
Process.sleep(2_000)
Kino.Frame.render(output_frame, "Processamento finalizado. ✅")
end)
grid([form, output_frame])
Kino.HTML + CSS + Javascript
Kino.Text.new("Algum texto")
defmodule KinoTextWithClipboard do
def new(text) do
Kino.HTML.new("""
.container {
box-sizing: border-box;
position: relative;
width: 100%;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;
padding: 10px;
display: flex;
align-items: flex-start;
gap: 10px;
}
.text-content {
flex: 1;
word-wrap: break-word;
}
.clipboard-icon {
cursor: pointer;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 4px;
padding: 5px;
flex-shrink: 0;
}
.clipboard-icon:hover {
background-color: #f0f0f0;
}
.copy-feedback {
position: absolute;
right: 40px;
top: 13px;
background-color: #4CAF50;
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
#{text}
Copied
function copyToClipboard() {
const textContent = document.getElementById('textContent');
const textToCopy = textContent.innerText;
const tempTextArea = document.createElement('textarea');
tempTextArea.value = textToCopy;
document.body.appendChild(tempTextArea);
tempTextArea.select();
document.execCommand('copy');
document.body.removeChild(tempTextArea);
const icon = document.querySelector('.clipboard-icon');
const feedback = document.getElementById('copyFeedback');
icon.style.backgroundColor = '#4CAF50';
feedback.style.opacity = '1';
setTimeout(() => {
icon.style.backgroundColor = '';
feedback.style.opacity = '0';
}, 2000);
}
""")
end
end
KinoTextWithClipboard.new("Algum texto")
Custom Kino
input = Kino.Input.textarea("Json", default: """
{
"name": "Hugo Baraúna",
"age": 18,
"company": "Dashbit / Livebook"
}
""")
defmodule KinoJsonInput do
use Kino.JS
use Kino.JS.Live
def new(json) do
Kino.JS.Live.new(__MODULE__, json)
end
def read(kino) do
Kino.JS.Live.call(kino, :read)
end
@impl true
def init(json, ctx) do
{:ok, assign(ctx, json: json)}
end
@impl true
def handle_connect(ctx) do
{:ok, ctx.assigns.json, ctx}
end
@impl true
def handle_event("update_json", json, ctx) do
{:noreply, assign(ctx, json: json)}
end
@impl true
def handle_call(:read, _from, ctx) do
{:reply, ctx.assigns.json, ctx}
end
asset "main.js" do
"""
export async function init(ctx, json) {
await ctx.importCSS("https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css");
await ctx.importJS("https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js");
await ctx.importJS("https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js");
await ctx.importCSS("https://cdn.jsdelivr.net/gh/WebCoder49/code-input@2.2/code-input.min.css");
await ctx.importJS("https://cdn.jsdelivr.net/gh/WebCoder49/code-input@2.2/code-input.min.js");
await ctx.importJS("https://cdn.jsdelivr.net/gh/WebCoder49/code-input@2.2.1/plugins/indent.min.js");
// This is needed because the CodeInput lib is activated
// on window.load (https://github.com/WebCoder49/code-input/blob/v2.2.1/code-input.js#L983-L985)
// but by the time this JS code is executed, the original load event has already been fired.
window.dispatchEvent(new Event("load"));
codeInput.registerTemplate("syntax-highlighted", codeInput.templates.prism(Prism, [new codeInput.plugins.Indent()]));
ctx.root.innerHTML = `
${json}
`;
const codeInputEl = document.getElementById("input-json");
codeInputEl.addEventListener("change", (event) => {
ctx.pushEvent("update_json", event.target.value);
});
}
"""
end
end
kino_json_input =
KinoJsonInput.new("""
{
"name": "Hugo Baraúna",
"age": 18,
"company": "Dashbit / Livebook"
}
""")
kino_json_input
|> KinoJsonInput.read()
|> Jason.decode!()