atomic_view 0.5.2 → 0.6.0
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d26209da003c20d55578d1f8c1036c7f2d6d8f479e1eafadba499aebe8b417a6
|
|
4
|
+
data.tar.gz: '0781dfbfd8ddf9d8a1f2e5be5a13b1f2e3511cc776ba4feb39e439933885f1f7'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 242b73833f14000d4efdd0d41f2be4a5352bc8548fc8bfeddd90501578d8b351c85cfc034a9fc1d64f75809dc4fb206d02dfabc137aa81842d8408dfa4951c3b
|
|
7
|
+
data.tar.gz: f591c5a4004651687bdb6229c7b44a44b20084615c48e52d97cc378b339a67f6237ec33edbb8a80a3e2c41faba79b07055c4975749d846c0c2b05b5e287c871c
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static values = { open: Boolean }
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
this.element.addEventListener("click", this.closeOnBackdrop)
|
|
8
|
+
if (this.openValue) this.open()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
disconnect() {
|
|
12
|
+
this.element.removeEventListener("click", this.closeOnBackdrop)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
open() {
|
|
16
|
+
this.element.showModal()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
close() {
|
|
20
|
+
this.element.close()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
closeOnBackdrop = (event) => {
|
|
24
|
+
if (event.target === this.element) this.close()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%= tag.dialog(**html_options, id: id, class: html_class, data: data_attributes) do %>
|
|
2
|
+
<div class="flex items-center justify-between gap-4 border-b border-border p-4">
|
|
3
|
+
<% if title.present? %>
|
|
4
|
+
<h2 class="font-body text-lg font-semibold text-foreground"><%= title %></h2>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<div class="ml-auto flex items-center gap-2">
|
|
8
|
+
<% if actions? %>
|
|
9
|
+
<%= actions %>
|
|
10
|
+
<% end %>
|
|
11
|
+
|
|
12
|
+
<%= tag.button(type: "button", class: "inline-flex shrink-0 items-center text-muted-foreground hover:text-foreground", data: {action: "click->atomic-view--drawer#close"}, aria: {label: "Close"}) do %>
|
|
13
|
+
<%= close_icon %>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="flex-1 overflow-y-auto p-4 text-sm text-foreground">
|
|
19
|
+
<%= content %>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<% if footer? %>
|
|
23
|
+
<div class="flex justify-end gap-2 border-t border-border p-4">
|
|
24
|
+
<%= footer %>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% end %>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AtomicView
|
|
4
|
+
module Components
|
|
5
|
+
# Drawer
|
|
6
|
+
#
|
|
7
|
+
# A side panel built on the native `<dialog>` element -- same structural
|
|
8
|
+
# trick as `ModalComponent` (see that component for the rationale), just
|
|
9
|
+
# pinned to one edge of the viewport (`side: :left`/`:right`, default
|
|
10
|
+
# `:right`) and stretched to full height instead of centered. `<dialog>`
|
|
11
|
+
# still gets focus-trap, `::backdrop` styling, and Esc-to-close for free;
|
|
12
|
+
# `atomic-view--drawer` (see
|
|
13
|
+
# `app/assets/javascripts/atomic_view/controllers/drawer_controller.js`)
|
|
14
|
+
# layers on `open`/`close` plus closing when the backdrop itself (not the
|
|
15
|
+
# content box) is clicked -- identical to Modal's controller.
|
|
16
|
+
#
|
|
17
|
+
# Layout is a fixed header (title, an optional `actions` slot for e.g. a
|
|
18
|
+
# secondary action button, and an always-present close button) and an
|
|
19
|
+
# optional fixed footer (`renders_one :footer`, typically Cancel/Confirm
|
|
20
|
+
# buttons) around a body that scrolls independently -- the body is the
|
|
21
|
+
# component's default slot (the block passed to `render`).
|
|
22
|
+
#
|
|
23
|
+
# A footer Confirm button that needs to submit a `form_with` in the body
|
|
24
|
+
# can't be nested inside it -- they're siblings, in separate header/
|
|
25
|
+
# body/footer containers. Give the form an explicit `id:` instead, and
|
|
26
|
+
# point the button at it with HTML5's `form="..."` attribute, which
|
|
27
|
+
# associates a button with a form by id no matter where in the DOM the
|
|
28
|
+
# button actually lives:
|
|
29
|
+
#
|
|
30
|
+
# render(DrawerComponent.new(id: "product-drawer", title: "Add a product")) do |drawer|
|
|
31
|
+
# drawer.with_footer do
|
|
32
|
+
# render ButtonComponent.new(nil, "Add product", {type: "submit", form: "product-drawer-form"})
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# form_with(model: @product, id: "product-drawer-form") { |form| ... }
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# == Turbo Streams: same content, opened two different ways
|
|
39
|
+
#
|
|
40
|
+
# A drawer's body is often "a page" -- e.g. a New Product form -- that
|
|
41
|
+
# should also work as an ordinary full-page route for a direct link,
|
|
42
|
+
# bookmark, or reload. Both cases render the exact same
|
|
43
|
+
# `DrawerComponent.new(id: "...", open: true) { ... }` call; the
|
|
44
|
+
# difference is only what's *around* it:
|
|
45
|
+
#
|
|
46
|
+
# * Direct visit (e.g. GET /products/new): render the underlying page
|
|
47
|
+
# (the product list) as normal, with this drawer -- `open: true` --
|
|
48
|
+
# included in that same response, already showing on top of it.
|
|
49
|
+
# * Opened from elsewhere via Turbo Streams: the triggering link/form
|
|
50
|
+
# targets `format: :turbo_stream`, and the response replaces or
|
|
51
|
+
# updates a container already sitting on the page with a fresh
|
|
52
|
+
# render of this same component (again `open: true`):
|
|
53
|
+
#
|
|
54
|
+
# <%= turbo_stream.update("product-drawer", render(
|
|
55
|
+
# AtomicView::Components::DrawerComponent.new(id: "product-drawer-dialog", open: true, title: "Add a product") do
|
|
56
|
+
# render "products/form"
|
|
57
|
+
# end
|
|
58
|
+
# )) %>
|
|
59
|
+
#
|
|
60
|
+
# Either way, Turbo inserts a brand new `<dialog>` node, so
|
|
61
|
+
# `atomic-view--drawer` never needs a bespoke "please open now" event to
|
|
62
|
+
# bridge the two cases -- `connect()` sees `open: true` and calls
|
|
63
|
+
# `showModal()` itself every time, whether that's on first paint or after
|
|
64
|
+
# a Turbo Stream swap. Closing it (backdrop click, Esc, or a Cancel
|
|
65
|
+
# button) is just `dialog.close()` -- the page behind it, already in the
|
|
66
|
+
# same document, is simply what's left after the drawer's gone; give the
|
|
67
|
+
# host app's own controller the job of updating the URL back if that
|
|
68
|
+
# matters.
|
|
69
|
+
class DrawerComponent < AtomicView::Component
|
|
70
|
+
renders_one :actions
|
|
71
|
+
renders_one :footer
|
|
72
|
+
|
|
73
|
+
attr_reader :id, :title, :side, :open
|
|
74
|
+
|
|
75
|
+
def initialize(id:, title: nil, side: :right, open: false, **options)
|
|
76
|
+
super()
|
|
77
|
+
@id = id
|
|
78
|
+
@title = title
|
|
79
|
+
@side = side
|
|
80
|
+
@open = open
|
|
81
|
+
@options = options
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def html_options
|
|
85
|
+
@options.except(:class, :data)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def html_class
|
|
89
|
+
class_names(
|
|
90
|
+
# `hidden`/`open:flex` (not a bare `flex`) -- the dialog needs
|
|
91
|
+
# `display:flex` once shown (for the header/body/footer column
|
|
92
|
+
# layout), but a bare `flex` utility class is author-origin CSS
|
|
93
|
+
# that would permanently beat the user-agent stylesheet's
|
|
94
|
+
# `dialog:not([open]) { display: none }`, leaving the "closed"
|
|
95
|
+
# dialog fully rendered (and eating clicks) at all times. The
|
|
96
|
+
# `open:` variant (targeting the `[open]` attribute the browser
|
|
97
|
+
# itself adds/removes on showModal()/close()) keeps it hidden
|
|
98
|
+
# until actually shown, same as Modal gets for free by not
|
|
99
|
+
# setting `display` at all.
|
|
100
|
+
"m-0 hidden h-dvh max-h-none w-full flex-col bg-surface p-0 text-foreground shadow-panel open:flex backdrop:bg-backdrop sm:max-w-lg",
|
|
101
|
+
side_classes,
|
|
102
|
+
@options[:class]
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def data_attributes
|
|
107
|
+
attributes = (@options[:data] || {}).merge(controller: "atomic-view--drawer")
|
|
108
|
+
attributes["atomic-view--drawer-open-value"] = true if open
|
|
109
|
+
attributes
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def close_icon
|
|
113
|
+
icon("x-mark", variant: :mini, options: {class: "size-5"}).to_s.html_safe
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def side_classes
|
|
119
|
+
(side == :left) ? "inset-y-0 left-0 right-auto" : "inset-y-0 right-0 left-auto"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
data/lib/atomic_view/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: atomic_view
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joel Warrington
|
|
@@ -143,6 +143,7 @@ files:
|
|
|
143
143
|
- app/assets/javascripts/atomic_view/controllers/auto_submit_controller.js
|
|
144
144
|
- app/assets/javascripts/atomic_view/controllers/chip_controller.js
|
|
145
145
|
- app/assets/javascripts/atomic_view/controllers/command_palette_controller.js
|
|
146
|
+
- app/assets/javascripts/atomic_view/controllers/drawer_controller.js
|
|
146
147
|
- app/assets/javascripts/atomic_view/controllers/dropdown_controller.js
|
|
147
148
|
- app/assets/javascripts/atomic_view/controllers/gantt_controller.js
|
|
148
149
|
- app/assets/javascripts/atomic_view/controllers/hotkey_controller.js
|
|
@@ -200,6 +201,8 @@ files:
|
|
|
200
201
|
- lib/atomic_view/components/date_select_component.rb
|
|
201
202
|
- lib/atomic_view/components/datetime_local_field_component.rb
|
|
202
203
|
- lib/atomic_view/components/datetime_select_component.rb
|
|
204
|
+
- lib/atomic_view/components/drawer_component.html.erb
|
|
205
|
+
- lib/atomic_view/components/drawer_component.rb
|
|
203
206
|
- lib/atomic_view/components/dropdown_component.html.erb
|
|
204
207
|
- lib/atomic_view/components/dropdown_component.rb
|
|
205
208
|
- lib/atomic_view/components/email_field_component.rb
|