chore: more god module work
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
181
lib/bds/desktop/shell_live/titlebar_menu.ex
Normal file
181
lib/bds/desktop/shell_live/titlebar_menu.ex
Normal file
@@ -0,0 +1,181 @@
|
||||
defmodule BDS.Desktop.ShellLive.TitlebarMenu do
|
||||
@moduledoc false
|
||||
|
||||
use Phoenix.Component
|
||||
alias BDS.Desktop.MenuBar, as: DesktopMenuBar
|
||||
|
||||
@spec groups() :: [map()]
|
||||
def groups do
|
||||
DesktopMenuBar.groups(dev_mode?: Application.get_env(:bds, :dev_routes, false))
|
||||
end
|
||||
|
||||
@spec dropdown_items(map()) :: [map()]
|
||||
def dropdown_items(group) do
|
||||
group.items
|
||||
|> Enum.map_reduce(0, fn item, keyboard_index ->
|
||||
if Map.get(item, :separator, false) do
|
||||
{%{separator: true}, keyboard_index}
|
||||
else
|
||||
{Map.put(item, :keyboard_index, keyboard_index), keyboard_index + 1}
|
||||
end
|
||||
end)
|
||||
|> elem(0)
|
||||
end
|
||||
|
||||
@spec item_active?(map(), map(), non_neg_integer() | nil) :: boolean()
|
||||
def item_active?(group, item, current_index) do
|
||||
cond do
|
||||
is_nil(current_index) ->
|
||||
false
|
||||
|
||||
Map.get(item, :separator, false) ->
|
||||
false
|
||||
|
||||
true ->
|
||||
group.items
|
||||
|> Enum.reject(&Map.get(&1, :separator, false))
|
||||
|> Enum.find_index(&(&1.id == item.id))
|
||||
|> Kernel.==(current_index)
|
||||
end
|
||||
end
|
||||
|
||||
@spec active_group(map()) :: map() | nil
|
||||
def active_group(assigns) do
|
||||
Enum.find(assigns.menu_groups || [], fn group -> Atom.to_string(group.id) == assigns.titlebar_menu_group end)
|
||||
end
|
||||
|
||||
@spec active_items(map()) :: [map()]
|
||||
def active_items(assigns) do
|
||||
assigns
|
||||
|> active_group()
|
||||
|> case do
|
||||
nil -> []
|
||||
group -> Enum.reject(group.items, &Map.get(&1, :separator, false))
|
||||
end
|
||||
end
|
||||
|
||||
@spec open(Phoenix.LiveView.Socket.t(), String.t()) :: Phoenix.LiveView.Socket.t()
|
||||
def open(socket, group) do
|
||||
socket
|
||||
|> assign(:titlebar_menu_group, group)
|
||||
|> assign(:titlebar_menu_item_index, nil)
|
||||
end
|
||||
|
||||
@spec close(Phoenix.LiveView.Socket.t()) :: Phoenix.LiveView.Socket.t()
|
||||
def close(socket) do
|
||||
socket
|
||||
|> assign(:titlebar_menu_group, nil)
|
||||
|> assign(:titlebar_menu_item_index, nil)
|
||||
end
|
||||
|
||||
@spec toggle(Phoenix.LiveView.Socket.t(), String.t()) :: Phoenix.LiveView.Socket.t()
|
||||
def toggle(socket, group) do
|
||||
if socket.assigns.titlebar_menu_group == group do
|
||||
close(socket)
|
||||
else
|
||||
open(socket, group)
|
||||
end
|
||||
end
|
||||
|
||||
@spec hover(Phoenix.LiveView.Socket.t(), String.t()) :: Phoenix.LiveView.Socket.t()
|
||||
def hover(socket, group) do
|
||||
if socket.assigns.titlebar_menu_group do
|
||||
open(socket, group)
|
||||
else
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Handle a keydown event on an open titlebar menu. `invoke_fun` is called
|
||||
with the action id (string) when the user activates an item.
|
||||
"""
|
||||
@spec handle_keydown(Phoenix.LiveView.Socket.t(), String.t(), (Phoenix.LiveView.Socket.t(), String.t() -> Phoenix.LiveView.Socket.t())) ::
|
||||
Phoenix.LiveView.Socket.t()
|
||||
def handle_keydown(socket, key, invoke_fun) do
|
||||
if socket.assigns.titlebar_menu_group do
|
||||
case key do
|
||||
"Escape" -> close(socket)
|
||||
"ArrowRight" -> rotate_group(socket, 1)
|
||||
"ArrowLeft" -> rotate_group(socket, -1)
|
||||
"ArrowDown" -> advance_item_index(socket, 1)
|
||||
"ArrowUp" -> advance_item_index(socket, -1)
|
||||
"Home" -> set_first_item_index(socket)
|
||||
"End" -> set_last_item_index(socket)
|
||||
"Enter" -> invoke_active_item(socket, invoke_fun)
|
||||
" " -> invoke_active_item(socket, invoke_fun)
|
||||
_other -> socket
|
||||
end
|
||||
else
|
||||
socket
|
||||
end
|
||||
end
|
||||
|
||||
defp rotate_group(socket, offset) do
|
||||
groups = socket.assigns.menu_groups || []
|
||||
current_group = socket.assigns.titlebar_menu_group
|
||||
current_index = Enum.find_index(groups, fn group -> Atom.to_string(group.id) == current_group end)
|
||||
|
||||
if is_nil(current_index) or groups == [] do
|
||||
socket
|
||||
else
|
||||
next_index = rem(current_index + offset + length(groups), length(groups))
|
||||
next_group = Enum.at(groups, next_index)
|
||||
open(socket, Atom.to_string(next_group.id))
|
||||
end
|
||||
end
|
||||
|
||||
defp advance_item_index(socket, offset) do
|
||||
items = active_items(socket.assigns)
|
||||
current_index = socket.assigns[:titlebar_menu_item_index]
|
||||
|
||||
cond do
|
||||
items == [] ->
|
||||
socket
|
||||
|
||||
current_index == nil and offset > 0 ->
|
||||
assign(socket, :titlebar_menu_item_index, 0)
|
||||
|
||||
current_index == nil and offset < 0 ->
|
||||
assign(socket, :titlebar_menu_item_index, length(items) - 1)
|
||||
|
||||
true ->
|
||||
next_index = rem(current_index + offset + length(items), length(items))
|
||||
assign(socket, :titlebar_menu_item_index, next_index)
|
||||
end
|
||||
end
|
||||
|
||||
defp set_last_item_index(socket) do
|
||||
items = active_items(socket.assigns)
|
||||
|
||||
if items == [] do
|
||||
socket
|
||||
else
|
||||
assign(socket, :titlebar_menu_item_index, length(items) - 1)
|
||||
end
|
||||
end
|
||||
|
||||
defp set_first_item_index(socket) do
|
||||
items = active_items(socket.assigns)
|
||||
|
||||
if items == [] do
|
||||
socket
|
||||
else
|
||||
assign(socket, :titlebar_menu_item_index, 0)
|
||||
end
|
||||
end
|
||||
|
||||
defp invoke_active_item(socket, invoke_fun) do
|
||||
items = active_items(socket.assigns)
|
||||
|
||||
case Enum.at(items, socket.assigns[:titlebar_menu_item_index]) do
|
||||
%{id: id} ->
|
||||
socket
|
||||
|> close()
|
||||
|> invoke_fun.(Atom.to_string(id))
|
||||
|
||||
_other ->
|
||||
socket
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user