fix: restore drawer positioning for transaction modals on desktop (#857) (#896)

* feat: Add responsive dialog behavior for transaction modals

Add responsive option to DS::Dialog component that switches between:
- Mobile (< 1024px): Modal style (centered) with inline close button
- Desktop (≥ 1024px): Drawer style (right side panel) with header close button
Update transaction, transfer, holding, trade, and valuation views to use
responsive behavior, maintaining mobile experience while reverting desktop
to drawer style like budget categories.

Changes:
- app/components/DS/dialog.rb: Add responsive parameter and helper methods
- app/components/DS/dialog.html.erb: Apply responsive styling
- app/views/*/show.html.erb: Add responsive: true and hide close icons on mobile

* fix: Enhance close button accessibility in dialog components

* fix: Refactor dialog component to improve close button handling and accessibility
This commit is contained in:
StalkerSea
2026-02-10 17:02:15 -06:00
committed by GitHub
parent 28d99a2b0d
commit 4cf25ada63
9 changed files with 71 additions and 136 deletions

View File

@@ -1,16 +1,14 @@
<%= wrapper_element do %>
<%= tag.dialog class: "w-full h-full bg-transparent theme-dark:backdrop:bg-alpha-black-900 backdrop:bg-overlay pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)] #{drawer? ? "lg:p-3" : "lg:p-1"}", **merged_opts do %>
<%= tag.dialog class: "w-full h-full bg-transparent theme-dark:backdrop:bg-alpha-black-900 backdrop:bg-overlay pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)] #{(drawer? || responsive?) ? "lg:p-3" : "lg:p-1"}", **merged_opts do %>
<%= tag.div class: dialog_outer_classes do %>
<%= tag.div class: dialog_inner_classes, data: { DS__dialog_target: "content" } do %>
<div class="grow overflow-y-auto py-4 space-y-4 flex flex-col">
<% if header? %>
<%= header %>
<% end %>
<% if body? %>
<div class="px-4 grow">
<%= body %>
<% if sections.any? %>
<div class="space-y-4">
<% sections.each do |section| %>
@@ -20,11 +18,9 @@
<% end %>
</div>
<% end %>
<%# Optional, for customizing dialogs %>
<%= content %>
</div>
<% if actions? %>
<div class="flex items-center gap-2 justify-end p-4">
<% actions.each do |action| %>

View File

@@ -1,9 +1,9 @@
class DS::Dialog < DesignSystemComponent
renders_one :header, ->(title: nil, subtitle: nil, hide_close_icon: false, **opts, &block) do
renders_one :header, ->(title: nil, subtitle: nil, custom_header: false, **opts, &block) do
content_tag(:header, class: "px-4 flex flex-col gap-2", **opts) do
title_div = content_tag(:div, class: "flex items-center justify-between gap-2") do
title = content_tag(:h2, title, class: class_names("font-medium text-primary", drawer? ? "text-lg" : "")) if title
close_icon = render DS::Button.new(variant: "icon", class: "ml-auto", icon: "x", tabindex: "-1", data: { action: "DS--dialog#close" }) unless hide_close_icon
close_icon = close_button unless custom_header
safe_join([ title, close_icon ].compact)
end
@@ -33,7 +33,7 @@ class DS::Dialog < DesignSystemComponent
end
end
attr_reader :variant, :auto_open, :reload_on_close, :width, :disable_frame, :content_class, :disable_click_outside, :opts
attr_reader :variant, :auto_open, :reload_on_close, :width, :disable_frame, :content_class, :disable_click_outside, :opts, :responsive
VARIANTS = %w[modal drawer].freeze
WIDTHS = {
@@ -43,7 +43,7 @@ class DS::Dialog < DesignSystemComponent
full: "lg:max-w-full"
}.freeze
def initialize(variant: "modal", auto_open: true, reload_on_close: false, width: "md", frame: nil, disable_frame: false, content_class: nil, disable_click_outside: false, **opts)
def initialize(variant: "modal", auto_open: true, reload_on_close: false, width: "md", frame: nil, disable_frame: false, content_class: nil, disable_click_outside: false, responsive: false, **opts)
@variant = variant.to_sym
@auto_open = auto_open
@reload_on_close = reload_on_close
@@ -52,6 +52,7 @@ class DS::Dialog < DesignSystemComponent
@disable_frame = disable_frame
@content_class = content_class
@disable_click_outside = disable_click_outside
@responsive = responsive
@opts = opts
end
@@ -69,7 +70,9 @@ class DS::Dialog < DesignSystemComponent
end
def dialog_outer_classes
variant_classes = if drawer?
variant_classes = if responsive?
"items-center justify-center lg:items-end lg:justify-end"
elsif drawer?
"items-end justify-end"
else
"items-center justify-center"
@@ -82,7 +85,9 @@ class DS::Dialog < DesignSystemComponent
end
def dialog_inner_classes
variant_classes = if drawer?
variant_classes = if responsive?
"max-h-full lg:h-full lg:w-[550px]"
elsif drawer?
"lg:w-[550px] h-full"
else
class_names(
@@ -116,4 +121,20 @@ class DS::Dialog < DesignSystemComponent
def drawer?
variant == :drawer
end
def responsive?
@responsive
end
def close_button
classes = responsive? ? "ml-auto hidden lg:flex" : "ml-auto"
render DS::Button.new(
variant: "icon",
class: classes,
icon: "x",
title: I18n.t("common.close"),
aria_label: I18n.t("common.close"),
data: { action: "DS--dialog#close" }
)
end
end