diff --git a/assets/svelte/components/PageAstNode.svelte b/assets/svelte/components/PageAstNode.svelte index b74d582f..6d3c493a 100644 --- a/assets/svelte/components/PageAstNode.svelte +++ b/assets/svelte/components/PageAstNode.svelte @@ -1,5 +1,6 @@ - -
-
- - {/if} - - - - - - - - -
- {#if $$slots["value"]} - - - -
- {:else if expanded} - - {#if internalValue} - {#if large} - - - {:else} - - {/if} - {#if $$slots["value"]} -
- {/if} - {:else if astNodes} - {#each astNodes as astNode, idx} - {#if isAstElement(astNode)} - -
highlightAstElement(astNode)} - on:mouseleave={() => unhighlightAstElement()} - class="mt-5" - > -
- <{astNode.tag}> - -
-
- - -
-
- {:else if large} - - {:else} - updateNodeContents(e, idx)} - /> - {/if} - {/each} - {/if} -
- {/if} -
diff --git a/assets/svelte/utils/ast-manipulation.ts b/assets/svelte/utils/ast-manipulation.ts index e08eaf5c..6529f5c1 100644 --- a/assets/svelte/utils/ast-manipulation.ts +++ b/assets/svelte/utils/ast-manipulation.ts @@ -4,10 +4,10 @@ import { get } from "svelte/store" import type { AstNode, Page, PageInfo } from "$lib/types" import { getParentNodeId } from "./ast-helpers" -export function updateNodeContent(node, text) { +export function updateNodeContent(path, node, text) { if (node && isAstElement(node)) { node.content = [text] - updateAst() + updateNode(path, node) } } @@ -18,15 +18,32 @@ export function updateAst() { live.pushEvent("update_page_ast", { id: info.id, ast }) } +export function updateNode(path, node) { + let live = get(liveStore) + let info: PageInfo = get(pageInfo) + live.pushEvent("update_page_node", { id: info.id, node_id: path, node: node }) +} + export function deleteAstNode(astElementId: string) { let ast: AstNode[] = get(pageAst) let astElement = findAstElement(ast, astElementId) let parentId = getParentNodeId(astElementId) - let content = parentId && parentId !== "root" ? findAstElement(ast, parentId)?.content : ast + let content + let parentNode + if (parentId && parentId !== "root") { + parentNode = findAstElement(ast, parentId) + content = parentNode.content + } else { + content = ast + } if (content) { let targetIndex = (content as unknown[]).indexOf(astElement) content.splice(targetIndex, 1) - updateAst() + if (parentNode) { + updateNode(parentId, parentNode) + } else { + updateAst() + } } } diff --git a/lib/beacon/live_admin/live/page_editor_live/edit.ex b/lib/beacon/live_admin/live/page_editor_live/edit.ex index d999113f..4ec4e3be 100644 --- a/lib/beacon/live_admin/live/page_editor_live/edit.ex +++ b/lib/beacon/live_admin/live/page_editor_live/edit.ex @@ -55,6 +55,16 @@ defmodule Beacon.LiveAdmin.PageEditorLive.Edit do {:noreply, socket} end + def handle_event("update_page_node", %{"node_id" => path, "node" => node}, socket) do + send_update(Beacon.LiveAdmin.PageEditorLive.FormComponent, + id: "page-editor-form", + path: path, + node: node + ) + + {:noreply, socket} + end + def handle_event( "render_component_in_page", %{"component_id" => component_id, "page_id" => page_id}, diff --git a/lib/beacon/live_admin/live/page_editor_live/form_component.ex b/lib/beacon/live_admin/live/page_editor_live/form_component.ex index 3b82f590..2a5f4721 100644 --- a/lib/beacon/live_admin/live/page_editor_live/form_component.ex +++ b/lib/beacon/live_admin/live/page_editor_live/form_component.ex @@ -88,7 +88,17 @@ defmodule Beacon.LiveAdmin.PageEditorLive.FormComponent do deleted_attrs = Map.get(payload, :deleted, []) ast = VisualEditor.update_node(socket.assigns.builder_page_ast, path, attrs, deleted_attrs) - # TODO: Don't save immediately. Debounce serializing this to a template + update_template_later(socket, ast) + end + + # changed element from visual editor control + def update(%{path: path, node: node}, %{assigns: %{editor: "visual"}} = socket) do + ast = VisualEditor.replace_node(socket.assigns.builder_page_ast, path, node) + + update_template_later(socket, ast) + end + + defp update_template_later(socket, ast) do template = Beacon.Template.HEEx.HEExDecoder.decode(ast) params = Map.merge(socket.assigns.form.params, %{"template" => template}) changeset = Content.change_page(socket.assigns.site, socket.assigns.page, params) diff --git a/lib/beacon/live_admin/live/page_editor_live/new.ex b/lib/beacon/live_admin/live/page_editor_live/new.ex index 88274b28..3a5a27f8 100644 --- a/lib/beacon/live_admin/live/page_editor_live/new.ex +++ b/lib/beacon/live_admin/live/page_editor_live/new.ex @@ -88,6 +88,16 @@ defmodule Beacon.LiveAdmin.PageEditorLive.New do {:noreply, socket} end + def handle_event("update_page_node", %{"node_id" => path, "node" => node}, socket) do + send_update(Beacon.LiveAdmin.PageEditorLive.FormComponent, + id: "page-editor-form", + path: path, + node: node + ) + + {:noreply, socket} + end + def handle_event("select_element", %{"path" => path}, socket) do ElementSelection.select_element(path, socket) end diff --git a/lib/beacon/live_admin/visual_editor.ex b/lib/beacon/live_admin/visual_editor.ex index 85d58218..19fff351 100644 --- a/lib/beacon/live_admin/visual_editor.ex +++ b/lib/beacon/live_admin/visual_editor.ex @@ -53,6 +53,21 @@ defmodule Beacon.LiveAdmin.VisualEditor do end end + def replace_node(nodes, path, new_node) do + path = resolve_path(path) + replace_node_recursive(nodes, path, new_node) + end + + defp replace_node_recursive(nodes, [index], new_node) do + List.update_at(nodes, index, fn _ -> new_node end) + end + + defp replace_node_recursive(nodes, [index | rest], new_node) do + List.update_at(nodes, index, fn node -> + %{node | "content" => replace_node_recursive(node["content"], rest, new_node)} + end) + end + # FIXME: update "root" node - it will crash if any property is updated on the root node (choose Up one level up to root) def update_node(nodes, path, attrs, deleted_attrs) do path = resolve_path(path)