Powered by AppSignal & Oban Pro

Form With Feedback

notebooks/example.livemd

Form With Feedback

Mix.install([{:kino, "~> 0.6.2"}, {:form_with_feedback, path: "."}])

Installation

FormWithFeedback depends on the Kino project. Use Mix.install/2 to install FormWithFeedback and Kino in the setup section of your livebook.

Mix.install([
  {:kino, "~> 0.6.2"},
  {:form_with_feedback, github: "BrooklinJazz/form_with_feedback"}
])

Usage

true
defmodule InputWithFeedback do
  use Kino.JS
  use Kino.JS.Live

  def new(label, answers) when is_list(answers) do
    Kino.JS.Live.new(__MODULE__, {label, answers})
  end

  @impl true
  def init({label, answers}, ctx) do
    {:ok, assign(ctx, label: label, answers: answers, input: "")}
  end

  @impl true
  def handle_connect(ctx) do
    {:ok, %{input: ctx.assigns.input, answers: ctx.assigns.answers, label: ctx.assigns.label},
     ctx}
  end

  def handle_event("update", %{"input" => input}, ctx) do
    broadcast_event(ctx, "update", %{"input" => input, "correct" => input in ctx.assigns.answers})
    {:noreply, assign(ctx, input: input)}
  end

  asset "main.js" do
    """
    export function init(ctx, payload) {
      ctx.importCSS("main.css")
      ctx.root.innerHTML = `
        <p>${payload.label}</p>
        <input class="input" autocomplete="false" id="input" />
      `;
      
      const input = ctx.root.querySelector("#input");
      
      input.addEventListener("input", (event) => {
        console.log("OH")
        ctx.pushEvent("update", { input: event.target.value });
      });

      ctx.handleEvent("update", ({ correct, input: input_text }) => {
        input.value = input_text
        if (correct) {
          input.classList.add("correct");
        } else {
          input.classList.remove("correct");
        }
      });
    }

    """
  end

  asset "main.css" do
    """
    .input {
      border-radius: 0.5rem;
      border-width: 1px;
      border: solid 1px rgb(225 232 240);
      font-size: .875rem;
      line-height: 1.25rem;
      padding: .5rem .75rem;
      box-sizing: border-box;
      background-color: #F8FAFC;
      width: 215px;
    }

    .input:focus {
      outline: none;
    }
    .correct {
      background-color: lightgreen;
    }
    """
  end
end
InputWithFeedback.new("input", ["hello"]) |> Kino.render()
InputWithFeedback.new("input", ["hello", "bob"])