atomic_view 0.4.0 → 0.4.1

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: 75aad701c9a9b4598ba4a086f8a3c1c37faaac0bbf23f4ef2927b4d667364f1b
4
- data.tar.gz: 1a63769c7c94188b829105777227267be43df6480e6b2f864a5c7be4e831a179
3
+ metadata.gz: f37c405116dc18595f21f9f7b3a45223edef67c921c991a7c1523a93b594cf39
4
+ data.tar.gz: 3ce7216ee4b6327f57dec679651d507ba799023f4b2eb3b865ef61bfa3781c1a
5
5
  SHA512:
6
- metadata.gz: ba9732b3bf6ed19951e2c29ac2feafa7af299679135645bf28650808fd9ff6a6c9e9c3d994be9d23319b69a4679e4e7a8d67a73ae46603a963544a11430e0579
7
- data.tar.gz: 10aff2eb5c66efb09918961c87cc708941cd30721c769cd3c2402082d09898098b32389cdfffe52519bbd3bedfa34bb8f2f265694cb0ed5226148abcfcbcd058
6
+ metadata.gz: a550e6749f5c77da2336b475b69d59d24d09be6e5dfb321bc9c2e73af3968d1b308e0d26962e728f8f0c5dfcd4a341e0d6e63a753ec5986943deba90db4223c7
7
+ data.tar.gz: 05477662ea7b03cd022ca121c0c1b30967cdf99feae9166209aabf64f531ef1902b3a5c746e76dca92cc107e2a7caa05705a36f91f2fd9bdb3e941270eb49e07
@@ -2,5 +2,5 @@
2
2
  <% if linked? %>
3
3
  <%= link_to "", path, class: "absolute inset-0", tabindex: (first ? 0 : -1), "aria-hidden": (first ? nil : "true"), "aria-label": (first ? label : nil), data: {row_link: true} %>
4
4
  <% end %>
5
- <%= tag.span(content, class: "relative") %>
5
+ <%= tag.span(content, class: content_class) %>
6
6
  <% end %>
@@ -10,21 +10,25 @@ module AtomicView
10
10
  # design. `first` and `clickable` are set by RowComponent's `cells`
11
11
  # slot from the row itself, not passed by the caller.
12
12
  #
13
- # Visible content renders as a normal sibling of the (empty,
14
- # transparent) stretched `<a>`, wrapped in its own `position:
15
- # relative` span so a real link nested inside a cell's content (e.g.
16
- # a second, more specific action) still stays clickable above the
17
- # row-wide overlay.
13
+ # Visible content renders as a sibling of the (empty, transparent)
14
+ # stretched `<a>`, wrapped in a `pointer-events-none` span so plain
15
+ # content (the common case -- a name, a badge) doesn't sit in the
16
+ # way of clicks meant for the row-wide overlay underneath it. Pass
17
+ # `interactive: true` on a cell that renders a real, more specific
18
+ # link/button of its own (e.g. an "Edit" action) to flip that span
19
+ # back to `pointer-events-auto`, so that cell's own content takes
20
+ # the click instead of falling through to the row link.
18
21
  class CellComponent < AtomicView::Component
19
- attr_reader :path, :label, :first, :align, :clickable
22
+ attr_reader :path, :label, :first, :align, :clickable, :interactive
20
23
 
21
- def initialize(path:, label:, first:, align: :left, clickable: true, **options)
24
+ def initialize(path:, label:, first:, align: :left, clickable: true, interactive: false, **options)
22
25
  super()
23
26
  @path = path
24
27
  @label = label
25
28
  @first = first
26
29
  @align = align
27
30
  @clickable = clickable
31
+ @interactive = interactive
28
32
  @options = options
29
33
  end
30
34
 
@@ -40,6 +44,10 @@ module AtomicView
40
44
  @options.except(:class)
41
45
  end
42
46
 
47
+ def content_class
48
+ interactive ? "relative pointer-events-auto" : "relative pointer-events-none"
49
+ end
50
+
43
51
  private
44
52
 
45
53
  def base_classes
@@ -24,32 +24,37 @@ module AtomicView
24
24
  #
25
25
  # `with_column` is an opt-in alternative to hand-building the <thead>:
26
26
  # give it at least one column and TableComponent renders the header row
27
- # itself, from `<th>`s built by TableComponent::ColumnComponent -- you
28
- # still own `<tbody>` content (typically TableComponent::RowComponent
29
- # rows) in the block, same as before:
27
+ # itself, from `<th>`s built by TableComponent::ColumnComponent -- and
28
+ # wraps the block's return value in a <tbody> for you too, so the block
29
+ # only has to render rows (typically TableComponent::RowComponent),
30
+ # same as it would for a turbo_stream append/update elsewhere:
30
31
  #
31
32
  # <%= render AtomicView::Components::TableComponent.new(model: Booking) do |table| %>
32
33
  # <% table.with_column(attribute: :camper_name) %>
33
34
  # <% table.with_column(attribute: :status, align: :center) %>
34
35
  # <% table.with_column(label: "") %>
35
36
  #
36
- # <tbody>
37
- # <% @bookings.each do |booking| %>
38
- # <%= render(AtomicView::Components::TableComponent::RowComponent.new(record: booking, label: booking.camper_name, path: booking_path(booking))) do |row| %>
39
- # <% row.with_cell { booking.camper_name } %>
40
- # <% row.with_cell(align: :center) { render(BadgeComponent.new) { booking.status } } %>
41
- # <% row.with_cell { render(LinkComponent.new(edit_booking_path(booking), variant: :outline)) { "Edit" } } %>
42
- # <% end %>
37
+ # <% @bookings.each do |booking| %>
38
+ # <%= render(AtomicView::Components::TableComponent::RowComponent.new(record: booking, label: booking.camper_name, path: booking_path(booking))) do |row| %>
39
+ # <% row.with_cell { booking.camper_name } %>
40
+ # <% row.with_cell(align: :center) { render(BadgeComponent.new) { booking.status } } %>
41
+ # <% row.with_cell(interactive: true) { render(LinkComponent.new(edit_booking_path(booking), variant: :outline)) { "Edit" } } %>
43
42
  # <% end %>
44
- # </tbody>
43
+ # <% end %>
45
44
  # <% end %>
46
45
  #
46
+ # The <tbody>'s `id` defaults to `model.model_name.plural` (e.g.
47
+ # "bookings") so a turbo_stream response can target it directly with
48
+ # no extra bookkeeping -- pass `body_id:` to override it (a nested
49
+ # resource sharing a differently-named frame, say).
50
+ #
47
51
  # A table with no columns given renders exactly as before -- `with_column`
48
- # is opt-in, not a second required API. `model:` is passed through to
49
- # each column so `with_column(attribute: :camper_name)` can default its
50
- # label via `model.human_attribute_name(:camper_name)`; a column with no
51
- # backing attribute (like the blank "" header above, for an actions
52
- # column) just passes `label:` directly instead.
52
+ # is opt-in, not a second required API, and in that raw mode you still
53
+ # own the whole `<thead>`/`<tbody>` yourself. `model:` is passed through
54
+ # to each column so `with_column(attribute: :camper_name)` can default
55
+ # its label via `model.human_attribute_name(:camper_name)`; a column
56
+ # with no backing attribute (like the blank "" header above, for an
57
+ # actions column) just passes `label:` directly instead.
53
58
  class TableComponent < AtomicView::Component
54
59
  HEAD_ROW_CLASSES = "text-left text-xs font-semibold uppercase tracking-wide text-muted-foreground border-b border-border"
55
60
  BODY_ROW_CLASSES = "border-b border-border hover:bg-offset"
@@ -58,15 +63,20 @@ module AtomicView
58
63
 
59
64
  attr_reader :model
60
65
 
61
- def initialize(model: nil, **options)
66
+ def initialize(model: nil, body_id: nil, **options)
62
67
  super()
63
68
  @model = model
69
+ @body_id = body_id
64
70
  @options = options
65
71
  end
66
72
 
67
73
  def call
68
74
  tag.table(**@options.except(:class), class: class_names(base_classes, @options[:class])) do
69
- safe_join([columns_header, content].compact)
75
+ if columns?
76
+ safe_join([columns_header, tag.tbody(content, id: body_id)])
77
+ else
78
+ content
79
+ end
70
80
  end
71
81
  end
72
82
 
@@ -89,6 +99,10 @@ module AtomicView
89
99
  columns.any?
90
100
  end
91
101
 
102
+ def body_id
103
+ @body_id || model&.model_name&.plural
104
+ end
105
+
92
106
  def base_classes
93
107
  "w-full text-sm"
94
108
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtomicView
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.1"
5
5
  end
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.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Warrington