modal_stack 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +23 -2
- data/app/assets/stylesheets/modal_stack/bootstrap.css +35 -0
- data/app/assets/stylesheets/modal_stack/tailwind_v3.css +35 -0
- data/app/assets/stylesheets/modal_stack/tailwind_v4.css +35 -0
- data/app/assets/stylesheets/modal_stack/vanilla.css +35 -0
- data/app/views/modal_stack/_panel.html.erb +13 -0
- data/lib/modal_stack/helpers/modal_stack_container_helper.rb +4 -1
- data/lib/modal_stack/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 992372f08bce8ea31105e8cba05c60035014852c197c38b325648eaebf154820
|
|
4
|
+
data.tar.gz: 70db58c91eda0101149fde86cec748812c7b8aff3f3908a30d6178538000c98f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98043c481ff734f042b88f2117b766fb1dc2110dfdacbad71d66de39471999d15e1dfb368e9466964d1127b809ed4bb3a8aea4fb83847aba6163d717f75eb4bf
|
|
7
|
+
data.tar.gz: 46b3b5491dd13836ee207e62b42d2a3023ee1b65ac75da634d40a944608822b2a40e102ef39861f0208ab47a17afb4dbe0d529341c8d9611adce2ef9ba600ab5
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.4.3] - 2026-05-25
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`title:` option on `modal_stack_container`** — renders a `<header class="modal-stack__panel-header">` containing an `<h2 class="modal-stack__panel-title">` above the panel content. No output when omitted, so existing panels are unaffected.
|
|
13
|
+
- **`close_button:` option on `modal_stack_container`** — renders a `×` button (`<button class="modal-stack__panel-close">`) wired to `modal-stack#pop`. Defaults to the `dismissible:` value, so dismissible layers get a close button for free and locked layers (`dismissible: false`) get none by default. Pass `close_button: false` to suppress it explicitly on a dismissible layer.
|
|
14
|
+
- **CSS** for the new header slot in all four presets (`tailwind_v4`, `tailwind_v3`, `bootstrap`, `vanilla`): `.modal-stack__panel-header` (flexbox row), `.modal-stack__panel-title` (flex-1), `.modal-stack__panel-close` (transparent button, opacity hover, focus-visible ring).
|
|
15
|
+
- `title` and `show_close` passed as **separate partial locals** to `_panel.html.erb` so host apps overriding the partial can consume them individually rather than receiving a pre-rendered HTML blob.
|
|
16
|
+
|
|
9
17
|
## [0.4.2] - 2026-05-15
|
|
10
18
|
|
|
11
19
|
### Added
|
data/README.md
CHANGED
|
@@ -279,9 +279,30 @@ options at the call site:
|
|
|
279
279
|
```
|
|
280
280
|
|
|
281
281
|
`modal_stack_container` accepts `size:`, `variant:`, `side:`, `width:`,
|
|
282
|
-
`height:`, `dismissible:`, and an `html: { class:, data:, ... }` Hash for
|
|
282
|
+
`height:`, `dismissible:`, `title:`, `close_button:`, and an `html: { class:, data:, ... }` Hash for
|
|
283
283
|
extra attributes on the wrapping `<div>`.
|
|
284
284
|
|
|
285
|
+
Pass `title:` to render a `<header>` with an `<h2>` above the panel content.
|
|
286
|
+
`close_button:` (default: inherits `dismissible:`) renders a `×` button wired to
|
|
287
|
+
`modal-stack#pop` — locked layers (`dismissible: false`) get no close button by default:
|
|
288
|
+
|
|
289
|
+
```erb
|
|
290
|
+
<%# Dismissible — title + auto close button %>
|
|
291
|
+
<%= modal_stack_container title: "Edit project", size: :md do %>
|
|
292
|
+
<%= render "form", project: @project %>
|
|
293
|
+
<% end %>
|
|
294
|
+
|
|
295
|
+
<%# Locked — title only, no × (close_button: defaults to false) %>
|
|
296
|
+
<%= modal_stack_container title: "Are you sure?",
|
|
297
|
+
variant: :confirmation,
|
|
298
|
+
dismissible: false do %>
|
|
299
|
+
<button data-action="click->modal-stack#pop">Confirm</button>
|
|
300
|
+
<% end %>
|
|
301
|
+
|
|
302
|
+
<%# Explicit override %>
|
|
303
|
+
<%= modal_stack_container title: "Info", close_button: false do %>…<% end %>
|
|
304
|
+
```
|
|
305
|
+
|
|
285
306
|
### Stack-aware controllers
|
|
286
307
|
|
|
287
308
|
`modal_stack_layout` switches the controller's layout to `modal` **only**
|
|
@@ -484,7 +505,7 @@ Injected into `ActionView::Base` by the engine — available in every view.
|
|
|
484
505
|
| Helper | Description |
|
|
485
506
|
| ------------------------------------------------- | ----------- |
|
|
486
507
|
| `modal_link_to(name, options, html_options)` | Renders a `link_to` wired to push a layer when clicked. Accepts the modal options (`as:`, `side:`, `size:`, `width:`, `height:`, `dismissible:`) on top of standard `link_to` arguments. Falls back to plain `link_to` for Hotwire Native requests. |
|
|
487
|
-
| `modal_stack_container(size:, variant:, side:, width:, height:, dismissible:, back:, transition:, html: {}) { ... }` | Wraps a panel view with the markup the JS runtime expects. `back: true` injects a back-button slot wired to `modal-stack#pathBack` (hidden by CSS at the first frame); `transition:` writes `data-modal-stack-transition` for host CSS hooks. |
|
|
508
|
+
| `modal_stack_container(size:, variant:, side:, width:, height:, dismissible:, back:, transition:, title:, close_button:, html: {}) { ... }` | Wraps a panel view with the markup the JS runtime expects. `back: true` injects a back-button slot wired to `modal-stack#pathBack` (hidden by CSS at the first frame); `transition:` writes `data-modal-stack-transition` for host CSS hooks. `title:` renders a `<header>` with `<h2>`; `close_button:` (default: `dismissible:` value) renders a `×` close button. |
|
|
488
509
|
| `modal_back_link(name = nil, **opts) { ... }` | Renders a `<button>` wired to the `modal-stack-back-link` Stimulus controller. Pass `steps:` (default `1`) to walk back multiple frames in a single click — clamped at the first frame, never closes the layer. Block form supported for custom markup (e.g. `<%= modal_back_link(class: "btn") { "← Back" } %>`). |
|
|
489
510
|
| `modal_stack_stylesheet_link_tag(**options)` | Emits `<link rel="stylesheet">` for the configured preset (`modal_stack/tailwind_v4.css`, etc.). Returns an empty SafeBuffer when `css_provider = :none`. |
|
|
490
511
|
| `modal_stack_dialog_tag(**html_options)` | Emits the singleton `<dialog id="modal-stack-root" data-controller="modal-stack">`. Drop just before `</body>`. |
|
|
@@ -331,6 +331,41 @@ body[data-modal-stack-locked] {
|
|
|
331
331
|
display: none;
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
/* --- Header slot ------------------------------------------------- */
|
|
335
|
+
|
|
336
|
+
.modal-stack__panel-header {
|
|
337
|
+
display: flex;
|
|
338
|
+
align-items: center;
|
|
339
|
+
justify-content: space-between;
|
|
340
|
+
gap: 0.75rem;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.modal-stack__panel-title {
|
|
344
|
+
margin: 0;
|
|
345
|
+
flex: 1;
|
|
346
|
+
min-width: 0;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.modal-stack__panel-close {
|
|
350
|
+
font: inherit;
|
|
351
|
+
background: transparent;
|
|
352
|
+
border: 0;
|
|
353
|
+
padding: 0.25rem;
|
|
354
|
+
cursor: pointer;
|
|
355
|
+
color: inherit;
|
|
356
|
+
opacity: 0.55;
|
|
357
|
+
line-height: 1;
|
|
358
|
+
font-size: 1.4em;
|
|
359
|
+
flex-shrink: 0;
|
|
360
|
+
transition: opacity 150ms ease;
|
|
361
|
+
}
|
|
362
|
+
.modal-stack__panel-close:hover { opacity: 1; }
|
|
363
|
+
.modal-stack__panel-close:focus-visible {
|
|
364
|
+
outline: 2px solid currentColor;
|
|
365
|
+
outline-offset: 2px;
|
|
366
|
+
border-radius: 2px;
|
|
367
|
+
}
|
|
368
|
+
|
|
334
369
|
@media (prefers-reduced-motion: reduce) {
|
|
335
370
|
#modal-stack-root,
|
|
336
371
|
#modal-stack-root::backdrop,
|
|
@@ -359,6 +359,41 @@ body[data-modal-stack-locked] {
|
|
|
359
359
|
display: none;
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
+
/* --- Header slot ------------------------------------------------- */
|
|
363
|
+
|
|
364
|
+
.modal-stack__panel-header {
|
|
365
|
+
display: flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
justify-content: space-between;
|
|
368
|
+
gap: 0.75rem;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.modal-stack__panel-title {
|
|
372
|
+
margin: 0;
|
|
373
|
+
flex: 1;
|
|
374
|
+
min-width: 0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.modal-stack__panel-close {
|
|
378
|
+
font: inherit;
|
|
379
|
+
background: transparent;
|
|
380
|
+
border: 0;
|
|
381
|
+
padding: 0.25rem;
|
|
382
|
+
cursor: pointer;
|
|
383
|
+
color: inherit;
|
|
384
|
+
opacity: 0.55;
|
|
385
|
+
line-height: 1;
|
|
386
|
+
font-size: 1.4em;
|
|
387
|
+
flex-shrink: 0;
|
|
388
|
+
transition: opacity 150ms ease;
|
|
389
|
+
}
|
|
390
|
+
.modal-stack__panel-close:hover { opacity: 1; }
|
|
391
|
+
.modal-stack__panel-close:focus-visible {
|
|
392
|
+
outline: 2px solid currentColor;
|
|
393
|
+
outline-offset: 2px;
|
|
394
|
+
border-radius: 2px;
|
|
395
|
+
}
|
|
396
|
+
|
|
362
397
|
/* --- Reduced motion ---------------------------------------------- */
|
|
363
398
|
|
|
364
399
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -360,6 +360,41 @@ body[data-modal-stack-locked] {
|
|
|
360
360
|
display: none;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
/* --- Header slot ------------------------------------------------- */
|
|
364
|
+
|
|
365
|
+
.modal-stack__panel-header {
|
|
366
|
+
display: flex;
|
|
367
|
+
align-items: center;
|
|
368
|
+
justify-content: space-between;
|
|
369
|
+
gap: 0.75rem;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.modal-stack__panel-title {
|
|
373
|
+
margin: 0;
|
|
374
|
+
flex: 1;
|
|
375
|
+
min-width: 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.modal-stack__panel-close {
|
|
379
|
+
font: inherit;
|
|
380
|
+
background: transparent;
|
|
381
|
+
border: 0;
|
|
382
|
+
padding: 0.25rem;
|
|
383
|
+
cursor: pointer;
|
|
384
|
+
color: inherit;
|
|
385
|
+
opacity: 0.55;
|
|
386
|
+
line-height: 1;
|
|
387
|
+
font-size: 1.4em;
|
|
388
|
+
flex-shrink: 0;
|
|
389
|
+
transition: opacity 150ms ease;
|
|
390
|
+
}
|
|
391
|
+
.modal-stack__panel-close:hover { opacity: 1; }
|
|
392
|
+
.modal-stack__panel-close:focus-visible {
|
|
393
|
+
outline: 2px solid currentColor;
|
|
394
|
+
outline-offset: 2px;
|
|
395
|
+
border-radius: 2px;
|
|
396
|
+
}
|
|
397
|
+
|
|
363
398
|
/* --- Reduced motion ---------------------------------------------- */
|
|
364
399
|
|
|
365
400
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -326,6 +326,41 @@ body[data-modal-stack-locked] {
|
|
|
326
326
|
display: none;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
/* --- Header slot ------------------------------------------------- */
|
|
330
|
+
|
|
331
|
+
.modal-stack__panel-header {
|
|
332
|
+
display: flex;
|
|
333
|
+
align-items: center;
|
|
334
|
+
justify-content: space-between;
|
|
335
|
+
gap: 0.75rem;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.modal-stack__panel-title {
|
|
339
|
+
margin: 0;
|
|
340
|
+
flex: 1;
|
|
341
|
+
min-width: 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.modal-stack__panel-close {
|
|
345
|
+
font: inherit;
|
|
346
|
+
background: transparent;
|
|
347
|
+
border: 0;
|
|
348
|
+
padding: 0.25rem;
|
|
349
|
+
cursor: pointer;
|
|
350
|
+
color: inherit;
|
|
351
|
+
opacity: 0.55;
|
|
352
|
+
line-height: 1;
|
|
353
|
+
font-size: 1.4em;
|
|
354
|
+
flex-shrink: 0;
|
|
355
|
+
transition: opacity 150ms ease;
|
|
356
|
+
}
|
|
357
|
+
.modal-stack__panel-close:hover { opacity: 1; }
|
|
358
|
+
.modal-stack__panel-close:focus-visible {
|
|
359
|
+
outline: 2px solid currentColor;
|
|
360
|
+
outline-offset: 2px;
|
|
361
|
+
border-radius: 2px;
|
|
362
|
+
}
|
|
363
|
+
|
|
329
364
|
@media (prefers-reduced-motion: reduce) {
|
|
330
365
|
#modal-stack-root,
|
|
331
366
|
#modal-stack-root::backdrop,
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
<%= content_tag(:div, wrapper_attrs) do %>
|
|
2
|
+
<% if local_assigns[:title].present? || local_assigns[:show_close] %>
|
|
3
|
+
<header class="modal-stack__panel-header">
|
|
4
|
+
<% if local_assigns[:title].present? %>
|
|
5
|
+
<h2 class="modal-stack__panel-title"><%= title %></h2>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% if local_assigns[:show_close] %>
|
|
8
|
+
<button type="button"
|
|
9
|
+
class="modal-stack__panel-close"
|
|
10
|
+
data-action="click->modal-stack#pop"
|
|
11
|
+
aria-label="<%= I18n.t("modal_stack.close", default: "Close") %>">×</button>
|
|
12
|
+
<% end %>
|
|
13
|
+
</header>
|
|
14
|
+
<% end %>
|
|
2
15
|
<%= back_button %>
|
|
3
16
|
<%= content %>
|
|
4
17
|
<% end %>
|
|
@@ -13,15 +13,18 @@ module ModalStack
|
|
|
13
13
|
DEFAULT_SIZE = :md
|
|
14
14
|
|
|
15
15
|
def modal_stack_container(size: DEFAULT_SIZE, dismissible: true, variant: :modal, side: nil, width: nil, height: nil,
|
|
16
|
-
back: false, transition: nil, html: {},
|
|
16
|
+
back: false, transition: nil, html: {},
|
|
17
|
+
title: nil, close_button: nil, &)
|
|
17
18
|
attrs = build_panel_attrs(size: size, variant: variant, side: side,
|
|
18
19
|
dismissible: dismissible, width: width,
|
|
19
20
|
height: height, transition: transition, html: html)
|
|
20
21
|
body = capture(&)
|
|
21
22
|
back_html = back ? modal_stack_container_back_button : nil
|
|
23
|
+
show_close = close_button.nil? ? dismissible : close_button
|
|
22
24
|
|
|
23
25
|
render partial: "modal_stack/panel", locals: {
|
|
24
26
|
content: body, back_button: back_html, wrapper_attrs: attrs,
|
|
27
|
+
title: title, show_close: show_close,
|
|
25
28
|
size: size, variant: variant, dismissible: dismissible,
|
|
26
29
|
side: side, width: width, height: height, transition: transition
|
|
27
30
|
}
|
data/lib/modal_stack/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: modal_stack
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Florian Gagnaire
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: railties
|