avo 4.0.25 → 4.1.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 +4 -4
- data/Gemfile.lock +8 -1
- data/app/assets/builds/avo/application.css +9 -21
- data/app/assets/builds/avo/application.js +71 -71
- data/app/assets/builds/avo/application.js.map +3 -3
- data/app/assets/stylesheets/css/components/modal.css +7 -1
- data/app/assets/stylesheets/css/components/ui/tabs.css +10 -0
- data/app/components/avo/actions_component.rb +12 -4
- data/app/components/avo/modal_component.html.erb +0 -1
- data/app/components/avo/modal_component.rb +1 -1
- data/app/components/avo/resource_component.rb +3 -0
- data/app/components/avo/row_selector_component.rb +3 -3
- data/app/controllers/avo/actions_controller.rb +1 -3
- data/app/controllers/avo/appearance_settings_controller.rb +1 -1
- data/app/controllers/avo/array_controller.rb +1 -1
- data/app/controllers/avo/associations_controller.rb +1 -3
- data/app/controllers/avo/attachments_controller.rb +1 -3
- data/app/controllers/avo/base_controller.rb +1 -3
- data/app/controllers/avo/charts_controller.rb +1 -3
- data/app/controllers/avo/debug_controller.rb +1 -3
- data/app/controllers/avo/home_controller.rb +1 -3
- data/app/controllers/avo/media_library_controller.rb +1 -1
- data/app/controllers/avo/private_controller.rb +1 -3
- data/app/controllers/avo/resources_controller.rb +1 -3
- data/app/controllers/avo/search_controller.rb +1 -3
- data/app/javascript/js/controllers/modal_size_controller.js +9 -49
- data/app/javascript/js/controllers/record_selector_controller.js +25 -8
- data/app/views/avo/media_library/index.html.erb +4 -1
- data/avo.gemspec +1 -1
- data/lib/avo/resources/controls/base_control.rb +13 -0
- data/lib/avo/version.rb +1 -1
- metadata +2 -2
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
.modal__body-content {
|
|
173
|
-
@apply w-full min-h-48 rounded-card-wrapper border border-tertiary bg-primary;
|
|
173
|
+
@apply w-full min-h-48 rounded-card-wrapper border border-tertiary bg-primary p-4;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/* Belongs-to "Create new" embeds an edit form in the modal. Drop the nested
|
|
@@ -233,6 +233,12 @@
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
.modal--width-5xl {
|
|
237
|
+
.modal__card {
|
|
238
|
+
@apply w-5xl;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
236
242
|
.modal--width-25 {
|
|
237
243
|
.modal__card {
|
|
238
244
|
@apply w-[25dvw];
|
|
@@ -184,6 +184,16 @@
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
/* The breakout above assumes the container has padding to give up. The
|
|
188
|
+
belongs-to "Create new" modal has exactly as much as the breakout consumes,
|
|
189
|
+
so the dashed border ends up flush against the modal edge while the sibling
|
|
190
|
+
panels stay inset. Keep the group in flow there and let its own padding
|
|
191
|
+
hold the content off the border. AVO-1365. */
|
|
192
|
+
.modal-form-body .tab-group {
|
|
193
|
+
width: 100%;
|
|
194
|
+
margin-inline: 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
187
197
|
/* ============================================
|
|
188
198
|
ACTIVE INDICATOR VIEW TRANSITION
|
|
189
199
|
============================================ */
|
|
@@ -78,10 +78,18 @@ class Avo::ActionsComponent < Avo::BaseComponent
|
|
|
78
78
|
when defined?(Avo::CustomControls::Resources::Controls::Action) && Avo::CustomControls::Resources::Controls::Action
|
|
79
79
|
render_action_link(action.action, icon: action.icon)
|
|
80
80
|
when defined?(Avo::CustomControls::Resources::Controls::LinkTo) && Avo::CustomControls::Resources::Controls::LinkTo
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
# Dropdown items render as plain links, not ButtonComponents, so the styling
|
|
82
|
+
# options have nothing to land in — spreading the raw kwargs leaked them into
|
|
83
|
+
# the markup (color: :fuchsia rendered as color="fuchsia"). Excluding
|
|
84
|
+
# CONTROL_OPTIONS drops those and the internal plumbing while still forwarding
|
|
85
|
+
# genuine HTML attributes like rel:.
|
|
86
|
+
link_to action.path,
|
|
87
|
+
**action.args.except(*Avo::Resources::Controls::BaseControl::CONTROL_OPTIONS),
|
|
88
|
+
class: action.classes,
|
|
89
|
+
title: action.title,
|
|
90
|
+
target: action.target,
|
|
91
|
+
data: action.data do
|
|
92
|
+
raw("#{icon(action.icon)} #{action.label}")
|
|
85
93
|
end
|
|
86
94
|
end
|
|
87
95
|
end
|
|
@@ -4,7 +4,7 @@ class Avo::ModalComponent < Avo::BaseComponent
|
|
|
4
4
|
renders_one :heading
|
|
5
5
|
renders_one :controls
|
|
6
6
|
|
|
7
|
-
prop :width, default: :xl # :sm, :md, :lg, :xl, :2xl, :3xl, :4xl, :full
|
|
7
|
+
prop :width, default: :xl # :sm, :md, :lg, :xl, :2xl, :3xl, :4xl, :5xl, :full
|
|
8
8
|
prop :height, default: :auto # :auto, :sm, :md, :lg, :xl, :2xl, :3xl, :4xl, :full
|
|
9
9
|
prop :body_class
|
|
10
10
|
prop :close_modal_on_backdrop_click, default: true, reader: :public
|
|
@@ -305,7 +305,10 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
305
305
|
end
|
|
306
306
|
|
|
307
307
|
def render_link_to(link)
|
|
308
|
+
# Anything outside CONTROL_OPTIONS is a plain HTML attribute (rel:, id:…) and
|
|
309
|
+
# rides ButtonComponent's `prop :args, kind: :**` wildcard onto the <a> tag.
|
|
308
310
|
a_link link.path,
|
|
311
|
+
**link.args.except(*Avo::Resources::Controls::BaseControl::CONTROL_OPTIONS),
|
|
309
312
|
color: link.color,
|
|
310
313
|
style: link.style,
|
|
311
314
|
icon: link.icon,
|
|
@@ -5,12 +5,12 @@ class Avo::RowSelectorComponent < Avo::BaseComponent
|
|
|
5
5
|
prop :index
|
|
6
6
|
prop :checked, default: false
|
|
7
7
|
|
|
8
|
+
# Shift-select is wired on table rows only: grid items render this component without an index and
|
|
9
|
+
# have no table row for the controller to walk up to.
|
|
8
10
|
def data_action
|
|
9
11
|
data = "input->item-selector#toggle input->item-select-all#selectRow"
|
|
10
12
|
|
|
11
|
-
if
|
|
12
|
-
data += " click->record-selector#toggleMultiple"
|
|
13
|
-
end
|
|
13
|
+
data += " click->record-selector#toggleMultiple" if @index.present?
|
|
14
14
|
|
|
15
15
|
data
|
|
16
16
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require_dependency "avo/application_controller"
|
|
2
|
-
|
|
3
1
|
module Avo
|
|
4
|
-
class ActionsController < ApplicationController
|
|
2
|
+
class ActionsController < Avo::ApplicationController
|
|
5
3
|
before_action :set_resource_name, :set_resource
|
|
6
4
|
before_action :set_query, :set_record, :set_action, :verify_authorization, only: [:show, :handle]
|
|
7
5
|
before_action :set_fields, only: :handle
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require_dependency "avo/base_controller"
|
|
2
|
-
|
|
3
1
|
module Avo
|
|
4
|
-
class AssociationsController < BaseController
|
|
2
|
+
class AssociationsController < Avo::BaseController
|
|
5
3
|
before_action :set_record, only: [:show, :index, :new, :create, :destroy]
|
|
6
4
|
before_action :set_related_resource_name
|
|
7
5
|
before_action :set_related_resource, only: [:show, :index, :new, :create, :destroy]
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require_dependency "avo/application_controller"
|
|
2
|
-
|
|
3
1
|
module Avo
|
|
4
|
-
class AttachmentsController < ApplicationController
|
|
2
|
+
class AttachmentsController < Avo::ApplicationController
|
|
5
3
|
before_action :set_resource_name, only: [:destroy, :create]
|
|
6
4
|
before_action :set_resource, only: [:destroy, :create]
|
|
7
5
|
before_action :set_record, only: [:destroy, :create]
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require_dependency "avo/application_controller"
|
|
2
|
-
|
|
3
1
|
module Avo
|
|
4
|
-
class BaseController < ApplicationController
|
|
2
|
+
class BaseController < Avo::ApplicationController
|
|
5
3
|
include Avo::Concerns::FiltersSessionHandler
|
|
6
4
|
include Avo::Concerns::AssociationQueryScope
|
|
7
5
|
include Avo::Concerns::SafeCall
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require_dependency "avo/base_controller"
|
|
2
|
-
|
|
3
1
|
module Avo
|
|
4
|
-
class ChartsController < BaseController
|
|
2
|
+
class ChartsController < Avo::BaseController
|
|
5
3
|
# Summarizable charts reuse the index query pipeline (including policy scope via
|
|
6
4
|
# `query_scope`). They were never meant to require `distribution_chart?` /
|
|
7
5
|
# `distribution_chart_full?` policy methods — those only surfaced once
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require_dependency "avo/application_controller"
|
|
2
|
-
|
|
3
1
|
module Avo
|
|
4
|
-
class HomeController < ApplicationController
|
|
2
|
+
class HomeController < Avo::ApplicationController
|
|
5
3
|
def index
|
|
6
4
|
if Avo.configuration.home_path.present?
|
|
7
5
|
# If the home_path is a block run it, if not, just use it
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
# This controller is currently only used for searchable fields, it keeps the old search controller without any changes
|
|
4
4
|
# Possibly have much more logic that it uses, but it's not worth the effort to refactor it since we're going to replace it with a new searchable fields way
|
|
5
5
|
|
|
6
|
-
require_dependency "avo/application_controller"
|
|
7
|
-
|
|
8
6
|
module Avo
|
|
9
|
-
class SearchController < ApplicationController
|
|
7
|
+
class SearchController < Avo::ApplicationController
|
|
10
8
|
include Rails.application.routes.url_helpers
|
|
11
9
|
include ActionView::Helpers::TextHelper
|
|
12
10
|
|
|
@@ -2,8 +2,6 @@ import { Controller } from '@hotwired/stimulus'
|
|
|
2
2
|
|
|
3
3
|
// Connects to data-controller="modal-size"
|
|
4
4
|
export default class extends Controller {
|
|
5
|
-
static targets = ['container']
|
|
6
|
-
|
|
7
5
|
static values = {
|
|
8
6
|
currentWidth: String,
|
|
9
7
|
currentHeight: String,
|
|
@@ -28,56 +26,18 @@ export default class extends Controller {
|
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
updateWidthClass(width) {
|
|
31
|
-
|
|
32
|
-
this.modalElement.classList.remove(
|
|
33
|
-
'modal--width-sm',
|
|
34
|
-
'modal--width-md',
|
|
35
|
-
'modal--width-lg',
|
|
36
|
-
'modal--width-xl',
|
|
37
|
-
'modal--width-2xl',
|
|
38
|
-
'modal--width-3xl',
|
|
39
|
-
'modal--width-4xl',
|
|
40
|
-
'modal--width-full',
|
|
41
|
-
'modal--width-25',
|
|
42
|
-
'modal--width-50',
|
|
43
|
-
'modal--width-75',
|
|
44
|
-
'modal--width-100',
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
// Remove all width classes from the container
|
|
48
|
-
this.containerTarget.classList.remove('w-sm', 'w-md', 'w-lg', 'w-xl', 'w-2xl', 'w-3xl', 'w-4xl', 'w-full')
|
|
49
|
-
|
|
50
|
-
// Add the new modal width class to the modal element
|
|
51
|
-
this.modalElement.classList.add(`modal--width-${width}`)
|
|
52
|
-
|
|
53
|
-
// Add the corresponding width class to the container
|
|
54
|
-
if (width === 'full') {
|
|
55
|
-
this.containerTarget.classList.add('w-full')
|
|
56
|
-
} else {
|
|
57
|
-
this.containerTarget.classList.add(`w-${width}`)
|
|
58
|
-
}
|
|
29
|
+
this.#swapModifier('modal--width-', width)
|
|
59
30
|
}
|
|
60
31
|
|
|
61
32
|
updateHeightClass(height) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
'modal--height-auto',
|
|
65
|
-
'modal--height-sm',
|
|
66
|
-
'modal--height-md',
|
|
67
|
-
'modal--height-lg',
|
|
68
|
-
'modal--height-xl',
|
|
69
|
-
'modal--height-2xl',
|
|
70
|
-
'modal--height-3xl',
|
|
71
|
-
'modal--height-4xl',
|
|
72
|
-
'modal--height-full',
|
|
73
|
-
'modal--height-25',
|
|
74
|
-
'modal--height-50',
|
|
75
|
-
'modal--height-75',
|
|
76
|
-
'modal--height-100',
|
|
77
|
-
)
|
|
33
|
+
this.#swapModifier('modal--height-', height)
|
|
34
|
+
}
|
|
78
35
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
36
|
+
// The CSS owns the sizes: each `modal--width-*` / `modal--height-*` modifier
|
|
37
|
+
// sizes `.modal__card`. Swapping by prefix means a new size token is a CSS
|
|
38
|
+
// rule and nothing else — no list here to keep in sync with the stylesheet.
|
|
39
|
+
#swapModifier(prefix, value) {
|
|
40
|
+
this.modalElement.classList.remove(...[...this.modalElement.classList].filter((name) => name.startsWith(prefix)))
|
|
41
|
+
this.modalElement.classList.add(`${prefix}${value}`)
|
|
82
42
|
}
|
|
83
43
|
}
|
|
@@ -9,8 +9,19 @@ export default class extends Controller {
|
|
|
9
9
|
|
|
10
10
|
autoClicking = false
|
|
11
11
|
|
|
12
|
+
// Every lookup in here is scoped to this controller's element. A page can hold several tables (a
|
|
13
|
+
// show page renders one per has_many association) and each numbers its rows from zero, so a
|
|
14
|
+
// document-wide lookup by index always lands in the first table.
|
|
12
15
|
get itemSelectorCells() {
|
|
13
|
-
return
|
|
16
|
+
return this.element.querySelectorAll('.item-selector-cell')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#rowAt(index) {
|
|
20
|
+
return this.element.querySelector(`tr[data-index="${index}"]`)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#checkboxAt(index) {
|
|
24
|
+
return this.element.querySelector(`input[type="checkbox"][data-index="${index}"]`)
|
|
14
25
|
}
|
|
15
26
|
|
|
16
27
|
get hasLastCheckedIndex() {
|
|
@@ -32,6 +43,12 @@ export default class extends Controller {
|
|
|
32
43
|
return
|
|
33
44
|
}
|
|
34
45
|
|
|
46
|
+
// Ranges are addressed by row. Index view types outside a table (avo-notifications renders one)
|
|
47
|
+
// give this component an index but no row to walk up to, so there's nothing to span.
|
|
48
|
+
if (!event.target.closest('tr')) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
// If there's no last checked index and the shift key isn't pressed, set the starting index
|
|
36
53
|
if (!this.hasLastCheckedIndex) {
|
|
37
54
|
this.#setStartingIndex(event)
|
|
@@ -57,7 +74,7 @@ export default class extends Controller {
|
|
|
57
74
|
|
|
58
75
|
// Loop through the range of rows and toggle the checkboxes
|
|
59
76
|
theRange.forEach((index) => {
|
|
60
|
-
const checkbox =
|
|
77
|
+
const checkbox = this.#checkboxAt(index)
|
|
61
78
|
|
|
62
79
|
// Toggle the checkbox if it's not in the same state as the target checkbox
|
|
63
80
|
if (checkbox.checked !== state) {
|
|
@@ -110,7 +127,8 @@ export default class extends Controller {
|
|
|
110
127
|
#selectorMouseenterHandler(event) {
|
|
111
128
|
// Add the highlighted-row class to the row that the mouse is over
|
|
112
129
|
event.target.closest('tr').classList.add('highlighted-row')
|
|
113
|
-
|
|
130
|
+
// Check against null, not truthiness — index 0 is a valid anchor
|
|
131
|
+
if (this.hasLastCheckedIndex) {
|
|
114
132
|
// Highlight the range of rows between the last checked index and the current index
|
|
115
133
|
this.#highlightRange(this.lastCheckedIndex, parseInt(event.target.closest('tr').dataset.index))
|
|
116
134
|
}
|
|
@@ -120,17 +138,16 @@ export default class extends Controller {
|
|
|
120
138
|
// Remove the highlighted-row class from the row that the mouse is over
|
|
121
139
|
event.target.closest('tr').classList.remove('highlighted-row')
|
|
122
140
|
// Remove the highlighted-row class from all rows
|
|
123
|
-
|
|
141
|
+
this.element.querySelectorAll('tr[data-index]').forEach((tr) => {
|
|
124
142
|
tr.classList.remove('highlighted-row')
|
|
125
143
|
})
|
|
126
144
|
}
|
|
127
145
|
|
|
128
146
|
// Highlight the range of rows between the start index and the end index
|
|
129
147
|
#highlightRange(startIndex, endIndex) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
tr.classList.add('highlighted-row')
|
|
148
|
+
// `range` excludes the end index, but the hovered row is highlighted by the caller
|
|
149
|
+
range(startIndex, endIndex).forEach((index) => {
|
|
150
|
+
this.#rowAt(index).classList.add('highlighted-row')
|
|
134
151
|
})
|
|
135
152
|
}
|
|
136
153
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<%= render Avo::TurboFrameWrapperComponent.new(turbo_frame_request_id) do %>
|
|
2
2
|
<% if turbo_frame_request_id %>
|
|
3
|
-
|
|
3
|
+
<%# The library is a grid of media cards (13rem floor per card), so the default
|
|
4
|
+
:xl modal fits two columns and 4xl three, leaving a ragged last row. 5xl
|
|
5
|
+
fits four, so a page of 12 lands as three full rows. %>
|
|
6
|
+
<%= render Avo::ModalComponent.new(title: t("avo.media_library.title"), width: :"5xl") do |c| %>
|
|
4
7
|
<div class="p-4">
|
|
5
8
|
<%= render Avo::MediaLibrary::ListComponent.new attaching: @attaching %>
|
|
6
9
|
</div>
|
data/avo.gemspec
CHANGED
|
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
"public gem pushes."
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
spec.required_ruby_version = ">= 3.
|
|
31
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
32
32
|
spec.post_install_message = "Thank you for using Avo 💪 Docs are available at https://docs.avohq.io"
|
|
33
33
|
|
|
34
34
|
# NOTE: `public/` is rejected below — Avo 4 ships precompiled assets from
|
|
@@ -2,6 +2,19 @@ module Avo
|
|
|
2
2
|
module Resources
|
|
3
3
|
module Controls
|
|
4
4
|
class BaseControl
|
|
5
|
+
unless defined?(CONTROL_OPTIONS)
|
|
6
|
+
# Keys the control consumes itself, either as an option it renders from
|
|
7
|
+
# (`style`, `icon`…) or as internal plumbing merged in by `execute_block`
|
|
8
|
+
# (`as_index_control`, `item`…). Everything a user passes *outside* this
|
|
9
|
+
# list is forwarded to the rendered element as an HTML attribute, so
|
|
10
|
+
# `link_to "Docs", url, rel: "noopener"` reaches the `<a>` tag.
|
|
11
|
+
CONTROL_OPTIONS = %i[
|
|
12
|
+
label path title target data class
|
|
13
|
+
style color size icon icon_class confirmation_message
|
|
14
|
+
as_index_control from_custom_list item
|
|
15
|
+
].freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
5
18
|
attr_reader :label, :title, :color, :style, :icon, :icon_class, :confirmation_message, :size, :as_index_control
|
|
6
19
|
|
|
7
20
|
def initialize(**args)
|
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.0
|
|
4
|
+
version: 4.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adrian Marin
|
|
@@ -1189,7 +1189,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
1189
1189
|
requirements:
|
|
1190
1190
|
- - ">="
|
|
1191
1191
|
- !ruby/object:Gem::Version
|
|
1192
|
-
version: 3.
|
|
1192
|
+
version: 3.2.0
|
|
1193
1193
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1194
1194
|
requirements:
|
|
1195
1195
|
- - ">="
|