From f7c39a880a53331c144a38dc60d259381bece4f4 Mon Sep 17 00:00:00 2001 From: sorax Date: Mon, 2 Sep 2024 20:50:25 +0200 Subject: [PATCH] fix child-node deletion --- assets/js/app.js | 1 + assets/js/hooks/index.ts | 52 +++------ lib/radiator_web/live/outline_component.ex | 83 +++++++++++--- .../live/outline_component.html.heex | 103 +++++++++--------- 4 files changed, 136 insertions(+), 103 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 5f5d7067..ad8d623d 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -34,6 +34,7 @@ let liveSocket = new LiveSocket("/live", Socket, { return { key: e.key, metaKey: e.metaKey, + shiftKey: e.shiftKey, repeat: e.repeat } } diff --git a/assets/js/hooks/index.ts b/assets/js/hooks/index.ts index 09460cb3..0c7212d3 100644 --- a/assets/js/hooks/index.ts +++ b/assets/js/hooks/index.ts @@ -1,32 +1,28 @@ -// import { input } from "./events/listener/input"; -// import { keydown } from "./events/listener"; -// import { -// handleInsert, -// handleContentChange, -// handleDelete, -// handleClean, -// } from "./events/handler"; import { getNodeByItem } from "./node"; import { getItemById, setAttribute } from "./item"; export const Hooks = { outline: { - selector: '.item:not([data-processed="true"]', + selector: '.node:not([data-processed="true"]', mounted() { - // const container: HTMLDivElement = this.el.querySelector(".nodes"); - - // container.addEventListener("input", input.bind(this)); - - // container.addEventListener("keydown", keydown.bind(this)); - // // container.addEventListener("keyup", keyup.bind(this)) + const nodes = this.el.querySelectorAll(this.selector); + nodes.forEach((item: HTMLDivElement) => { + const { parent_id, prev_id } = getNodeByItem(item); + const parentNode = getItemById(parent_id); + const prevNode = getItemById(prev_id); - // this.handleEvent("insert", handleInsert.bind(this)); - // this.handleEvent("change_content", handleContentChange.bind(this)); - // this.handleEvent("delete", handleDelete.bind(this)); - // this.handleEvent("clean", handleClean.bind(this)); + if (prevNode) { + prevNode.after(item); + } else if (parentNode) { + parentNode.querySelector(".children")!.append(item); + } - const items = this.el.querySelectorAll(this.selector); - items.forEach((item: HTMLDivElement) => { + setAttribute(item, "processed", true); + }); + }, + updated() { + const nodes = this.el.querySelectorAll(this.selector); + nodes.forEach((item: HTMLDivElement) => { const { parent_id, prev_id } = getNodeByItem(item); const parentNode = getItemById(parent_id); const prevNode = getItemById(prev_id); @@ -41,18 +37,4 @@ export const Hooks = { }); }, }, - updated() { - // const items = this.el.querySelectorAll(this.selector); - // items.forEach((item: HTMLDivElement) => { - // const { parent_id, prev_id } = getNodeByItem(item); - // const parentNode = getItemById(parent_id); - // const prevNode = getItemById(prev_id); - // if (prevNode) { - // prevNode.after(item); - // } else if (parentNode) { - // parentNode.querySelector(".children")!.append(item); - // } - // setAttribute(item, "processed", true); - // }); - }, }; diff --git a/lib/radiator_web/live/outline_component.ex b/lib/radiator_web/live/outline_component.ex index b09bf6b4..87236bfb 100644 --- a/lib/radiator_web/live/outline_component.ex +++ b/lib/radiator_web/live/outline_component.ex @@ -18,35 +18,43 @@ defmodule RadiatorWeb.OutlineComponent do alias RadiatorWeb.OutlineComponents @impl true - def update(%{event: %NodeInsertedEvent{node: %{uuid: uuid}}} = _event, socket) do - node = NodeRepository.get_node!(uuid) + def update(%{event: %NodeInsertedEvent{node: node, next_id: next_id}}, socket) do + next_node = NodeRepository.get_node!(next_id) socket |> stream_insert(:nodes, to_change_form(node, %{})) + |> stream_insert(:nodes, to_change_form(next_node, %{})) |> reply(:ok) end - def update(%{event: %NodeContentChangedEvent{node_id: uuid}} = _event, socket) do - node = NodeRepository.get_node!(uuid) + def update(%{event: %NodeContentChangedEvent{node_id: node_id, content: content}}, socket) do + node = NodeRepository.get_node!(node_id) socket - |> stream_insert(:nodes, to_change_form(node, %{})) + |> stream_insert(:nodes, to_change_form(node, %{content: content})) |> reply(:ok) end - def update(%{event: %NodeMovedEvent{node_id: uuid}} = _event, socket) do - node = NodeRepository.get_node!(uuid) - + def update( + %{event: %NodeMovedEvent{node_id: _node_id, parent_id: _parent_id, prev_id: _prev_id}}, + socket + ) do socket - |> stream_insert(:nodes, to_change_form(node, %{})) + # |> stream_insert(:nodes, to_change_form(node, %{})) |> reply(:ok) end - def update(%{event: %NodeDeletedEvent{node_id: uuid}}, socket) do - node = %Node{uuid: uuid} + def update( + %{event: %NodeDeletedEvent{node_id: node_id, next_id: next_id, children: children}}, + socket + ) do + next_node = NodeRepository.get_node!(next_id) + children_forms = Enum.map(children, &to_change_form(&1, %{})) socket - |> stream_delete(:nodes, to_change_form(node, %{})) + |> stream_delete_by_dom_id(:nodes, "nodes-form-#{node_id}") + |> stream_insert(:nodes, to_change_form(next_node, %{})) + |> stream(:nodes, children_forms) |> reply(:ok) end @@ -89,19 +97,46 @@ defmodule RadiatorWeb.OutlineComponent do |> reply(:noreply) end + def handle_event( + "keydown", + %{"key" => "Tab", "shiftKey" => false, "uuid" => uuid, "prev" => prev_id}, + socket + ) do + socket + |> indent(uuid, prev_id) + |> reply(:noreply) + end + + def handle_event("keydown", %{"key" => "Tab", "shiftKey" => false}, socket) do + socket + |> reply(:noreply) + end + + def handle_event( + "keydown", + %{"key" => "Tab", "shiftKey" => true, "uuid" => uuid, "parent" => parent_id}, + socket + ) do + socket + |> outdent(uuid, parent_id) + |> reply(:noreply) + end + + def handle_event("keydown", %{"key" => "Tab", "shiftKey" => true}, socket) do + socket + |> reply(:noreply) + end + def handle_event("keydown", _params, socket) do socket |> reply(:noreply) end def handle_event("save", %{"uuid" => uuid, "node" => params}, socket) do - node = NodeRepository.get_node!(uuid) - user_id = socket.assigns.user_id Dispatch.change_node_content(uuid, params["content"], user_id, generate_event_id(socket.id)) socket - |> stream_insert(:nodes, to_change_form(node, params, :validate)) |> reply(:noreply) end @@ -120,12 +155,18 @@ defmodule RadiatorWeb.OutlineComponent do "episode_id" => episode_id } - new_node = %Node{uuid: new_uuid} + new_node = %Node{ + uuid: new_uuid, + parent_id: node.parent_id, + prev_id: node.uuid, + creator_id: user_id, + episode_id: episode_id + } Dispatch.insert_node(params, user_id, generate_event_id(socket.id)) socket - |> stream_insert(:nodes, to_change_form(new_node, params)) + |> stream_insert(:nodes, to_change_form(new_node, %{})) |> reply(:noreply) end @@ -139,4 +180,12 @@ defmodule RadiatorWeb.OutlineComponent do end defp generate_event_id(id), do: Ecto.UUID.generate() <> ":" <> id + + defp indent(socket, _uuid, _prev_id) do + socket + end + + defp outdent(socket, _uuid, _parent_id) do + socket + end end diff --git a/lib/radiator_web/live/outline_component.html.heex b/lib/radiator_web/live/outline_component.html.heex index 26aaaa2b..ed76cbad 100644 --- a/lib/radiator_web/live/outline_component.html.heex +++ b/lib/radiator_web/live/outline_component.html.heex @@ -1,60 +1,61 @@
-
-
-
+
+ - - <.input - type="text" - field={form[:content]} - placeholder="Content..." - phx-focus="focus" - phx-blur="blur" - phx-keydown="keydown" - phx-value-uuid={form.data.uuid} - phx-target={@myself} - /> - -
- - -
+ /> + +
+ +