atomic_view 0.1.14 → 0.1.15
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/lib/atomic_view/components/command_palette_component/row_component.html.erb +19 -0
- data/lib/atomic_view/components/command_palette_component/row_component.rb +37 -0
- data/lib/atomic_view/components/command_palette_component/section_component.html.erb +7 -0
- data/lib/atomic_view/components/command_palette_component/section_component.rb +24 -0
- data/lib/atomic_view/components/command_palette_component.html.erb +3 -22
- data/lib/atomic_view/components/command_palette_component.rb +42 -28
- data/lib/atomic_view/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bada6883ff0ebcfb57bb0cf9f153c8c978bd32601b3371d0a5db2db02a57fab3
|
|
4
|
+
data.tar.gz: 59a99dd072921000d4dc906eafcc2d51b4638a7f78b625fd53e8c82dfe1017e6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2bd3d154f20a387d44c6fd2fbf30c6c00ca790ae8a4531d92c69291ed0a1f1c0cad1313a0807c9d576bec552e4faa297d3acab6be83feb7c14e4227440a228c6
|
|
7
|
+
data.tar.gz: 77657284d3f55f498dda813f30542b65077c979804ef0d64fed520162ab0e154c6b58a7719ef345d516e4d58f66e013adc6b839ec5b180570a74b9c1658f1bfd
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%= link_to(
|
|
2
|
+
href,
|
|
3
|
+
class: "flex items-center justify-between gap-3 px-3 py-1.5 text-sm text-foreground hover:bg-offset",
|
|
4
|
+
data: data_attributes
|
|
5
|
+
) do %>
|
|
6
|
+
<span class="flex items-center gap-2">
|
|
7
|
+
<% if icon_name.present? %>
|
|
8
|
+
<%= icon(icon_name, options: {class: "size-4 shrink-0 text-muted-foreground"}).to_s.html_safe %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<%= label %>
|
|
11
|
+
</span>
|
|
12
|
+
<% if hint.present? %>
|
|
13
|
+
<span class="inline-flex items-center gap-0.5">
|
|
14
|
+
<% Array(hint).each do |key| %>
|
|
15
|
+
<%= render(AtomicView::Components::KbdComponent.new) { key } %>
|
|
16
|
+
<% end %>
|
|
17
|
+
</span>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AtomicView
|
|
4
|
+
module Components
|
|
5
|
+
class CommandPaletteComponent
|
|
6
|
+
# A single CommandPalette result: a link carrying the JS hooks --
|
|
7
|
+
# `[data-atomic-view--command-palette-target="row"]` and
|
|
8
|
+
# `data-search-text` -- the filter/keyboard-nav controller relies on.
|
|
9
|
+
# Rendered via `CommandPaletteComponent#with_section`'s `results:`
|
|
10
|
+
# sugar, or `SectionComponent#with_row` directly -- see
|
|
11
|
+
# `CommandPaletteComponent`'s class docs.
|
|
12
|
+
#
|
|
13
|
+
# `icon:`, when given, renders a Heroicon before the label -- same
|
|
14
|
+
# `icon:` convention as `NavigationTreeComponent::ItemComponent`.
|
|
15
|
+
class RowComponent < AtomicView::Component
|
|
16
|
+
attr_reader :label, :href, :hint, :icon_name
|
|
17
|
+
|
|
18
|
+
def initialize(label:, href: "#", hint: nil, search_text: nil, icon: nil, **options)
|
|
19
|
+
super()
|
|
20
|
+
@label = label
|
|
21
|
+
@href = href
|
|
22
|
+
@hint = hint
|
|
23
|
+
@search_text = search_text
|
|
24
|
+
@icon_name = icon
|
|
25
|
+
@options = options
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def data_attributes
|
|
29
|
+
(@options[:data] || {}).merge(
|
|
30
|
+
"atomic-view--command-palette-target" => "row",
|
|
31
|
+
"search_text" => (@search_text || label).to_s.downcase
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AtomicView
|
|
4
|
+
module Components
|
|
5
|
+
class CommandPaletteComponent
|
|
6
|
+
# A labeled group of `RowComponent` results within a CommandPalette.
|
|
7
|
+
# Rendered via `CommandPaletteComponent#with_section`, or standalone
|
|
8
|
+
# (e.g. from a Turbo Stream response updating `results_id`) -- see
|
|
9
|
+
# `CommandPaletteComponent`'s class docs.
|
|
10
|
+
class SectionComponent < AtomicView::Component
|
|
11
|
+
renders_many :rows, "AtomicView::Components::CommandPaletteComponent::RowComponent"
|
|
12
|
+
|
|
13
|
+
attr_reader :label
|
|
14
|
+
|
|
15
|
+
def initialize(label:, results: [], **options)
|
|
16
|
+
super()
|
|
17
|
+
@label = label
|
|
18
|
+
@options = options
|
|
19
|
+
results.each { |result| with_row(**result) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -4,32 +4,13 @@
|
|
|
4
4
|
placeholder: placeholder,
|
|
5
5
|
autocomplete: "off",
|
|
6
6
|
autofocus: true,
|
|
7
|
-
class: "w-full border-0 bg-transparent px-3 py-2 text-
|
|
7
|
+
class: "w-full border-0 bg-transparent px-3 py-2 text-sm text-foreground placeholder:text-placeholder focus:outline-none focus:ring-0",
|
|
8
8
|
data: input_data_attributes
|
|
9
9
|
) %>
|
|
10
10
|
|
|
11
|
-
<div class="max-h-80 overflow-y-auto border-t border-border py-2">
|
|
11
|
+
<div id="<%= results_id %>" class="max-h-80 overflow-y-auto border-t border-border py-2">
|
|
12
12
|
<% sections.each do |section| %>
|
|
13
|
-
|
|
14
|
-
<p class="px-3 pb-1 pt-2 text-xs uppercase text-muted-foreground"><%= section[:label] %></p>
|
|
15
|
-
|
|
16
|
-
<% section[:results].each do |result| %>
|
|
17
|
-
<%= link_to(
|
|
18
|
-
result[:href] || "#",
|
|
19
|
-
class: "flex items-center justify-between gap-3 px-3 py-1.5 text-sm text-foreground hover:bg-offset",
|
|
20
|
-
data: row_data_attributes(result)
|
|
21
|
-
) do %>
|
|
22
|
-
<span><%= result[:label] %></span>
|
|
23
|
-
<% if result[:hint].present? %>
|
|
24
|
-
<span class="inline-flex items-center gap-0.5">
|
|
25
|
-
<% Array(result[:hint]).each do |key| %>
|
|
26
|
-
<%= render(AtomicView::Components::KbdComponent.new) { key } %>
|
|
27
|
-
<% end %>
|
|
28
|
-
</span>
|
|
29
|
-
<% end %>
|
|
30
|
-
<% end %>
|
|
31
|
-
<% end %>
|
|
32
|
-
</div>
|
|
13
|
+
<%= section %>
|
|
33
14
|
<% end %>
|
|
34
15
|
</div>
|
|
35
16
|
|
|
@@ -24,15 +24,31 @@ module AtomicView
|
|
|
24
24
|
#
|
|
25
25
|
# `SegmentedControlComponent` and `PaginationComponent` already use this
|
|
26
26
|
# array-of-hashes shape for link collections, and it's the better fit
|
|
27
|
-
# here (over slots, `DropdownComponent`'s approach)
|
|
28
|
-
# same two JS hooks -- a
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
27
|
+
# here (over slots, `DropdownComponent`'s approach) as the *default*
|
|
28
|
+
# entry point: every row needs the same two JS hooks -- a
|
|
29
|
+
# `[data-atomic-view--command-palette-target="row"]` marker and a
|
|
30
|
+
# `data-search-text` attribute -- generated consistently so the
|
|
31
|
+
# filter/keyboard-nav controller can rely on them, which a free-form
|
|
32
|
+
# slot would push onto every consumer to wire up by hand. The component
|
|
33
|
+
# stays agnostic about what a result *links to* (any `href` works -- a
|
|
34
|
+
# path, an external URL, a `turbo_frame`-aware route, etc.), which is
|
|
35
|
+
# the same kind of agnosticism `DropdownComponent` keeps about its menu
|
|
36
|
+
# content, just expressed as data instead of markup.
|
|
37
|
+
#
|
|
38
|
+
# Under the hood, `sections:`/`results:` are sugar over real
|
|
39
|
+
# `SectionComponent`/`RowComponent` slots -- `with_section`/`with_row`
|
|
40
|
+
# accept the same keyword shape:
|
|
41
|
+
#
|
|
42
|
+
# render(CommandPaletteComponent.new(id: "x")) do |palette|
|
|
43
|
+
# palette.with_section(label: "Campers") do |section|
|
|
44
|
+
# section.with_row(label: "Joel", href: "/campers/1")
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# Because they're real components, `SectionComponent` and `RowComponent`
|
|
49
|
+
# are independently renderable outside the dialog -- e.g. from a Turbo
|
|
50
|
+
# Stream response that re-renders `results_id` while server-side
|
|
51
|
+
# filtering, without forking this markup. See those classes' docs.
|
|
36
52
|
#
|
|
37
53
|
# `result[:hint]`, when given, renders as a `<kbd>` -- e.g. a keyboard
|
|
38
54
|
# shortcut like "G D" -- next to the result label.
|
|
@@ -43,15 +59,21 @@ module AtomicView
|
|
|
43
59
|
# structured data.
|
|
44
60
|
class CommandPaletteComponent < AtomicView::Component
|
|
45
61
|
renders_one :footer
|
|
62
|
+
renders_many :sections, "AtomicView::Components::CommandPaletteComponent::SectionComponent"
|
|
46
63
|
|
|
47
|
-
attr_reader :id, :placeholder
|
|
64
|
+
attr_reader :id, :placeholder
|
|
48
65
|
|
|
49
|
-
def
|
|
66
|
+
def self.results_id(id)
|
|
67
|
+
"#{id}-results"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def initialize(id:, sections: [], placeholder: "Search...", input_data: {}, **options)
|
|
50
71
|
super()
|
|
51
72
|
@id = id
|
|
52
|
-
@sections = sections
|
|
53
73
|
@placeholder = placeholder
|
|
74
|
+
@input_data = input_data
|
|
54
75
|
@options = options
|
|
76
|
+
sections.each { |section| with_section(**section) }
|
|
55
77
|
end
|
|
56
78
|
|
|
57
79
|
def html_options
|
|
@@ -62,26 +84,18 @@ module AtomicView
|
|
|
62
84
|
class_names("mx-auto mt-[15vh] w-full max-w-lg overflow-hidden rounded-card bg-surface p-0 text-foreground shadow-panel backdrop:bg-backdrop", @options[:class])
|
|
63
85
|
end
|
|
64
86
|
|
|
65
|
-
def
|
|
66
|
-
|
|
87
|
+
def results_id
|
|
88
|
+
self.class.results_id(id)
|
|
67
89
|
end
|
|
68
90
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
:action => "input->atomic-view--command-palette#filter keydown->atomic-view--command-palette#navigate"
|
|
73
|
-
}
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def row_data_attributes(result)
|
|
77
|
-
{
|
|
78
|
-
"atomic-view--command-palette-target" => "row",
|
|
79
|
-
:search_text => search_text_for(result)
|
|
80
|
-
}
|
|
91
|
+
def data_attributes
|
|
92
|
+
controllers = ["atomic-view--command-palette", @options.dig(:data, :controller)].compact.join(" ")
|
|
93
|
+
(@options[:data] || {}).except(:controller).merge(controller: controllers)
|
|
81
94
|
end
|
|
82
95
|
|
|
83
|
-
def
|
|
84
|
-
|
|
96
|
+
def input_data_attributes
|
|
97
|
+
actions = ["input->atomic-view--command-palette#filter", "keydown->atomic-view--command-palette#navigate", @input_data[:action]].compact.join(" ")
|
|
98
|
+
{"atomic-view--command-palette-target" => "input", **@input_data.except(:action), "action" => actions}
|
|
85
99
|
end
|
|
86
100
|
end
|
|
87
101
|
end
|
data/lib/atomic_view/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: atomic_view
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joel Warrington
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: zeitwerk
|
|
@@ -181,6 +181,10 @@ files:
|
|
|
181
181
|
- lib/atomic_view/components/color_field_component.rb
|
|
182
182
|
- lib/atomic_view/components/command_palette_component.html.erb
|
|
183
183
|
- lib/atomic_view/components/command_palette_component.rb
|
|
184
|
+
- lib/atomic_view/components/command_palette_component/row_component.html.erb
|
|
185
|
+
- lib/atomic_view/components/command_palette_component/row_component.rb
|
|
186
|
+
- lib/atomic_view/components/command_palette_component/section_component.html.erb
|
|
187
|
+
- lib/atomic_view/components/command_palette_component/section_component.rb
|
|
184
188
|
- lib/atomic_view/components/concerns/button_variants.rb
|
|
185
189
|
- lib/atomic_view/components/concerns/field_chrome.rb
|
|
186
190
|
- lib/atomic_view/components/concerns/section_support.rb
|