fix: more alignment
This commit is contained in:
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -2,6 +2,8 @@
|
||||
"chat.tools.terminal.autoApprove": {
|
||||
"mix": true,
|
||||
"allium": true,
|
||||
"command": true
|
||||
"command": true,
|
||||
"printf": true,
|
||||
"git ls-tree": true
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ defmodule BDS.Desktop.ShellLive do
|
||||
import Phoenix.HTML
|
||||
|
||||
alias BDS.Desktop.{FolderPicker, ShellCommands, ShellData}
|
||||
alias BDS.Desktop.MenuBar, as: DesktopMenuBar
|
||||
alias BDS.Git
|
||||
alias BDS.Media.Media
|
||||
alias BDS.PostLinks
|
||||
@@ -46,6 +47,9 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> assign(:page_language, ShellData.ui_language())
|
||||
|> assign(:client_shortcuts, Commands.client_shortcuts())
|
||||
|> assign(:offline_mode, true)
|
||||
|> assign(:is_mac_ui, mac_ui?())
|
||||
|> assign(:menu_groups, titlebar_menu_groups())
|
||||
|> assign(:titlebar_menu_group, nil)
|
||||
|> assign(:tab_meta, %{})
|
||||
|> assign(:project_menu_open, false)
|
||||
|> assign(:sidebar_filters_by_view, %{})
|
||||
@@ -337,6 +341,33 @@ defmodule BDS.Desktop.ShellLive do
|
||||
{:noreply, handle_native_menu_action(socket, action)}
|
||||
end
|
||||
|
||||
def handle_event("toggle_titlebar_menu", %{"group" => group}, socket) do
|
||||
next_group = if socket.assigns.titlebar_menu_group == group, do: nil, else: group
|
||||
{:noreply, assign(socket, :titlebar_menu_group, next_group)}
|
||||
end
|
||||
|
||||
def handle_event("hover_titlebar_menu", %{"group" => group}, socket) do
|
||||
socket =
|
||||
if socket.assigns.titlebar_menu_group do
|
||||
assign(socket, :titlebar_menu_group, group)
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("close_titlebar_menu", _params, socket) do
|
||||
{:noreply, assign(socket, :titlebar_menu_group, nil)}
|
||||
end
|
||||
|
||||
def handle_event("titlebar_menu_action", %{"action" => action}, socket) do
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:titlebar_menu_group, nil)
|
||||
|> handle_native_menu_action(action)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:refresh_task_status, socket) do
|
||||
task_status = BDS.Tasks.status_snapshot()
|
||||
@@ -395,6 +426,7 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> assign(:activity_buttons, activity_buttons)
|
||||
|> assign(:panel_tabs, ShellData.panel_tabs(workbench))
|
||||
|> assign(:supported_ui_languages, ShellData.supported_ui_languages())
|
||||
|> assign(:menu_groups, socket.assigns[:menu_groups] || titlebar_menu_groups())
|
||||
|> assign(:current_tab, current_tab(workbench))
|
||||
end
|
||||
|
||||
@@ -1349,6 +1381,21 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|> Enum.map_join(" ", &String.capitalize/1)
|
||||
end
|
||||
|
||||
defp titlebar_menu_groups do
|
||||
DesktopMenuBar.groups(dev_mode?: Application.get_env(:bds, :dev_routes, false))
|
||||
end
|
||||
|
||||
defp active_titlebar_menu_group(assigns) do
|
||||
Enum.find(assigns.menu_groups || [], fn group -> Atom.to_string(group.id) == assigns.titlebar_menu_group end)
|
||||
end
|
||||
|
||||
defp mac_ui? do
|
||||
case Application.get_env(:bds, :shell_platform) do
|
||||
nil -> match?({:unix, :darwin}, :os.type())
|
||||
platform -> match?({:unix, :darwin}, platform)
|
||||
end
|
||||
end
|
||||
|
||||
defp post_link_entries(assigns) do
|
||||
case assigns.current_tab do
|
||||
%{type: :post, id: post_id} ->
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
<div class="app" id="bds-shell-app" phx-hook="AppShell" data-shortcuts={encoded_shortcuts(@client_shortcuts)}>
|
||||
<div class="window-titlebar" data-region="title-bar">
|
||||
<div class="window-titlebar-menu-bar is-hidden">
|
||||
<button class="window-titlebar-menu-button" type="button">File</button>
|
||||
<button class="window-titlebar-menu-button" type="button">Edit</button>
|
||||
<button class="window-titlebar-menu-button" type="button">View</button>
|
||||
<button class="window-titlebar-menu-button" type="button">Blog</button>
|
||||
<button class="window-titlebar-menu-button" type="button">Help</button>
|
||||
<div
|
||||
class={["window-titlebar", if(@is_mac_ui, do: "is-mac")]}
|
||||
data-region="title-bar"
|
||||
data-testid="window-titlebar"
|
||||
data-open-menu-group={@titlebar_menu_group || ""}
|
||||
>
|
||||
<%= unless @is_mac_ui do %>
|
||||
<div class="window-titlebar-menu-bar" data-testid="window-titlebar-menu-bar">
|
||||
<%= for group <- @menu_groups do %>
|
||||
<button
|
||||
class={[
|
||||
"window-titlebar-menu-button",
|
||||
if(@titlebar_menu_group == Atom.to_string(group.id), do: "is-active")
|
||||
]}
|
||||
data-testid="window-titlebar-menu-button"
|
||||
data-menu-group={group.id}
|
||||
type="button"
|
||||
phx-click="toggle_titlebar_menu"
|
||||
phx-mouseenter="hover_titlebar_menu"
|
||||
phx-value-group={group.id}
|
||||
aria-label={group.label}
|
||||
><%= group.label %></button>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="window-titlebar-drag-region"></div>
|
||||
<div class="window-titlebar-title" data-testid="window-title"><%= @page_title %></div>
|
||||
<div class="window-titlebar-actions">
|
||||
@@ -47,6 +64,36 @@
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<%= if not @is_mac_ui do %>
|
||||
<%= if group = active_titlebar_menu_group(assigns) do %>
|
||||
<div
|
||||
class="window-titlebar-menu-dropdown"
|
||||
data-testid="window-titlebar-menu-dropdown"
|
||||
phx-click-away="close_titlebar_menu"
|
||||
>
|
||||
<%= for item <- group.items do %>
|
||||
<%= if item.separator do %>
|
||||
<div class="window-titlebar-menu-separator"></div>
|
||||
<% else %>
|
||||
<button
|
||||
class="window-titlebar-menu-item"
|
||||
data-testid="window-titlebar-menu-item"
|
||||
data-menu-action={item.id}
|
||||
type="button"
|
||||
phx-click="titlebar_menu_action"
|
||||
phx-value-action={item.id}
|
||||
aria-label={item.label}
|
||||
>
|
||||
<span class="window-titlebar-menu-item-label"><%= item.label %></span>
|
||||
<%= if item.shortcut do %>
|
||||
<span class="window-titlebar-menu-item-accelerator"><%= item.shortcut %></span>
|
||||
<% end %>
|
||||
</button>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="app-main">
|
||||
|
||||
@@ -146,6 +146,10 @@ button {
|
||||
background-color: var(--vscode-toolbar-hoverBackground);
|
||||
}
|
||||
|
||||
.window-titlebar-menu-button.is-active {
|
||||
background-color: var(--vscode-toolbar-hoverBackground);
|
||||
}
|
||||
|
||||
.window-titlebar-menu-button:focus,
|
||||
.window-titlebar-menu-button:focus-visible,
|
||||
.window-titlebar-action-button:focus,
|
||||
@@ -154,6 +158,61 @@ button {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.window-titlebar-menu-dropdown {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 6px;
|
||||
min-width: 210px;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
background-color: var(--vscode-menu-background, var(--vscode-editorWidget-background));
|
||||
border: 1px solid var(--vscode-menu-border, var(--vscode-panel-border));
|
||||
border-radius: 6px;
|
||||
box-shadow: var(--vscode-widget-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));
|
||||
app-region: no-drag;
|
||||
-webkit-app-region: no-drag;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.window-titlebar-menu-item {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--vscode-menu-foreground, var(--vscode-foreground));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
text-align: left;
|
||||
border-radius: 4px;
|
||||
padding: 6px 8px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.window-titlebar-menu-item:focus,
|
||||
.window-titlebar-menu-item:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
background-color: var(--vscode-toolbar-hoverBackground);
|
||||
}
|
||||
|
||||
.window-titlebar-menu-item:hover,
|
||||
.window-titlebar-menu-item.is-keyboard-active {
|
||||
background-color: var(--vscode-menu-selectionBackground, var(--vscode-toolbar-hoverBackground));
|
||||
}
|
||||
|
||||
.window-titlebar-menu-item-accelerator {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.window-titlebar-menu-separator {
|
||||
height: 1px;
|
||||
margin: 4px 2px;
|
||||
background-color: var(--vscode-menu-separatorBackground, rgba(255, 255, 255, 0.08));
|
||||
}
|
||||
|
||||
.window-titlebar-drag-region {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
|
||||
@@ -183,6 +183,27 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
};
|
||||
|
||||
this.menuIsOpen = () => {
|
||||
const titlebar = this.el.querySelector("[data-testid='window-titlebar']");
|
||||
return Boolean(titlebar?.dataset.openMenuGroup);
|
||||
};
|
||||
|
||||
this.handleTitlebarPointerDown = (event) => {
|
||||
if (!this.menuIsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target.closest("[data-testid='window-titlebar-menu-button']")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target.closest("[data-testid='window-titlebar-menu-dropdown']")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pushEvent("close_titlebar_menu", {});
|
||||
};
|
||||
|
||||
this.handleChange = (event) => {
|
||||
const select = event.target.closest(".status-bar-language-select");
|
||||
|
||||
@@ -216,8 +237,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
});
|
||||
};
|
||||
|
||||
this.handleEscapeKey = (event) => {
|
||||
if (event.key === "Escape" && this.menuIsOpen()) {
|
||||
this.pushEvent("close_titlebar_menu", {});
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
||||
window.addEventListener("keydown", this.handleShortcutKeyDown, true);
|
||||
window.addEventListener("keydown", this.handleEscapeKey, true);
|
||||
window.addEventListener("pointerdown", this.handleTitlebarPointerDown, true);
|
||||
this.el.addEventListener("change", this.handleChange);
|
||||
},
|
||||
|
||||
@@ -226,6 +255,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
this.el.removeEventListener("change", this.handleChange);
|
||||
window.removeEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
||||
window.removeEventListener("keydown", this.handleShortcutKeyDown, true);
|
||||
window.removeEventListener("keydown", this.handleEscapeKey, true);
|
||||
window.removeEventListener("pointerdown", this.handleTitlebarPointerDown, true);
|
||||
if (this.destroyOverlaySync) {
|
||||
this.destroyOverlaySync();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,16 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
{:ok, project} = Projects.create_project(%{name: "Shell Project", data_path: temp_dir})
|
||||
{:ok, _project} = Projects.set_active_project(project.id)
|
||||
|
||||
original_shell_platform = Application.get_env(:bds, :shell_platform)
|
||||
|
||||
on_exit(fn ->
|
||||
if is_nil(original_shell_platform) do
|
||||
Application.delete_env(:bds, :shell_platform)
|
||||
else
|
||||
Application.put_env(:bds, :shell_platform, original_shell_platform)
|
||||
end
|
||||
end)
|
||||
|
||||
%{project: project, temp_dir: temp_dir}
|
||||
end
|
||||
|
||||
@@ -95,6 +105,59 @@ defmodule BDS.Desktop.ShellLiveTest do
|
||||
assert html =~ ~s(class="tab-bar-empty")
|
||||
end
|
||||
|
||||
test "titlebar menu stays hidden on macos because the native menu owns it" do
|
||||
{:ok, view, html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||
|
||||
assert html =~ ~s(class="window-titlebar is-mac")
|
||||
refute html =~ ~s(data-testid="window-titlebar-menu-bar")
|
||||
refute html =~ ~s(data-testid="window-titlebar-menu-button")
|
||||
refute html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
||||
|
||||
html =
|
||||
render_hook(view, "native_menu_action", %{"action" => "edit_preferences"})
|
||||
|
||||
assert html =~ ~s(data-tab-type="settings")
|
||||
assert html =~ ">Settings<"
|
||||
end
|
||||
|
||||
test "titlebar menu matches the old shell contract on windows and linux" do
|
||||
Application.put_env(:bds, :shell_platform, {:unix, :linux})
|
||||
|
||||
{:ok, view, html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||
|
||||
refute html =~ ~s(class="window-titlebar is-mac")
|
||||
assert html =~ ~s(data-testid="window-titlebar-menu-bar")
|
||||
assert html =~ ~s(data-testid="window-titlebar-menu-button")
|
||||
assert html =~ ~s(data-menu-group="file")
|
||||
assert html =~ ~s(>File<)
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("[data-testid='window-titlebar-menu-button'][data-menu-group='file']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
||||
assert html =~ ~s(data-testid="window-titlebar-menu-item")
|
||||
assert html =~ ~s(data-menu-action="new_post")
|
||||
assert html =~ ~s(>New Post<)
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("[data-testid='window-titlebar-menu-button'][data-menu-group='edit']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ ~s(data-menu-action="edit_preferences")
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("[data-testid='window-titlebar-menu-item'][data-menu-action='edit_preferences']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ ~s(data-tab-type="settings")
|
||||
assert html =~ ">Settings<"
|
||||
refute html =~ ~s(data-testid="window-titlebar-menu-dropdown")
|
||||
end
|
||||
|
||||
test "sidebar open supports preview and pin intents for entity tabs" do
|
||||
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||
|
||||
|
||||
@@ -101,7 +101,9 @@ defmodule BDS.DesktopTest do
|
||||
|
||||
assert conn.status == 200
|
||||
assert conn.resp_body =~ ~s(class="app")
|
||||
assert conn.resp_body =~ ~s(class="window-titlebar")
|
||||
assert conn.resp_body =~ ~s(data-testid="window-titlebar")
|
||||
assert conn.resp_body =~ ~s(class="window-titlebar is-mac")
|
||||
refute conn.resp_body =~ ~s(data-testid="window-titlebar-menu-bar")
|
||||
assert conn.resp_body =~ ~s(class="activity-bar")
|
||||
assert conn.resp_body =~ ~s(class="sidebar")
|
||||
assert conn.resp_body =~ ~s(class="status-bar")
|
||||
|
||||
Reference in New Issue
Block a user