avo 4.2.5 → 4.2.6
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/Gemfile.lock +35 -35
- data/app/assets/builds/avo/application.css +19 -0
- data/app/assets/stylesheets/css/components/tooltip.css +21 -0
- data/app/components/avo/field_wrapper_component.html.erb +15 -10
- data/app/components/avo/field_wrapper_component.rb +11 -0
- data/app/components/avo/index/field_wrapper_component.html.erb +3 -3
- data/app/components/avo/index/field_wrapper_component.rb +2 -0
- data/app/controllers/avo/associations_controller.rb +4 -4
- data/app/controllers/avo/base_controller.rb +8 -3
- data/app/views/avo/partials/_table_header.html.erb +3 -2
- data/app/views/avo/partials/_table_header_label.html.erb +10 -0
- data/lib/avo/concerns/has_field_tooltips.rb +38 -0
- data/lib/avo/fields/area_field.rb +4 -0
- data/lib/avo/fields/base_field.rb +25 -0
- data/lib/avo/fields/code_field.rb +4 -0
- data/lib/avo/fields/easy_mde_field.rb +4 -0
- data/lib/avo/fields/files_field.rb +4 -0
- data/lib/avo/fields/location_field.rb +4 -0
- data/lib/avo/fields/progress_bar_field.rb +4 -0
- data/lib/avo/resources/array_resource.rb +23 -15
- data/lib/avo/resources/base.rb +3 -0
- data/lib/avo/skills/avo-associations/SKILL.md +1 -1
- data/lib/avo/skills/avo-fields/SKILL.md +1 -0
- data/lib/avo/skills/docs-index.json +2 -2
- data/lib/avo/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6a64a8878e0c42cef0fcbe360cdf217635be5cdcca396cf180888e24a034ab8d
|
|
4
|
+
data.tar.gz: f05b38be082e6fbe6cc9c5f5c85c87452108d8539dd284d320bd3bd09c9fb92d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 19222c1d66ac795e3e0fa668ca5a903604dbe3ae412e2aa528bb8afe6e753cf79abfdd3f3d84f0cfaaecca6e4847ce56f692a14a7abbec498e524c5bbe63009a
|
|
7
|
+
data.tar.gz: 294e34314e882e5cc255f34e9060f683e384fa888795083275566546d305955d7d090e7293471e1588ea4c0f06a9afb8d4b3085c31e611b98c2ee16bad037d2c
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
avo (4.2.
|
|
4
|
+
avo (4.2.6)
|
|
5
5
|
actionview (>= 6.1)
|
|
6
6
|
active_link_to
|
|
7
7
|
activerecord (>= 6.1)
|
|
@@ -120,7 +120,7 @@ GEM
|
|
|
120
120
|
prism
|
|
121
121
|
addressable (2.9.0)
|
|
122
122
|
public_suffix (>= 2.0.2, < 8.0)
|
|
123
|
-
amazing_print (
|
|
123
|
+
amazing_print (3.0.0)
|
|
124
124
|
annotate (2.6.5)
|
|
125
125
|
activerecord (>= 2.3.0)
|
|
126
126
|
rake (>= 0.8.7)
|
|
@@ -139,8 +139,8 @@ GEM
|
|
|
139
139
|
avo-record_link_field (0.0.2)
|
|
140
140
|
avo-rhino_field (4.1.0)
|
|
141
141
|
aws-eventstream (1.4.0)
|
|
142
|
-
aws-partitions (1.
|
|
143
|
-
aws-sdk-core (3.
|
|
142
|
+
aws-partitions (1.1287.0)
|
|
143
|
+
aws-sdk-core (3.257.0)
|
|
144
144
|
aws-eventstream (~> 1, >= 1.3.0)
|
|
145
145
|
aws-partitions (~> 1, >= 1.992.0)
|
|
146
146
|
aws-sigv4 (~> 1.9)
|
|
@@ -148,11 +148,12 @@ GEM
|
|
|
148
148
|
bigdecimal
|
|
149
149
|
jmespath (~> 1, >= 1.6.1)
|
|
150
150
|
logger
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
rexml (~> 3.4, >= 3.4.2)
|
|
152
|
+
aws-sdk-kms (1.132.0)
|
|
153
|
+
aws-sdk-core (~> 3, >= 3.256.0)
|
|
153
154
|
aws-sigv4 (~> 1.5)
|
|
154
|
-
aws-sdk-s3 (1.
|
|
155
|
-
aws-sdk-core (~> 3, >= 3.
|
|
155
|
+
aws-sdk-s3 (1.232.1)
|
|
156
|
+
aws-sdk-core (~> 3, >= 3.256.0)
|
|
156
157
|
aws-sdk-kms (~> 1)
|
|
157
158
|
aws-sigv4 (~> 1.5)
|
|
158
159
|
aws-sigv4 (1.12.1)
|
|
@@ -179,9 +180,9 @@ GEM
|
|
|
179
180
|
erubi (~> 1.4)
|
|
180
181
|
parser (>= 2.4)
|
|
181
182
|
smart_properties
|
|
182
|
-
bigdecimal (4.1.
|
|
183
|
+
bigdecimal (4.1.3)
|
|
183
184
|
bindex (0.8.1)
|
|
184
|
-
bootsnap (1.
|
|
185
|
+
bootsnap (1.26.0)
|
|
185
186
|
msgpack (~> 1.5)
|
|
186
187
|
builder (3.3.0)
|
|
187
188
|
bump (0.10.0)
|
|
@@ -218,15 +219,16 @@ GEM
|
|
|
218
219
|
bigdecimal
|
|
219
220
|
rexml
|
|
220
221
|
crass (1.0.7)
|
|
221
|
-
css_parser (3.
|
|
222
|
+
css_parser (3.2.0)
|
|
222
223
|
addressable
|
|
224
|
+
base64
|
|
223
225
|
ssrf_filter (~> 1.5)
|
|
224
226
|
cssbundling-rails (1.4.3)
|
|
225
227
|
railties (>= 6.0.0)
|
|
226
228
|
csv (3.3.6)
|
|
227
|
-
cuprite (0.
|
|
229
|
+
cuprite (0.18)
|
|
228
230
|
capybara (~> 3.0)
|
|
229
|
-
ferrum (~> 0.
|
|
231
|
+
ferrum (~> 0.18.0)
|
|
230
232
|
danger (9.6.1)
|
|
231
233
|
base64 (~> 0.2)
|
|
232
234
|
claide (~> 1.0)
|
|
@@ -326,21 +328,20 @@ GEM
|
|
|
326
328
|
railties (>= 6.1.0)
|
|
327
329
|
faker (3.8.0)
|
|
328
330
|
i18n (>= 1.8.11, < 2)
|
|
329
|
-
faraday (2.14.
|
|
331
|
+
faraday (2.14.4)
|
|
330
332
|
faraday-net_http (>= 2.0, < 3.5)
|
|
331
333
|
json
|
|
332
334
|
logger
|
|
333
|
-
faraday-http-cache (2.
|
|
335
|
+
faraday-http-cache (2.8.0)
|
|
334
336
|
faraday (>= 0.8)
|
|
335
337
|
faraday-multipart (1.2.0)
|
|
336
338
|
multipart-post (~> 2.0)
|
|
337
339
|
faraday-net_http (3.4.4)
|
|
338
340
|
net-http (~> 0.5)
|
|
339
|
-
ferrum (0.
|
|
341
|
+
ferrum (0.18.0)
|
|
340
342
|
addressable (~> 2.5)
|
|
341
343
|
base64 (~> 0.2)
|
|
342
344
|
concurrent-ruby (~> 1.1)
|
|
343
|
-
webrick (~> 1.7)
|
|
344
345
|
websocket-driver (~> 0.7)
|
|
345
346
|
ffi (1.17.4)
|
|
346
347
|
ffi (1.17.4-arm64-darwin)
|
|
@@ -362,7 +363,7 @@ GEM
|
|
|
362
363
|
gem-release (2.2.4)
|
|
363
364
|
get_process_mem (0.2.7)
|
|
364
365
|
ffi (~> 1.0)
|
|
365
|
-
git (5.
|
|
366
|
+
git (5.6.0)
|
|
366
367
|
activesupport (>= 5.0)
|
|
367
368
|
addressable (~> 2.8)
|
|
368
369
|
process_executer (~> 4.1)
|
|
@@ -409,7 +410,7 @@ GEM
|
|
|
409
410
|
inline_svg (1.10.0)
|
|
410
411
|
activesupport (>= 3.0)
|
|
411
412
|
nokogiri (>= 1.6)
|
|
412
|
-
io-console (0.9.
|
|
413
|
+
io-console (0.9.4)
|
|
413
414
|
irb (1.18.0)
|
|
414
415
|
pp (>= 0.6.0)
|
|
415
416
|
prism (>= 1.3.0)
|
|
@@ -431,8 +432,8 @@ GEM
|
|
|
431
432
|
addressable (~> 2.8)
|
|
432
433
|
childprocess (~> 5.0)
|
|
433
434
|
logger (~> 1.6)
|
|
434
|
-
lexxy (0.9.
|
|
435
|
-
|
|
435
|
+
lexxy (0.9.32)
|
|
436
|
+
railties (>= 8.0.2)
|
|
436
437
|
lint_roller (1.1.0)
|
|
437
438
|
listen (3.10.0)
|
|
438
439
|
logger
|
|
@@ -485,17 +486,17 @@ GEM
|
|
|
485
486
|
monetize (~> 1.9)
|
|
486
487
|
money (~> 6.13)
|
|
487
488
|
railties (>= 3.0)
|
|
488
|
-
msgpack (1.8.
|
|
489
|
+
msgpack (1.8.5)
|
|
489
490
|
multipart-post (2.4.1)
|
|
490
491
|
nap (1.1.0)
|
|
491
492
|
net-http (0.9.1)
|
|
492
493
|
uri (>= 0.11.1)
|
|
493
|
-
net-imap (0.6.
|
|
494
|
+
net-imap (0.6.7)
|
|
494
495
|
date
|
|
495
496
|
net-protocol
|
|
496
497
|
net-pop (0.1.2)
|
|
497
498
|
net-protocol
|
|
498
|
-
net-protocol (0.
|
|
499
|
+
net-protocol (0.4.0)
|
|
499
500
|
timeout
|
|
500
501
|
net-smtp (0.5.1)
|
|
501
502
|
net-protocol
|
|
@@ -517,8 +518,8 @@ GEM
|
|
|
517
518
|
json
|
|
518
519
|
uri
|
|
519
520
|
yaml
|
|
520
|
-
parallel (2.
|
|
521
|
-
parallel_tests (5.
|
|
521
|
+
parallel (2.2.0)
|
|
522
|
+
parallel_tests (5.8.0)
|
|
522
523
|
parallel
|
|
523
524
|
parser (3.3.12.0)
|
|
524
525
|
ast (~> 2.4.1)
|
|
@@ -707,19 +708,19 @@ GEM
|
|
|
707
708
|
simplecov (>= 0.22.0)
|
|
708
709
|
tty-which (~> 0.5.0)
|
|
709
710
|
virtus (~> 2.0)
|
|
710
|
-
rubyzip (3.
|
|
711
|
+
rubyzip (3.7.0)
|
|
711
712
|
sawyer (0.9.3)
|
|
712
713
|
addressable (>= 2.3.5)
|
|
713
714
|
faraday (>= 0.17.3, < 3)
|
|
714
715
|
securerandom (0.4.1)
|
|
715
|
-
selenium-webdriver (4.
|
|
716
|
+
selenium-webdriver (4.49.0)
|
|
716
717
|
base64 (~> 0.2)
|
|
717
718
|
logger (~> 1.4)
|
|
718
719
|
rexml (~> 3.2, >= 3.2.5)
|
|
719
720
|
rubyzip (>= 1.2.2, < 4.0)
|
|
720
721
|
websocket (~> 1.0)
|
|
721
|
-
sexp_processor (4.17.
|
|
722
|
-
simplecov (1.
|
|
722
|
+
sexp_processor (4.17.6)
|
|
723
|
+
simplecov (1.3.0)
|
|
723
724
|
simplecov-cobertura (4.0.0)
|
|
724
725
|
rexml
|
|
725
726
|
simplecov (~> 1.0)
|
|
@@ -754,7 +755,7 @@ GEM
|
|
|
754
755
|
spring (4.7.0)
|
|
755
756
|
spring-commands-rspec (1.0.4)
|
|
756
757
|
spring (>= 0.9.1)
|
|
757
|
-
ssrf_filter (1.
|
|
758
|
+
ssrf_filter (1.6.0)
|
|
758
759
|
standard (1.56.0)
|
|
759
760
|
language_server-protocol (~> 3.17.0.2)
|
|
760
761
|
lint_roller (~> 1.0)
|
|
@@ -789,9 +790,9 @@ GEM
|
|
|
789
790
|
tzinfo (2.0.6)
|
|
790
791
|
concurrent-ruby (~> 1.0)
|
|
791
792
|
unaccent (0.4.0)
|
|
792
|
-
unicode-display_width (3.
|
|
793
|
-
unicode-emoji (~> 4.
|
|
794
|
-
unicode-emoji (4.
|
|
793
|
+
unicode-display_width (3.3.0)
|
|
794
|
+
unicode-emoji (~> 4.3)
|
|
795
|
+
unicode-emoji (4.3.0)
|
|
795
796
|
uri (1.1.1)
|
|
796
797
|
useragent (0.16.11)
|
|
797
798
|
view_component (4.15.0)
|
|
@@ -812,7 +813,6 @@ GEM
|
|
|
812
813
|
addressable (>= 2.8.0)
|
|
813
814
|
crack (>= 0.3.2)
|
|
814
815
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
815
|
-
webrick (1.9.2)
|
|
816
816
|
websocket (1.2.11)
|
|
817
817
|
websocket-driver (0.8.2)
|
|
818
818
|
base64
|
|
@@ -4019,6 +4019,25 @@
|
|
|
4019
4019
|
.tippy-box[data-theme~='preview'] .tippy-arrow {
|
|
4020
4020
|
display: none;
|
|
4021
4021
|
}
|
|
4022
|
+
.tooltip-anchor {
|
|
4023
|
+
max-width: 100%;
|
|
4024
|
+
min-width: 0px;
|
|
4025
|
+
}
|
|
4026
|
+
.tooltip-anchor--inline {
|
|
4027
|
+
display: inline-block;
|
|
4028
|
+
}
|
|
4029
|
+
.tooltip-anchor--block {
|
|
4030
|
+
display: block;
|
|
4031
|
+
width: 100%;
|
|
4032
|
+
}
|
|
4033
|
+
.label-tooltip__icon {
|
|
4034
|
+
margin-inline-start: var(--spacing);
|
|
4035
|
+
display: inline-block;
|
|
4036
|
+
width: 1em;
|
|
4037
|
+
height: 1em;
|
|
4038
|
+
vertical-align: -0.125em;
|
|
4039
|
+
color: var(--color-content-secondary);
|
|
4040
|
+
}
|
|
4022
4041
|
tags.tagify {
|
|
4023
4042
|
--tags-border-color: var(--color-tertiary);
|
|
4024
4043
|
--tags-hover-border-color: var(--color-tertiary);
|
|
@@ -36,3 +36,24 @@
|
|
|
36
36
|
@apply hidden;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/* Anchors for the field `tooltip:` option. Inline hugs the value so the bubble
|
|
40
|
+
sits over a badge or a date rather than over the row; block keeps the width
|
|
41
|
+
of values that fill the row (editors, maps, progress bars). */
|
|
42
|
+
.tooltip-anchor {
|
|
43
|
+
@apply min-w-0 max-w-full;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.tooltip-anchor--inline {
|
|
47
|
+
@apply inline-block;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.tooltip-anchor--block {
|
|
51
|
+
@apply block w-full;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* The cue that marks a label as hoverable, for the `label_tooltip:` option.
|
|
55
|
+
Sized off the label's own text so it fits the Show label and the smaller
|
|
56
|
+
Index header alike. */
|
|
57
|
+
.label-tooltip__icon {
|
|
58
|
+
@apply inline-block size-[1em] ms-1 align-[-0.125em] text-content-secondary;
|
|
59
|
+
}
|
|
@@ -7,17 +7,20 @@
|
|
|
7
7
|
class: label_classes,
|
|
8
8
|
style: label_style,
|
|
9
9
|
data: label_data do %>
|
|
10
|
-
|
|
10
|
+
<%= content_tag :span, class: label_text_classes, **tooltip_attributes(label_tooltip) do %>
|
|
11
11
|
<% if @form.present? %>
|
|
12
12
|
<%= @form.label label_for, label %>
|
|
13
13
|
<% else %>
|
|
14
14
|
<%= @field.name %>
|
|
15
15
|
<% end %>
|
|
16
16
|
<% if on_edit? && @field.is_required? %> <span class="text-danger-content ms-1">*</span> <% end %>
|
|
17
|
+
<% if label_tooltip.present? %>
|
|
18
|
+
<%= helpers.svg "tabler/outline/info-circle", class: "label-tooltip__icon" %>
|
|
19
|
+
<% end %>
|
|
17
20
|
<% if label_help.present? %>
|
|
18
21
|
<div class="field-wrapper__label-help"><%== label_help %></div>
|
|
19
22
|
<% end %>
|
|
20
|
-
|
|
23
|
+
<% end %>
|
|
21
24
|
<% end %>
|
|
22
25
|
<%= content_tag :div,
|
|
23
26
|
class: content_classes,
|
|
@@ -26,23 +29,25 @@
|
|
|
26
29
|
<%= tag.div class: "field-wrapper__content-wrapper" do %>
|
|
27
30
|
<% if on_show? %>
|
|
28
31
|
<div class="flex flex-row items-center group gap-x-1 group/clipboard w-full">
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
<%= value_tooltip_anchor(block: block_tooltip_anchor?) do %>
|
|
33
|
+
<% if render_dash? %>
|
|
34
|
+
—
|
|
35
|
+
<% elsif collapsable? %>
|
|
36
|
+
<%= render Avo::Fields::Common::CollapsableContentComponent.new do %>
|
|
37
|
+
<%= content %>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% else %>
|
|
33
40
|
<%= content %>
|
|
34
41
|
<% end %>
|
|
35
|
-
<% else %>
|
|
36
|
-
<%= content %>
|
|
37
42
|
<% end %>
|
|
38
43
|
<% if @field.copyable %>
|
|
39
44
|
<%= render Avo::ClipboardComponent.new(value: @field.value) %>
|
|
40
45
|
<% end %>
|
|
41
46
|
</div>
|
|
42
47
|
<% elsif on_edit? %>
|
|
43
|
-
|
|
48
|
+
<%= content_tag :div, class: "field-wrapper__input", **tooltip_attributes(tooltip) do %>
|
|
44
49
|
<%= content %>
|
|
45
|
-
|
|
50
|
+
<% end %>
|
|
46
51
|
<% if @field.record_errors.include?(@field.id) %>
|
|
47
52
|
<div class="field-wrapper__error"><%= @field.record_errors.full_messages_for(@field.id).to_sentence %></div>
|
|
48
53
|
<% end %>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
class Avo::FieldWrapperComponent < Avo::BaseComponent
|
|
4
4
|
include Avo::Concerns::HasResourceStimulusControllers
|
|
5
|
+
include Avo::Concerns::HasFieldTooltips
|
|
5
6
|
|
|
6
7
|
prop :collapsable, default: false
|
|
7
8
|
prop :dash_if_blank, default: true
|
|
@@ -90,6 +91,16 @@ class Avo::FieldWrapperComponent < Avo::BaseComponent
|
|
|
90
91
|
Avo::ExecutionContext.new(target: @label_help || @field.label_help, record: record, resource: @resource, view: @view).handle
|
|
91
92
|
end
|
|
92
93
|
|
|
94
|
+
def label_text_classes
|
|
95
|
+
class_names("self-center", "label-tooltip": label_tooltip.present?)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Collapsable content fills the row like a block-shaped field does, so its
|
|
99
|
+
# anchor keeps that width too.
|
|
100
|
+
def block_tooltip_anchor?
|
|
101
|
+
collapsable? || @field.tooltip_anchor == :block
|
|
102
|
+
end
|
|
103
|
+
|
|
93
104
|
def record
|
|
94
105
|
@resource.present? ? @resource.record : nil
|
|
95
106
|
end
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
},
|
|
15
15
|
style: @field.get_html(:style, view: @view, element: :wrapper) do %>
|
|
16
16
|
<% if render_dash? %>
|
|
17
|
-
—
|
|
17
|
+
<%= value_tooltip_anchor { "—" } %>
|
|
18
18
|
<% else %>
|
|
19
19
|
<% if @center_content %>
|
|
20
20
|
<div class="flex items-center justify-center">
|
|
21
|
-
<%= content %>
|
|
21
|
+
<%= value_tooltip_anchor(block: @field.tooltip_anchor == :block) { content } %>
|
|
22
22
|
</div>
|
|
23
23
|
<% else %>
|
|
24
|
-
<%= content %>
|
|
24
|
+
<%= value_tooltip_anchor(block: @field.tooltip_anchor == :block) { content } %>
|
|
25
25
|
<% end %>
|
|
26
26
|
<% if @field.copyable %>
|
|
27
27
|
<%= render Avo::ClipboardComponent.new(value: @field.value) %>
|
|
@@ -446,9 +446,9 @@ module Avo
|
|
|
446
446
|
page_key = "#{pagination_key}.page"
|
|
447
447
|
|
|
448
448
|
@index_params[:page] = if Avo.configuration.session_persistence_enabled?
|
|
449
|
-
session[page_key] = params[:page] || session[page_key]
|
|
449
|
+
session[page_key] = positive_integer_or(params[:page] || session[page_key], 1)
|
|
450
450
|
else
|
|
451
|
-
params[:page]
|
|
451
|
+
positive_integer_or(params[:page], 1)
|
|
452
452
|
end
|
|
453
453
|
end
|
|
454
454
|
|
|
@@ -457,9 +457,9 @@ module Avo
|
|
|
457
457
|
per_page_key = "#{pagination_key}.per_page"
|
|
458
458
|
|
|
459
459
|
@index_params[:per_page] = if Avo.configuration.session_persistence_enabled?
|
|
460
|
-
session[per_page_key] = params[:per_page] || session[per_page_key]
|
|
460
|
+
session[per_page_key] = positive_integer_or(params[:per_page] || session[per_page_key], Avo.configuration.via_per_page)
|
|
461
461
|
else
|
|
462
|
-
params[:per_page]
|
|
462
|
+
positive_integer_or(params[:per_page], Avo.configuration.via_per_page)
|
|
463
463
|
end
|
|
464
464
|
end
|
|
465
465
|
end
|
|
@@ -706,12 +706,17 @@ module Avo
|
|
|
706
706
|
end
|
|
707
707
|
|
|
708
708
|
def set_pagination_params
|
|
709
|
-
@index_params[:page] = params[:page]
|
|
709
|
+
@index_params[:page] = positive_integer_or(params[:page], 1)
|
|
710
|
+
@index_params[:per_page] = positive_integer_or(params[:per_page].presence || cookies[:per_page], Avo.configuration.per_page)
|
|
710
711
|
|
|
711
712
|
# If the request includes the 'per_page' parameter, save its value to the cookies
|
|
712
|
-
cookies[:per_page] =
|
|
713
|
+
cookies[:per_page] = @index_params[:per_page] if params[:per_page].present?
|
|
714
|
+
end
|
|
713
715
|
|
|
714
|
-
|
|
716
|
+
# Pagy raises on anything that is not a positive integer, so fall back when the value is not one.
|
|
717
|
+
def positive_integer_or(value, fallback)
|
|
718
|
+
value = value.to_s.to_i
|
|
719
|
+
value.positive? ? value : fallback
|
|
715
720
|
end
|
|
716
721
|
|
|
717
722
|
# If we don't get a query object predefined from a child controller like associations, just spin one up.
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
sort_direction = first_option
|
|
54
54
|
end
|
|
55
55
|
text_classes = "text-content-secondary tracking-tight leading-tight text-xs font-semibold"
|
|
56
|
+
label_tooltip = field.label_tooltip
|
|
56
57
|
table_header_class = field.table_header_class
|
|
57
58
|
end_aligned = table_header_class.to_s.split.include?("text-end") %>
|
|
58
59
|
|
|
@@ -86,7 +87,7 @@
|
|
|
86
87
|
"flex-row-reverse": end_aligned
|
|
87
88
|
),
|
|
88
89
|
'data-turbo-frame': params[:turbo_frame] do %>
|
|
89
|
-
<%=
|
|
90
|
+
<%= render partial: "avo/partials/table_header_label", locals: {field: field, label_tooltip: label_tooltip} %>
|
|
90
91
|
|
|
91
92
|
<%= render partial: 'avo/partials/sortable_component', locals: {
|
|
92
93
|
field: field,
|
|
@@ -95,7 +96,7 @@
|
|
|
95
96
|
<% end %>
|
|
96
97
|
<% else %>
|
|
97
98
|
<%= content_tag :div, class: text_classes, data: {sortable: false} do %>
|
|
98
|
-
<%=
|
|
99
|
+
<%= render partial: "avo/partials/table_header_label", locals: {field: field, label_tooltip: label_tooltip} %>
|
|
99
100
|
<% end %>
|
|
100
101
|
<% end %>
|
|
101
102
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%# The label alone unless the field declares `label_tooltip:`; then the text and
|
|
2
|
+
its cue share one anchor, so hovering either shows the bubble. %>
|
|
3
|
+
<% if label_tooltip.present? %>
|
|
4
|
+
<%= content_tag :span, class: "label-tooltip", title: label_tooltip, data: {tippy: :tooltip} do %>
|
|
5
|
+
<%= field.table_header_label %>
|
|
6
|
+
<%= svg "tabler/outline/info-circle", class: "label-tooltip__icon" %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% else %>
|
|
9
|
+
<%= field.table_header_label %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Avo
|
|
2
|
+
module Concerns
|
|
3
|
+
# Hover tooltips on a field's label and value, declared with the field's
|
|
4
|
+
# `tooltip:` and `label_tooltip:` options. tippy takes the text from
|
|
5
|
+
# `title` and selects on `data-tippy`, so both attributes travel together.
|
|
6
|
+
module HasFieldTooltips
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
def tooltip
|
|
10
|
+
@resolved_tooltip ||= @field.tooltip
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def label_tooltip
|
|
14
|
+
@resolved_label_tooltip ||= @field.label_tooltip
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def tooltip_attributes(text)
|
|
18
|
+
return {} if text.blank?
|
|
19
|
+
|
|
20
|
+
{title: text, data: {tippy: :tooltip}}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Wraps the rendered value in the tooltip anchor. Inline hugs the value so
|
|
24
|
+
# the bubble sits over a badge or a date rather than mid-row; block keeps
|
|
25
|
+
# the width of values that fill the row (editors, maps, bars). Rendered
|
|
26
|
+
# only when there is a tooltip: an empty wrapper would still change how
|
|
27
|
+
# the value sizes.
|
|
28
|
+
def value_tooltip_anchor(block: false, &content_block)
|
|
29
|
+
return capture(&content_block) if tooltip.blank?
|
|
30
|
+
|
|
31
|
+
content_tag :div,
|
|
32
|
+
class: class_names("tooltip-anchor", block ? "tooltip-anchor--block" : "tooltip-anchor--inline"),
|
|
33
|
+
**tooltip_attributes(tooltip),
|
|
34
|
+
&content_block
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -20,6 +20,9 @@ module Avo
|
|
|
20
20
|
include Avo::Fields::Concerns::DomId
|
|
21
21
|
|
|
22
22
|
include ActionView::Helpers::UrlHelper
|
|
23
|
+
# Rails 8.2 extracted link_to, button_to and current_page? into NavigationHelper
|
|
24
|
+
# (rails/rails#58735). Without it, link_to on a resource is a NoMethodError there.
|
|
25
|
+
include ActionView::Helpers::NavigationHelper if defined?(ActionView::Helpers::NavigationHelper)
|
|
23
26
|
|
|
24
27
|
delegate :app, to: ::Avo::Current
|
|
25
28
|
delegate :view_context, to: :app
|
|
@@ -92,6 +95,8 @@ module Avo
|
|
|
92
95
|
@autocomplete = args[:autocomplete]
|
|
93
96
|
@help = args[:help]
|
|
94
97
|
@label_help = args[:label_help]
|
|
98
|
+
@tooltip = args[:tooltip]
|
|
99
|
+
@label_tooltip = args[:label_tooltip]
|
|
95
100
|
@default = args[:default]
|
|
96
101
|
@visible = args[:visible]
|
|
97
102
|
@html = args[:html]
|
|
@@ -227,6 +232,26 @@ module Avo
|
|
|
227
232
|
translated_option(:help)
|
|
228
233
|
end
|
|
229
234
|
|
|
235
|
+
# Hover text for the value: anchored to the value itself on Index and
|
|
236
|
+
# Show, to the input on forms. Resolved here rather than in the wrapper
|
|
237
|
+
# because the Index header renders the label outside any wrapper.
|
|
238
|
+
def tooltip
|
|
239
|
+
execute_context(@tooltip.nil? ? translated_option(:tooltip) : @tooltip)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Hover text for the label, on every view and on the Index header.
|
|
243
|
+
def label_tooltip
|
|
244
|
+
execute_context(@label_tooltip.nil? ? translated_option(:label_tooltip) : @label_tooltip)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# How the value tooltip anchors to the rendered value. `:inline` hugs the
|
|
248
|
+
# value so the bubble sits over a badge or a date rather than mid-row;
|
|
249
|
+
# fields whose value fills the row (editors, maps, progress bars) answer
|
|
250
|
+
# `:block` so the anchor keeps their width instead of collapsing it.
|
|
251
|
+
def tooltip_anchor
|
|
252
|
+
:inline
|
|
253
|
+
end
|
|
254
|
+
|
|
230
255
|
def placeholder
|
|
231
256
|
target = if !@placeholder.nil?
|
|
232
257
|
@placeholder
|
|
@@ -14,17 +14,23 @@ module Avo
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
class << self
|
|
17
|
+
# Each array resource keeps its own model class. It starts as a placeholder class and
|
|
18
|
+
# fetch_records replaces it with the class of the records it builds or finds.
|
|
17
19
|
def model_class
|
|
18
|
-
|
|
20
|
+
return @model_class if @model_class
|
|
21
|
+
|
|
22
|
+
resource_class = self
|
|
23
|
+
|
|
24
|
+
Avo.send(:remove_const, class_name) if Avo.const_defined?(class_name, false)
|
|
25
|
+
|
|
26
|
+
@model_class = Avo.const_set(
|
|
19
27
|
class_name,
|
|
20
28
|
Class.new do
|
|
21
29
|
include ActiveModel::Model
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def all = "Avo::Resources::#{class_name}".constantize.new.fetch_records
|
|
27
|
-
end
|
|
31
|
+
define_singleton_method(:primary_key) { nil }
|
|
32
|
+
define_singleton_method(:class_name) { resource_class.class_name }
|
|
33
|
+
define_singleton_method(:all) { resource_class.new.fetch_records }
|
|
28
34
|
end
|
|
29
35
|
)
|
|
30
36
|
end
|
|
@@ -68,21 +74,22 @@ module Avo
|
|
|
68
74
|
@fetched_records ||= if array_of_records.empty?
|
|
69
75
|
array_of_records
|
|
70
76
|
elsif is_array_of_active_records?(array_of_records)
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
self.class.model_class = array_of_records.first.class
|
|
78
|
+
model_class.where(id: array_of_records.map(&:id))
|
|
73
79
|
elsif is_active_record_relation?(array_of_records)
|
|
74
|
-
|
|
80
|
+
self.class.model_class = array_of_records.try(:model)
|
|
75
81
|
array_of_records
|
|
76
82
|
elsif is_array_of_store_model?(array_of_records)
|
|
77
|
-
|
|
83
|
+
self.class.model_class = array_of_records.first.class
|
|
78
84
|
return(array_of_records)
|
|
79
85
|
else
|
|
80
86
|
# Dynamically create a class with accessors for all unique keys from the records
|
|
81
87
|
keys = array_of_records.flat_map(&:keys).uniq
|
|
88
|
+
resource_class = self.class
|
|
82
89
|
|
|
83
90
|
Avo.send(:remove_const, class_name) if Avo.const_defined?(class_name, false)
|
|
84
91
|
|
|
85
|
-
Avo.const_set(
|
|
92
|
+
custom_class = Avo.const_set(
|
|
86
93
|
class_name,
|
|
87
94
|
Class.new do
|
|
88
95
|
include ActiveModel::Model
|
|
@@ -94,13 +101,14 @@ module Avo
|
|
|
94
101
|
id
|
|
95
102
|
end
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
define_singleton_method(:primary_key) { nil }
|
|
105
|
+
define_singleton_method(:class_name) { resource_class.class_name }
|
|
106
|
+
define_singleton_method(:all) { resource_class.new.fetch_records }
|
|
100
107
|
end
|
|
101
108
|
)
|
|
102
109
|
|
|
103
|
-
|
|
110
|
+
# Keep model_class in sync so the records are instances of it
|
|
111
|
+
resource_class.model_class = custom_class
|
|
104
112
|
|
|
105
113
|
# Map the records to instances of the dynamically created class
|
|
106
114
|
array_of_records.map do |item|
|
data/lib/avo/resources/base.rb
CHANGED
|
@@ -4,6 +4,9 @@ module Avo
|
|
|
4
4
|
extend ActiveSupport::DescendantsTracker
|
|
5
5
|
|
|
6
6
|
include ActionView::Helpers::UrlHelper
|
|
7
|
+
# Rails 8.2 extracted link_to, button_to and current_page? into NavigationHelper
|
|
8
|
+
# (rails/rails#58735). Without it, link_to on a resource is a NoMethodError there.
|
|
9
|
+
include ActionView::Helpers::NavigationHelper if defined?(ActionView::Helpers::NavigationHelper)
|
|
7
10
|
include Avo::Concerns::HasFieldDiscovery
|
|
8
11
|
include Avo::Concerns::HasItems
|
|
9
12
|
include Avo::Concerns::CanReplaceItems
|
|
@@ -160,7 +160,7 @@ Turning a long `belongs_to` / `has_many` picker into a type-to-search field is t
|
|
|
160
160
|
## Gotchas
|
|
161
161
|
|
|
162
162
|
- **Set `inverse_of` on the model association.** Avo relies on it to resolve the reciprocal; missing it causes wrong/empty attach lists and save bugs. Set it on both sides.
|
|
163
|
-
- **The Rails association must exist first.** Adding `field :x, as: :has_many` does nothing if the model has no `has_many :x` — the association
|
|
163
|
+
- **The Rails association must exist first.** Adding `field :x, as: :has_many` does nothing if the model has no `has_many :x` — the association panel raises `Avo::MissingAssociationError`, naming the model, the field, and the declaration to add (`has_many :orders`). The cause is either side: the model genuinely lacks the association, or the field name doesn't match the one the model declares (`field :comments` against a `has_many :post_comments`). The **`array`** field is the one exception — it renders a panel deliberately not backed by an association, so it never raises this.
|
|
164
164
|
- **`has_*` fields are hidden on Edit by default.** Add `show_on: :edit` to surface them on the form. For editing the related record *in* the form (not just displaying it), use `nested` — which needs the `avo-nested` gem.
|
|
165
165
|
- **Attach/detach/create/destroy buttons come from the *target* resource's Pundit policy**, and the method names are **plural, matching the association name**: `attach_users?`, `detach_users?`, `create_users?`, `destroy_users?`, `view_users?`, `show_users?` — *not* the singular `detach_user?`. This is the #1 "why isn't my button showing" cause. Cross-link `avo-authorization`.
|
|
166
166
|
- **`can_create: true` is still vetoed by the policy.** If the target resource's `create?` returns `false`, no create link appears regardless of `can_create`.
|
|
@@ -146,6 +146,7 @@ Every field accepts these common options (full list + types at the field-options
|
|
|
146
146
|
- **`disabled:`** vs **`readonly:`** — both render the input disabled on forms. `disabled:` **also ignores the value on save** (safe against DOM tampering). `readonly:` is UI-only — a user can re-enable it and submit; don't rely on it for protection.
|
|
147
147
|
- **`default:`** — pre-fills the New form (and action modals): `default: -> { Time.current }`.
|
|
148
148
|
- **`help:`** / **`label_help:`** — help text (HTML allowed) below the input (forms only) or below the label (every view).
|
|
149
|
+
- **`tooltip:`** / **`label_tooltip:`** — hover bubble (HTML allowed) on the value (badge/text on Index and Show, the input on forms) or on the label (every view, plus the Index header, with an info icon as the cue). String or block; the block gets `record`, `resource`, `view`, `field`, and returning `nil` renders no tooltip. `record` is `nil` for `label_tooltip` on the Index header. Prefer over `help:`/`label_help:` when the context matters only on demand: `tooltip: -> { "Revoked at #{record.revoked_at}" if record.revoked_at? }`.
|
|
149
150
|
- **`placeholder:`** — placeholder for empty text-like inputs.
|
|
150
151
|
- **`sortable:`** — makes the Index column sortable. `true` for real columns; a `-> { query.order(...) }` lambda (receives `query` and `direction`) for computed fields.
|
|
151
152
|
- **Number `format:`** — formats `:number` fields on Index and Show with `:delimited`, `:percentage`, or `:human`; forms keep the raw number. A formatted number is end-aligned in tables, while a bare number stays start-aligned for identifier/year-shaped values. Rails i18n controls delimiters and separators. For money use the `:money` field, and for anything else `format_display_using:`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"repo": "avo-hq/docs.avohq.io",
|
|
3
3
|
"docs_path": "docs/4.0",
|
|
4
|
-
"commit": "
|
|
5
|
-
"date": "2026-09-
|
|
4
|
+
"commit": "7f2d52546ecf8bd84044cd011d58f5ffcdeae016",
|
|
5
|
+
"date": "2026-09-22",
|
|
6
6
|
"note": "The docs commit these skills were last indexed from. Run ruby lib/avo/skills/bin/docs_drift.rb to see what changed since, and --update after a refresh."
|
|
7
7
|
}
|
data/lib/avo/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: avo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.2.
|
|
4
|
+
version: 4.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adrian Marin
|
|
@@ -915,6 +915,7 @@ files:
|
|
|
915
915
|
- app/views/avo/partials/_switcher_dropdown.html.erb
|
|
916
916
|
- app/views/avo/partials/_switcher_inline.html.erb
|
|
917
917
|
- app/views/avo/partials/_table_header.html.erb
|
|
918
|
+
- app/views/avo/partials/_table_header_label.html.erb
|
|
918
919
|
- app/views/avo/partials/_view_toggle_button.html.erb
|
|
919
920
|
- app/views/avo/partials/distribution_chart.html.erb
|
|
920
921
|
- app/views/avo/partials/distribution_chart_full.html.erb
|
|
@@ -969,6 +970,7 @@ files:
|
|
|
969
970
|
- lib/avo/concerns/has_description.rb
|
|
970
971
|
- lib/avo/concerns/has_discreet_information.rb
|
|
971
972
|
- lib/avo/concerns/has_field_discovery.rb
|
|
973
|
+
- lib/avo/concerns/has_field_tooltips.rb
|
|
972
974
|
- lib/avo/concerns/has_helpers.rb
|
|
973
975
|
- lib/avo/concerns/has_item_type.rb
|
|
974
976
|
- lib/avo/concerns/has_items.rb
|