diff --git a/lib/bds/desktop/shell_live.ex b/lib/bds/desktop/shell_live.ex
index c6950b2..1bbf505 100644
--- a/lib/bds/desktop/shell_live.ex
+++ b/lib/bds/desktop/shell_live.ex
@@ -527,51 +527,6 @@ defmodule BDS.Desktop.ShellLive do
{:noreply, apply_shell_command(socket, action)}
end
- def handle_event("menu_editor_select_item", %{"item_id" => item_id}, socket) do
- {:noreply, MenuEditor.select_item(socket, item_id, &reload_shell/2)}
- end
-
- def handle_event("change_menu_editor_entry", %{"menu_editor_entry" => params}, socket) do
- {:noreply, MenuEditor.change_entry(socket, params, &reload_shell/2)}
- end
-
- def handle_event("submit_menu_editor_entry", _params, socket) do
- {:noreply, MenuEditor.submit_entry(socket, &reload_shell/2)}
- end
-
- def handle_event("cancel_menu_editor_entry", _params, socket) do
- {:noreply, MenuEditor.cancel_entry(socket, &reload_shell/2)}
- end
-
- def handle_event("select_menu_editor_page", %{"post_id" => post_id}, socket) do
- {:noreply, MenuEditor.select_page(socket, post_id, &reload_shell/2)}
- end
-
- def handle_event("select_menu_editor_category", %{"name" => name}, socket) do
- {:noreply, MenuEditor.select_category(socket, name, &reload_shell/2)}
- end
-
- def handle_event("menu_editor_toolbar_action", %{"action" => action}, socket) do
- {:noreply, MenuEditor.toolbar_action(socket, action, &reload_shell/2, &append_output_entry/5)}
- end
-
- def handle_event(
- "menu_editor_drop_item",
- %{
- "drag_item_id" => drag_item_id,
- "target_item_id" => target_item_id,
- "position" => position
- },
- socket
- ) do
- {:noreply,
- MenuEditor.drop_item(socket, drag_item_id, target_item_id, position, &reload_shell/2)}
- end
-
- def handle_event("menu_editor_keydown", %{"key" => key}, socket) do
- {:noreply, MenuEditor.handle_keydown(socket, key, &reload_shell/2)}
- end
-
def handle_event("change_script_editor", %{"script_editor" => params}, socket) do
{:noreply, CodeEntityEditor.update_script(socket, params, &reload_shell/2)}
end
@@ -1353,6 +1308,10 @@ defmodule BDS.Desktop.ShellLive do
{:noreply, reload_shell(socket, socket.assigns.workbench)}
end
+ def handle_info({:menu_editor_output, title, message, level}, socket) do
+ {:noreply, append_output_entry(socket, title, message, nil, level)}
+ end
+
@impl true
def render(assigns) do
UILocale.put(assigns.page_language)
@@ -1423,7 +1382,6 @@ defmodule BDS.Desktop.ShellLive do
|> assign(:current_tab, current_tab(workbench))
|> assign_post_editor()
|> assign_media_editor()
- |> assign_menu_editor()
|> assign_code_entity_editor()
|> assign_chat_editor()
|> assign_import_editor()
@@ -1477,10 +1435,6 @@ defmodule BDS.Desktop.ShellLive do
MediaEditor.assign_socket(socket)
end
- defp assign_menu_editor(socket) do
- MenuEditor.assign_socket(socket)
- end
-
defp assign_code_entity_editor(socket) do
CodeEntityEditor.assign_socket(socket)
end
@@ -1658,7 +1612,8 @@ defmodule BDS.Desktop.ShellLive do
end
defp save_current_tab(%{assigns: %{current_tab: %{type: :menu_editor}}} = socket) do
- MenuEditor.toolbar_action(socket, "save", &reload_shell/2, &append_output_entry/5)
+ send_update(MenuEditor, id: "menu-editor", action: :save)
+ socket
end
defp save_current_tab(%{assigns: %{current_tab: %{type: :tags}}} = socket) do
diff --git a/lib/bds/desktop/shell_live/index.html.heex b/lib/bds/desktop/shell_live/index.html.heex
index bdbb5ed..b85f7e5 100644
--- a/lib/bds/desktop/shell_live/index.html.heex
+++ b/lib/bds/desktop/shell_live/index.html.heex
@@ -399,8 +399,12 @@
offline_mode={@offline_mode}
/>
- <% @current_tab.type == :menu_editor and @menu_editor -> %>
-
+ <% @current_tab.type == :menu_editor and @current_project -> %>
+ <.live_component module={MenuEditor} id="menu-editor"
+ project_id={@current_project.id}
+ current_tab={@current_tab}
+ tab_meta={@tab_meta}
+ />
<% @current_tab.type == :tags and @current_project -> %>
<.live_component module={TagsEditor} id="tags-editor" project_id={@current_project.id} current_tab={@current_tab} tab_meta={@tab_meta} />
diff --git a/lib/bds/desktop/shell_live/menu_editor.ex b/lib/bds/desktop/shell_live/menu_editor.ex
index 05dd42a..81bf265 100644
--- a/lib/bds/desktop/shell_live/menu_editor.ex
+++ b/lib/bds/desktop/shell_live/menu_editor.ex
@@ -1,7 +1,7 @@
defmodule BDS.Desktop.ShellLive.MenuEditor do
@moduledoc false
- use Phoenix.Component
+ use Phoenix.LiveComponent
alias BDS.Desktop.ShellData
@@ -15,158 +15,239 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
embed_templates("menu_editor_html/*")
- @spec assign_socket(term()) :: term()
- def assign_socket(socket) do
- case socket.assigns[:current_tab] do
- %{type: :menu_editor, id: tab_id} ->
- state = State.ensure_state(socket.assigns)
- menu_editor = State.build(socket.assigns, state)
-
- socket
- |> assign(:menu_editor_state, state)
- |> assign(:menu_editor, menu_editor)
- |> assign(
- :tab_meta,
- Map.put(socket.assigns.tab_meta, {:menu_editor, tab_id}, %{
- title: translated("menuEditor.tabTitle"),
- subtitle: translated("menuEditor.description")
- })
- )
-
- _other ->
- assign(socket, :menu_editor, nil)
- end
+ @spec update(map(), Phoenix.LiveView.Socket.t()) :: {:ok, Phoenix.LiveView.Socket.t()}
+ @impl true
+ def update(%{action: :save} = assigns, socket) do
+ socket = assign(socket, Map.drop(assigns, [:action]))
+ socket = do_save(socket)
+ {:ok, socket}
end
- @spec select_item(term(), term(), term()) :: term()
- def select_item(socket, item_id, reload) do
- socket
- |> State.update_state(fn state -> %{state | selected_id: item_id} end)
- |> reload.(socket.assigns.workbench)
+ def update(assigns, socket) do
+ socket =
+ socket
+ |> assign(assigns)
+ |> ensure_project_assigns()
+ |> build_data()
+
+ {:ok, socket}
end
- @spec change_entry(term(), term(), term()) :: term()
- def change_entry(socket, params, reload) do
+ @spec render(map()) :: Phoenix.LiveView.Rendered.t()
+ @impl true
+ def render(assigns) do
+ menu_editor(assigns)
+ end
+
+ @spec handle_event(String.t(), map(), Phoenix.LiveView.Socket.t()) ::
+ {:noreply, Phoenix.LiveView.Socket.t()}
+ @impl true
+ def handle_event("menu_editor_select_item", %{"item_id" => item_id}, socket) do
+ socket =
+ socket
+ |> State.update_state(fn state -> %{state | selected_id: item_id} end)
+ |> build_data()
+
+ {:noreply, socket}
+ end
+
+ def handle_event("change_menu_editor_entry", %{"menu_editor_entry" => params}, socket) do
query = Map.get(params, "query", "")
- socket
- |> State.update_state(fn state -> put_in(state, [:draft, :query], query) end)
- |> reload.(socket.assigns.workbench)
+ socket =
+ socket
+ |> State.update_state(fn state -> put_in(state, [:draft, :query], query) end)
+ |> build_data()
+
+ {:noreply, socket}
end
- @spec submit_entry(term(), term()) :: term()
- def submit_entry(socket, reload) do
- case DraftManagement.current_draft(socket.assigns) do
- %{type: :page} ->
- socket
- |> State.update_state(&DraftManagement.finalize_submenu_draft/1)
- |> reload.(socket.assigns.workbench)
+ def handle_event("submit_menu_editor_entry", _params, socket) do
+ socket =
+ case DraftManagement.current_draft(socket.assigns) do
+ %{type: :page} ->
+ socket
+ |> State.update_state(&DraftManagement.finalize_submenu_draft/1)
+ |> build_data()
- %{type: :category} ->
- socket
- |> DraftManagement.confirm_category_draft(&State.update_state/2)
- |> reload.(socket.assigns.workbench)
+ %{type: :category} ->
+ socket
+ |> DraftManagement.confirm_category_draft(&State.update_state/2)
+ |> build_data()
- _other ->
- reload.(socket, socket.assigns.workbench)
- end
+ _other ->
+ socket
+ end
+
+ {:noreply, socket}
end
- @spec cancel_entry(term(), term()) :: term()
- def cancel_entry(socket, reload) do
- socket
- |> State.update_state(&DraftManagement.cancel_draft/1)
- |> reload.(socket.assigns.workbench)
+ def handle_event("cancel_menu_editor_entry", _params, socket) do
+ socket =
+ socket
+ |> State.update_state(&DraftManagement.cancel_draft/1)
+ |> build_data()
+
+ {:noreply, socket}
end
- @spec select_page(term(), term(), term()) :: term()
- def select_page(socket, post_id, reload) do
- case PageCategory.page_post(socket.assigns.projects.active_project_id, post_id) do
- nil ->
- reload.(socket, socket.assigns.workbench)
+ def handle_event("select_menu_editor_page", %{"post_id" => post_id}, socket) do
+ socket =
+ case PageCategory.page_post(socket.assigns.projects.active_project_id, post_id) do
+ nil ->
+ socket
- post ->
- socket
- |> State.update_state(&DraftManagement.assign_page_to_draft(&1, post))
- |> reload.(socket.assigns.workbench)
- end
+ post ->
+ socket
+ |> State.update_state(&DraftManagement.assign_page_to_draft(&1, post))
+ |> build_data()
+ end
+
+ {:noreply, socket}
end
- @spec select_category(term(), term(), term()) :: term()
- def select_category(socket, name, reload) do
+ def handle_event("select_menu_editor_category", %{"name" => name}, socket) do
project_id = socket.assigns.projects.active_project_id
- case Enum.find(PageCategory.category_options(project_id), &(&1.name == name)) do
- nil ->
- reload.(socket, socket.assigns.workbench)
+ socket =
+ case Enum.find(PageCategory.category_options(project_id), &(&1.name == name)) do
+ nil ->
+ socket
- category ->
- socket
- |> State.update_state(&DraftManagement.assign_category_to_draft(&1, category))
- |> reload.(socket.assigns.workbench)
- end
+ category ->
+ socket
+ |> State.update_state(&DraftManagement.assign_category_to_draft(&1, category))
+ |> build_data()
+ end
+
+ {:noreply, socket}
end
- @spec toolbar_action(term(), term(), term(), term()) :: term()
- def toolbar_action(socket, action, reload, append_output) do
- case action do
- "add-entry" ->
- socket
- |> State.update_state(&DraftManagement.start_page_draft/1)
- |> reload.(socket.assigns.workbench)
-
- "add-category-archive" ->
- socket
- |> State.update_state(&DraftManagement.start_category_draft/1)
- |> reload.(socket.assigns.workbench)
-
- "save" ->
- State.save(socket, reload, append_output)
-
- "move-up" ->
- socket
- |> State.update_state(&TreeOps.move_selected(&1, :up))
- |> reload.(socket.assigns.workbench)
-
- "move-down" ->
- socket
- |> State.update_state(&TreeOps.move_selected(&1, :down))
- |> reload.(socket.assigns.workbench)
-
- "indent" ->
- socket
- |> State.update_state(&TreeOps.indent_selected/1)
- |> reload.(socket.assigns.workbench)
-
- "unindent" ->
- socket
- |> State.update_state(&TreeOps.unindent_selected/1)
- |> reload.(socket.assigns.workbench)
-
- "delete" ->
- socket
- |> State.update_state(&TreeOps.delete_selected/1)
- |> reload.(socket.assigns.workbench)
-
- _other ->
- reload.(socket, socket.assigns.workbench)
- end
+ def handle_event("menu_editor_toolbar_action", %{"action" => action}, socket) do
+ socket = handle_toolbar_action(socket, action)
+ {:noreply, socket}
end
- @spec drop_item(term(), term(), term(), term(), term()) :: term()
- def drop_item(socket, drag_item_id, target_item_id, position, reload) do
+ def handle_event(
+ "menu_editor_drop_item",
+ %{
+ "drag_item_id" => drag_item_id,
+ "target_item_id" => target_item_id,
+ "position" => position
+ },
+ socket
+ ) do
+ socket =
+ socket
+ |> State.update_state(&TreeOps.drop_selected(&1, drag_item_id, target_item_id, position))
+ |> build_data()
+
+ {:noreply, socket}
+ end
+
+ def handle_event("menu_editor_keydown", %{"key" => "Escape"}, socket) do
+ socket =
+ socket
+ |> State.update_state(&DraftManagement.cancel_draft/1)
+ |> build_data()
+
+ {:noreply, socket}
+ end
+
+ def handle_event("menu_editor_keydown", _params, socket) do
+ {:noreply, socket}
+ end
+
+ defp handle_toolbar_action(socket, "add-entry") do
socket
- |> State.update_state(&TreeOps.drop_selected(&1, drag_item_id, target_item_id, position))
- |> reload.(socket.assigns.workbench)
+ |> State.update_state(&DraftManagement.start_page_draft/1)
+ |> build_data()
end
- @spec handle_keydown(term(), term(), term()) :: term()
- def handle_keydown(socket, "Escape", reload) do
- cancel_entry(socket, reload)
+ defp handle_toolbar_action(socket, "add-category-archive") do
+ socket
+ |> State.update_state(&DraftManagement.start_category_draft/1)
+ |> build_data()
end
- def handle_keydown(socket, _key, reload) do
- reload.(socket, socket.assigns.workbench)
+ defp handle_toolbar_action(socket, "save") do
+ do_save(socket)
+ end
+
+ defp handle_toolbar_action(socket, "move-up") do
+ socket
+ |> State.update_state(&TreeOps.move_selected(&1, :up))
+ |> build_data()
+ end
+
+ defp handle_toolbar_action(socket, "move-down") do
+ socket
+ |> State.update_state(&TreeOps.move_selected(&1, :down))
+ |> build_data()
+ end
+
+ defp handle_toolbar_action(socket, "indent") do
+ socket
+ |> State.update_state(&TreeOps.indent_selected/1)
+ |> build_data()
+ end
+
+ defp handle_toolbar_action(socket, "unindent") do
+ socket
+ |> State.update_state(&TreeOps.unindent_selected/1)
+ |> build_data()
+ end
+
+ defp handle_toolbar_action(socket, "delete") do
+ socket
+ |> State.update_state(&TreeOps.delete_selected/1)
+ |> build_data()
+ end
+
+ defp handle_toolbar_action(socket, _other), do: socket
+
+ defp do_save(socket) do
+ state = socket.assigns.menu_editor_state
+
+ {:ok, _menu} =
+ BDS.Menu.update_menu(
+ state.project_id,
+ Enum.map(state.items, &TreeOps.persisted_item/1)
+ )
+
+ notify_output(translated("menuEditor.tabTitle"), translated("menuEditor.saved"), "info")
+ socket |> build_data()
+ end
+
+ defp ensure_project_assigns(socket) do
+ project_id = socket.assigns.project_id
+
+ if Map.has_key?(socket.assigns, :projects) do
+ socket
+ else
+ assign(socket, :projects, %{active_project_id: project_id})
+ end
+ end
+
+ defp build_data(socket) do
+ tab_id = socket.assigns.current_tab.id
+ state = State.ensure_state(socket.assigns)
+ menu_editor = State.build(socket.assigns, state)
+
+ tab_meta =
+ Map.put(socket.assigns.tab_meta, {:menu_editor, tab_id}, %{
+ title: translated("menuEditor.tabTitle"),
+ subtitle: translated("menuEditor.description")
+ })
+
+ socket
+ |> assign(:menu_editor_state, state)
+ |> assign(:menu_editor, menu_editor)
+ |> assign(:tab_meta, tab_meta)
+ end
+
+ defp notify_output(title, message, level) do
+ send(self(), {:menu_editor_output, title, message, level})
end
attr(:menu_editor, :map, required: true)
@@ -177,6 +258,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
attr(:items, :list, required: true)
attr(:menu_editor, :map, required: true)
attr(:depth, :integer, required: true)
+ attr(:myself, :any, required: true)
@spec menu_tree_level(term()) :: term()
def menu_tree_level(assigns) do
@@ -199,6 +281,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
data-selected={to_string(selected?)}
phx-click={unless(editing?, do: "menu_editor_select_item")}
phx-value-item_id={unless(editing?, do: item.item_id)}
+ phx-target={@myself}
style={"--menu-editor-depth: #{@depth};"}
>
⋮⋮
@@ -213,6 +296,7 @@ defmodule BDS.Desktop.ShellLive.MenuEditor do
data-testid="menu-editor-entry-form"
phx-change="change_menu_editor_entry"
phx-submit="submit_menu_editor_entry"
+ phx-target={@myself}
>
<% end %>
-