From a9a5d1faee2257e8e83c144a5be88c378347640d Mon Sep 17 00:00:00 2001 From: Leonardo Ventura Date: Fri, 23 Oct 2026 11:30:18 -0300 Subject: [PATCH] [Bug Fix] Overlays: use shadcn's nova overlay on every modal Each modal paints its own backdrop: Dialog, Sheet and CommandDialog use the theme background at 80% with a blur, a light veil in light mode, while AlertDialog uses black at 80%. None of them matches shadcn, whose default nova style gives every overlay the same value. Use that value on all four: `bg-black/10` with `supports-backdrop-filter:backdrop-blur-xs`, black in both themes and blurred only where the browser supports it. Dialog takes it through the `backdrop:` variant on its native ``. Keep the classes inline, since `ruby_ui:component` never touches the app's stylesheet. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../ruby_ui/alert_dialog/alert_dialog_content.rb | 2 +- gem/lib/ruby_ui/command/command_dialog_content.rb | 2 +- gem/lib/ruby_ui/dialog/dialog_content.rb | 2 +- gem/lib/ruby_ui/sheet/sheet_content.rb | 2 +- gem/test/ruby_ui/alert_dialog_test.rb | 11 +++++++++++ gem/test/ruby_ui/command_test.rb | 11 +++++++++++ gem/test/ruby_ui/dialog_test.rb | 13 +++++++++++++ gem/test/ruby_ui/sheet_test.rb | 11 +++++++++++ mcp/data/registry.json | 8 ++++---- 9 files changed, 54 insertions(+), 8 deletions(-) diff --git a/gem/lib/ruby_ui/alert_dialog/alert_dialog_content.rb b/gem/lib/ruby_ui/alert_dialog/alert_dialog_content.rb index adc81fd4b..903137932 100644 --- a/gem/lib/ruby_ui/alert_dialog/alert_dialog_content.rb +++ b/gem/lib/ruby_ui/alert_dialog/alert_dialog_content.rb @@ -14,7 +14,7 @@ def view_template(&block) def background div( data_state: "open", - class: "fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in", + class: "fixed inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in", style: "pointer-events:auto", data_aria_hidden: "true", aria_hidden: "true" diff --git a/gem/lib/ruby_ui/command/command_dialog_content.rb b/gem/lib/ruby_ui/command/command_dialog_content.rb index 3e8e3a885..ad162a3fe 100644 --- a/gem/lib/ruby_ui/command/command_dialog_content.rb +++ b/gem/lib/ruby_ui/command/command_dialog_content.rb @@ -43,7 +43,7 @@ def backdrop data_state: "open", data_action: "click->ruby-ui--command#dismiss esc->ruby-ui--command#dismiss", data_ruby_ui__command_target: "backdrop", - class: "fixed pointer-events-auto inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards" + class: "fixed pointer-events-auto inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards" ) end end diff --git a/gem/lib/ruby_ui/dialog/dialog_content.rb b/gem/lib/ruby_ui/dialog/dialog_content.rb index 144504350..d0e9feaeb 100644 --- a/gem/lib/ruby_ui/dialog/dialog_content.rb +++ b/gem/lib/ruby_ui/dialog/dialog_content.rb @@ -30,7 +30,7 @@ def default_attrs data_ruby_ui__dialog_target: "dialog", data_action: "click->ruby-ui--dialog#backdropClick", class: [ - "fixed open:flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 backdrop:bg-background/80 backdrop:backdrop-blur-sm open:animate-in open:fade-in-0 open:zoom-in-95 sm:rounded-lg md:w-full", + "fixed open:flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 backdrop:bg-black/10 backdrop:supports-backdrop-filter:backdrop-blur-xs open:animate-in open:fade-in-0 open:zoom-in-95 sm:rounded-lg md:w-full", SIZES[@size] ] } diff --git a/gem/lib/ruby_ui/sheet/sheet_content.rb b/gem/lib/ruby_ui/sheet/sheet_content.rb index 596d29386..d20d2656c 100644 --- a/gem/lib/ruby_ui/sheet/sheet_content.rb +++ b/gem/lib/ruby_ui/sheet/sheet_content.rb @@ -72,7 +72,7 @@ def backdrop data_action: "click->ruby-ui--sheet-content#close", data_ruby_ui__sheet_content_target: "backdrop", class: - "fixed pointer-events-auto inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards" + "fixed pointer-events-auto inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards" ) end end diff --git a/gem/test/ruby_ui/alert_dialog_test.rb b/gem/test/ruby_ui/alert_dialog_test.rb index 9c27fb710..4fa219fbd 100644 --- a/gem/test/ruby_ui/alert_dialog_test.rb +++ b/gem/test/ruby_ui/alert_dialog_test.rb @@ -24,4 +24,15 @@ def test_render_with_all_items assert_match(/Show dialog/, output) end + + # Every modal shares shadcn's overlay: black at 10% in both themes, blurred only where backdrop-filter is supported. + def test_content_uses_the_shared_modal_overlay + output = phlex do + RubyUI.AlertDialogContent { "Content" } + end + + assert_includes output, "bg-black/10" + assert_includes output, "supports-backdrop-filter:backdrop-blur-xs" + refute_match(/bg-background\/80|bg-black\/80|backdrop-blur-sm/, output) + end end diff --git a/gem/test/ruby_ui/command_test.rb b/gem/test/ruby_ui/command_test.rb index dbdd19d02..f22483afa 100644 --- a/gem/test/ruby_ui/command_test.rb +++ b/gem/test/ruby_ui/command_test.rb @@ -71,4 +71,15 @@ def test_content_and_backdrop_hold_the_last_frame_of_the_exit_animation assert_equal 2, output.scan("data-[state=closed]:fill-mode-forwards").size end + + # Every modal shares shadcn's overlay: black at 10% in both themes, blurred only where backdrop-filter is supported. + def test_backdrop_uses_the_shared_modal_overlay + output = phlex do + RubyUI.CommandDialogContent { "command body" } + end + + assert_includes output, "bg-black/10" + assert_includes output, "supports-backdrop-filter:backdrop-blur-xs" + refute_match(/bg-background\/80|bg-black\/80|backdrop-blur-sm/, output) + end end diff --git a/gem/test/ruby_ui/dialog_test.rb b/gem/test/ruby_ui/dialog_test.rb index 90061d34d..014a77c5e 100644 --- a/gem/test/ruby_ui/dialog_test.rb +++ b/gem/test/ruby_ui/dialog_test.rb @@ -136,4 +136,17 @@ def test_trigger_has_open_action assert_match(/data-action="click->ruby-ui--dialog#open"/, output) end + + # Every modal shares shadcn's overlay: black at 10% in both themes, blurred only where backdrop-filter is supported. + def test_dialog_content_uses_the_shared_modal_overlay + output = phlex do + RubyUI.Dialog do + RubyUI.DialogContent { "Content" } + end + end + + assert_includes output, "backdrop:bg-black/10" + assert_includes output, "backdrop:supports-backdrop-filter:backdrop-blur-xs" + refute_match(/bg-background\/80|bg-black\/80|backdrop-blur-sm/, output) + end end diff --git a/gem/test/ruby_ui/sheet_test.rb b/gem/test/ruby_ui/sheet_test.rb index 57e206b59..0f542893a 100644 --- a/gem/test/ruby_ui/sheet_test.rb +++ b/gem/test/ruby_ui/sheet_test.rb @@ -51,4 +51,15 @@ def test_content_and_backdrop_hold_the_last_frame_of_the_exit_animation assert_equal 2, output.scan("data-[state=closed]:fill-mode-forwards").size end + + # Every modal shares shadcn's overlay: black at 10% in both themes, blurred only where backdrop-filter is supported. + def test_backdrop_uses_the_shared_modal_overlay + output = phlex do + RubyUI.SheetContent { "sheet body" } + end + + assert_includes output, "bg-black/10" + assert_includes output, "supports-backdrop-filter:backdrop-blur-xs" + refute_match(/bg-background\/80|bg-black\/80|backdrop-blur-sm/, output) + end end diff --git a/mcp/data/registry.json b/mcp/data/registry.json index c5505d648..a8f9953c6 100644 --- a/mcp/data/registry.json +++ b/mcp/data/registry.json @@ -125,7 +125,7 @@ }, { "path": "alert_dialog_content.rb", - "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class AlertDialogContent < Base\n def view_template(&block)\n template(**attrs) do\n div(data: {controller: \"ruby-ui--alert-dialog\"}) do\n background\n container(&block)\n end\n end\n end\n\n def background\n div(\n data_state: \"open\",\n class: \"fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in\",\n style: \"pointer-events:auto\",\n data_aria_hidden: \"true\",\n aria_hidden: \"true\"\n )\n end\n\n def container(&)\n div(\n role: \"alertdialog\",\n data_state: \"open\",\n class: \"flex flex-col fixed left-[50%] top-[50%] z-50 w-full max-w-lg max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:rounded-lg md:w-full\",\n style: \"pointer-events:auto\",\n &\n )\n end\n\n private\n\n def default_attrs\n {\n data: {\n ruby_ui__alert_dialog_target: \"content\"\n }\n }\n end\n end\nend\n" + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class AlertDialogContent < Base\n def view_template(&block)\n template(**attrs) do\n div(data: {controller: \"ruby-ui--alert-dialog\"}) do\n background\n container(&block)\n end\n end\n end\n\n def background\n div(\n data_state: \"open\",\n class: \"fixed inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in\",\n style: \"pointer-events:auto\",\n data_aria_hidden: \"true\",\n aria_hidden: \"true\"\n )\n end\n\n def container(&)\n div(\n role: \"alertdialog\",\n data_state: \"open\",\n class: \"flex flex-col fixed left-[50%] top-[50%] z-50 w-full max-w-lg max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:rounded-lg md:w-full\",\n style: \"pointer-events:auto\",\n &\n )\n end\n\n private\n\n def default_attrs\n {\n data: {\n ruby_ui__alert_dialog_target: \"content\"\n }\n }\n end\n end\nend\n" }, { "path": "alert_dialog_controller.js", @@ -1060,7 +1060,7 @@ }, { "path": "command_dialog_content.rb", - "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class CommandDialogContent < Base\n SIZES = {\n xs: \"max-w-sm\",\n sm: \"max-w-md\",\n md: \"max-w-lg\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n full: \"max-w-full\"\n }\n\n def initialize(size: :md, **attrs)\n @size = size\n super(**attrs)\n end\n\n def view_template(&block)\n template(data: {ruby_ui__command_dialog_target: \"content\"}) do\n div(data: {controller: \"ruby-ui--command\", ruby_ui__command_dialog_instance: true}) do\n backdrop\n div(**attrs, &block)\n end\n end\n end\n\n private\n\n def default_attrs\n {\n data_state: \"open\",\n data_ruby_ui__command_target: \"panel\",\n class: [\n \"fixed pointer-events-auto left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:fill-mode-forwards data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full\",\n SIZES[@size]\n ]\n }\n end\n\n def backdrop\n div(\n data_state: \"open\",\n data_action: \"click->ruby-ui--command#dismiss esc->ruby-ui--command#dismiss\",\n data_ruby_ui__command_target: \"backdrop\",\n class: \"fixed pointer-events-auto inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards\"\n )\n end\n end\nend\n" + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class CommandDialogContent < Base\n SIZES = {\n xs: \"max-w-sm\",\n sm: \"max-w-md\",\n md: \"max-w-lg\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n full: \"max-w-full\"\n }\n\n def initialize(size: :md, **attrs)\n @size = size\n super(**attrs)\n end\n\n def view_template(&block)\n template(data: {ruby_ui__command_dialog_target: \"content\"}) do\n div(data: {controller: \"ruby-ui--command\", ruby_ui__command_dialog_instance: true}) do\n backdrop\n div(**attrs, &block)\n end\n end\n end\n\n private\n\n def default_attrs\n {\n data_state: \"open\",\n data_ruby_ui__command_target: \"panel\",\n class: [\n \"fixed pointer-events-auto left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:fill-mode-forwards data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full\",\n SIZES[@size]\n ]\n }\n end\n\n def backdrop\n div(\n data_state: \"open\",\n data_action: \"click->ruby-ui--command#dismiss esc->ruby-ui--command#dismiss\",\n data_ruby_ui__command_target: \"backdrop\",\n class: \"fixed pointer-events-auto inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards\"\n )\n end\n end\nend\n" }, { "path": "command_dialog_controller.js", @@ -1329,7 +1329,7 @@ }, { "path": "dialog_content.rb", - "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class DialogContent < Base\n SIZES = {\n xs: \"max-w-sm\",\n sm: \"max-w-md\",\n md: \"max-w-lg\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n full: \"max-w-full\"\n }\n\n def initialize(size: :md, **attrs)\n @size = size\n super(**attrs)\n end\n\n def view_template\n dialog(**attrs) do\n yield\n close_button\n end\n end\n\n private\n\n def default_attrs\n {\n data_ruby_ui__dialog_target: \"dialog\",\n data_action: \"click->ruby-ui--dialog#backdropClick\",\n class: [\n \"fixed open:flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 backdrop:bg-background/80 backdrop:backdrop-blur-sm open:animate-in open:fade-in-0 open:zoom-in-95 sm:rounded-lg md:w-full\",\n SIZES[@size]\n ]\n }\n end\n\n def close_button\n button(\n type: \"button\",\n class: \"absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\",\n data_action: \"click->ruby-ui--dialog#dismiss\"\n ) do\n svg(\n width: \"15\",\n height: \"15\",\n viewbox: \"0 0 15 15\",\n fill: \"none\",\n xmlns: \"http://www.w3.org/2000/svg\",\n class: \"h-4 w-4\"\n ) do |s|\n s.path(\n d:\n \"M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z\",\n fill: \"currentColor\",\n fill_rule: \"evenodd\",\n clip_rule: \"evenodd\"\n )\n end\n span(class: \"sr-only\") { \"Close\" }\n end\n end\n end\nend\n" + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class DialogContent < Base\n SIZES = {\n xs: \"max-w-sm\",\n sm: \"max-w-md\",\n md: \"max-w-lg\",\n lg: \"max-w-2xl\",\n xl: \"max-w-4xl\",\n full: \"max-w-full\"\n }\n\n def initialize(size: :md, **attrs)\n @size = size\n super(**attrs)\n end\n\n def view_template\n dialog(**attrs) do\n yield\n close_button\n end\n end\n\n private\n\n def default_attrs\n {\n data_ruby_ui__dialog_target: \"dialog\",\n data_action: \"click->ruby-ui--dialog#backdropClick\",\n class: [\n \"fixed open:flex flex-col pointer-events-auto left-[50%] top-[50%] z-50 w-full max-h-screen overflow-y-auto translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 backdrop:bg-black/10 backdrop:supports-backdrop-filter:backdrop-blur-xs open:animate-in open:fade-in-0 open:zoom-in-95 sm:rounded-lg md:w-full\",\n SIZES[@size]\n ]\n }\n end\n\n def close_button\n button(\n type: \"button\",\n class: \"absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\",\n data_action: \"click->ruby-ui--dialog#dismiss\"\n ) do\n svg(\n width: \"15\",\n height: \"15\",\n viewbox: \"0 0 15 15\",\n fill: \"none\",\n xmlns: \"http://www.w3.org/2000/svg\",\n class: \"h-4 w-4\"\n ) do |s|\n s.path(\n d:\n \"M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z\",\n fill: \"currentColor\",\n fill_rule: \"evenodd\",\n clip_rule: \"evenodd\"\n )\n end\n span(class: \"sr-only\") { \"Close\" }\n end\n end\n end\nend\n" }, { "path": "dialog_controller.js", @@ -2285,7 +2285,7 @@ }, { "path": "sheet_content.rb", - "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class SheetContent < Base\n SIDE_CLASS = {\n top: \"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n right: \"inset-y-0 right-0 h-full border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right\",\n bottom: \"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left\"\n }\n\n def initialize(side: :right, **attrs)\n @side = side\n @side_classes = SIDE_CLASS[side]\n super(**attrs)\n end\n\n def view_template(&block)\n template(data: {ruby_ui__sheet_target: \"content\"}) do\n div(data: {controller: \"ruby-ui--sheet-content\"}) do\n backdrop\n div(**attrs) do\n block&.call\n close_button\n end\n end\n end\n end\n\n private\n\n def default_attrs\n {\n data_state: \"open\", # For animate in\n data_ruby_ui__sheet_content_target: \"panel\",\n class: [\n \"fixed pointer-events-auto z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards data-[state=closed]:duration-300 data-[state=open]:duration-500 overflow-scroll\",\n @side_classes\n ]\n }\n end\n\n def close_button\n button(\n type: \"button\",\n class: \"absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\",\n data_action: \"click->ruby-ui--sheet-content#close\"\n ) do\n svg(\n width: \"15\",\n height: \"15\",\n viewbox: \"0 0 15 15\",\n fill: \"none\",\n xmlns: \"http://www.w3.org/2000/svg\",\n class: \"h-4 w-4\"\n ) do |s|\n s.path(\n d:\n \"M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z\",\n fill: \"currentColor\",\n fill_rule: \"evenodd\",\n clip_rule: \"evenodd\"\n )\n end\n span(class: \"sr-only\") { \"Close\" }\n end\n end\n\n def backdrop\n div(\n data_state: \"open\",\n data_action: \"click->ruby-ui--sheet-content#close\",\n data_ruby_ui__sheet_content_target: \"backdrop\",\n class:\n \"fixed pointer-events-auto inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards\"\n )\n end\n end\nend\n" + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class SheetContent < Base\n SIDE_CLASS = {\n top: \"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n right: \"inset-y-0 right-0 h-full border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right\",\n bottom: \"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left\"\n }\n\n def initialize(side: :right, **attrs)\n @side = side\n @side_classes = SIDE_CLASS[side]\n super(**attrs)\n end\n\n def view_template(&block)\n template(data: {ruby_ui__sheet_target: \"content\"}) do\n div(data: {controller: \"ruby-ui--sheet-content\"}) do\n backdrop\n div(**attrs) do\n block&.call\n close_button\n end\n end\n end\n end\n\n private\n\n def default_attrs\n {\n data_state: \"open\", # For animate in\n data_ruby_ui__sheet_content_target: \"panel\",\n class: [\n \"fixed pointer-events-auto z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fill-mode-forwards data-[state=closed]:duration-300 data-[state=open]:duration-500 overflow-scroll\",\n @side_classes\n ]\n }\n end\n\n def close_button\n button(\n type: \"button\",\n class: \"absolute end-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\",\n data_action: \"click->ruby-ui--sheet-content#close\"\n ) do\n svg(\n width: \"15\",\n height: \"15\",\n viewbox: \"0 0 15 15\",\n fill: \"none\",\n xmlns: \"http://www.w3.org/2000/svg\",\n class: \"h-4 w-4\"\n ) do |s|\n s.path(\n d:\n \"M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z\",\n fill: \"currentColor\",\n fill_rule: \"evenodd\",\n clip_rule: \"evenodd\"\n )\n end\n span(class: \"sr-only\") { \"Close\" }\n end\n end\n\n def backdrop\n div(\n data_state: \"open\",\n data_action: \"click->ruby-ui--sheet-content#close\",\n data_ruby_ui__sheet_content_target: \"backdrop\",\n class:\n \"fixed pointer-events-auto inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:fill-mode-forwards\"\n )\n end\n end\nend\n" }, { "path": "sheet_content_controller.js",