avo 4.0.0.beta.4 → 4.0.0.beta.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 +12 -12
- data/app/assets/builds/avo/application.css +45 -65
- data/app/assets/builds/avo/application.js +113 -113
- data/app/assets/builds/avo/application.js.map +4 -4
- data/app/assets/stylesheets/application.css +1 -0
- data/app/assets/stylesheets/css/components/button.css +5 -0
- data/app/assets/stylesheets/css/components/color_scheme_switcher.css +8 -23
- data/app/assets/stylesheets/css/components/input.css +12 -3
- data/app/assets/stylesheets/css/components/ui/card.css +2 -0
- data/app/assets/stylesheets/css/fields/code.css +3 -4
- data/app/assets/stylesheets/css/resource-controls.css +3 -3
- data/app/assets/stylesheets/css/sidebar.css +2 -6
- data/app/assets/stylesheets/css/table.css +4 -0
- data/app/assets/stylesheets/css/typography.css +3 -1
- data/app/components/avo/actions_component.html.erb +2 -1
- data/app/components/avo/actions_component.rb +1 -0
- data/app/components/avo/base_component.rb +36 -5
- data/app/components/avo/button_component.rb +1 -1
- data/app/components/avo/keyboard_shortcuts_component.rb +23 -2
- data/app/components/avo/modal_component.html.erb +1 -0
- data/app/components/avo/resource_component.rb +33 -4
- data/app/components/avo/sidebar/heading_component.html.erb +24 -0
- data/app/components/avo/sidebar/heading_component.rb +10 -0
- data/app/components/avo/sidebar/link_component.rb +1 -1
- data/app/components/avo/sidebar/section_component.html.erb +6 -26
- data/app/components/avo/sidebar_component.html.erb +16 -10
- data/app/components/avo/u_i/search_input_component.html.erb +8 -3
- data/app/components/avo/u_i/search_input_component.rb +1 -1
- data/app/components/avo/views/resource_index_component.html.erb +3 -2
- data/app/javascript/application.js +2 -1
- data/app/javascript/js/controllers/color_scheme_switcher_controller.js +1 -11
- data/app/javascript/js/controllers/dropdown_menu_controller.js +59 -3
- data/app/javascript/js/controllers/index_row_navigator_controller.js +185 -0
- data/app/javascript/js/controllers/item_select_all_controller.js +10 -0
- data/app/javascript/js/controllers/resource_search_controller.js +4 -0
- data/app/javascript/js/controllers.js +2 -0
- data/app/javascript/js/global_hotkeys.js +44 -9
- data/app/views/avo/actions/show.html.erb +2 -1
- data/app/views/avo/partials/_color_scheme_switcher.html.erb +42 -47
- data/app/views/avo/partials/_view_toggle_button.html.erb +6 -1
- data/lib/avo/concerns/row_controls_configuration.rb +1 -1
- data/lib/avo/version.rb +1 -1
- metadata +4 -1
|
@@ -141,6 +141,11 @@
|
|
|
141
141
|
--btn-color-accent-content: var(--color-rose-600);
|
|
142
142
|
--btn-color-accent-foreground: var(--color-white);
|
|
143
143
|
}
|
|
144
|
+
|
|
145
|
+
/* The kbd element is taller than the button text, so we need to offset it */
|
|
146
|
+
&>span >kbd {
|
|
147
|
+
@apply -my-1;
|
|
148
|
+
}
|
|
144
149
|
}
|
|
145
150
|
|
|
146
151
|
.dark {
|
|
@@ -43,24 +43,20 @@
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/* Theme (neutrals) dropdown */
|
|
46
|
-
.color-scheme-switcher__theme-wrapper {
|
|
47
|
-
@apply relative;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
46
|
.color-scheme-switcher__button--theme {
|
|
51
47
|
@apply gap-1;
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
.color-scheme-switcher__theme-
|
|
55
|
-
@apply
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
.color-scheme-switcher__theme-popover {
|
|
51
|
+
@apply start-0 end-auto min-w-[160px];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.color-scheme-switcher__accent-popover {
|
|
55
|
+
@apply min-w-[160px];
|
|
60
56
|
}
|
|
61
57
|
|
|
62
58
|
.color-scheme-switcher__theme-options {
|
|
63
|
-
@apply flex flex-col gap-0.5;
|
|
59
|
+
@apply flex flex-col gap-0.5 self-stretch;
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
.color-scheme-switcher__theme-option {
|
|
@@ -126,20 +122,9 @@
|
|
|
126
122
|
}
|
|
127
123
|
|
|
128
124
|
/* Accent color dropdown */
|
|
129
|
-
.color-scheme-switcher__accent-wrapper {
|
|
130
|
-
@apply relative;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.color-scheme-switcher__accent-panel {
|
|
134
|
-
@apply absolute top-full mt-2 end-0 z-40;
|
|
135
|
-
@apply rounded-lg bg-white dark:bg-neutral-800;
|
|
136
|
-
@apply border border-neutral-200 dark:border-neutral-700;
|
|
137
|
-
@apply shadow-lg p-2 min-w-[160px];
|
|
138
|
-
animation: css-animate-slide-down 0.15s ease-out;
|
|
139
|
-
}
|
|
140
125
|
|
|
141
126
|
.color-scheme-switcher__accent-options {
|
|
142
|
-
@apply grid grid-cols-3 gap-2;
|
|
127
|
+
@apply grid grid-cols-3 gap-2 self-stretch;
|
|
143
128
|
}
|
|
144
129
|
|
|
145
130
|
.color-scheme-switcher__accent-option {
|
|
@@ -266,8 +266,17 @@
|
|
|
266
266
|
);
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
/*
|
|
269
|
+
/* Single-key shortcut: "/" */
|
|
270
270
|
.search-input__input.search-input__input--with-shortcut {
|
|
271
|
+
padding-inline-end: calc(
|
|
272
|
+
var(--input-icon-offset) +
|
|
273
|
+
var(--input-icon-size) +
|
|
274
|
+
var(--input-icon-gap) * 2
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* Two-key shortcut: ⌘K (Mac) */
|
|
279
|
+
.search-input__input.search-input__input--with-two-key-shortcut {
|
|
271
280
|
padding-inline-end: calc(
|
|
272
281
|
var(--input-icon-offset) +
|
|
273
282
|
var(--input-icon-size) * 2 +
|
|
@@ -275,8 +284,8 @@
|
|
|
275
284
|
);
|
|
276
285
|
}
|
|
277
286
|
|
|
278
|
-
/*
|
|
279
|
-
body.os-pc .search-input__input.search-input__input--with-shortcut {
|
|
287
|
+
/* Two-key shortcut: Ctrl+K (PC — wider because "CTRL" is wider than "⌘") */
|
|
288
|
+
body.os-pc .search-input__input.search-input__input--with-two-key-shortcut {
|
|
280
289
|
padding-inline-end: calc(
|
|
281
290
|
var(--input-icon-offset) +
|
|
282
291
|
var(--input-icon-size) * 3 +
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
@apply flex flex-col items-start self-stretch w-full;
|
|
30
30
|
/* offset 1px from the wrapper to match the design */
|
|
31
31
|
padding: calc(var(--spacing) - 1px);
|
|
32
|
+
/* prevent scrollIntoView from shifting the table horizontally by the padding amount */
|
|
33
|
+
scroll-padding-inline: calc(var(--spacing) );
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/* for pagination we need the padding to be 4px */
|
|
@@ -38,10 +38,9 @@
|
|
|
38
38
|
@apply border border-tertiary rounded-lg;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/*
|
|
42
|
-
.CodeMirror-fullscreen
|
|
43
|
-
|
|
44
|
-
z-index: 50;
|
|
41
|
+
/* Lift the main stacking context above the navbar (z-index: 100) when CodeMirror is fullscreen */
|
|
42
|
+
.main-content-area:has(.CodeMirror-fullscreen) {
|
|
43
|
+
z-index: 101;
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
/* EasyMDE shell */
|
|
@@ -46,10 +46,6 @@
|
|
|
46
46
|
@apply flex flex-col px-3 w-full;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
.sidebar__nav-list {
|
|
50
|
-
@apply w-full space-y-1;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
49
|
.sidebar__footer {
|
|
54
50
|
@apply shrink-0;
|
|
55
51
|
}
|
|
@@ -60,11 +56,11 @@
|
|
|
60
56
|
/* ================================================ */
|
|
61
57
|
|
|
62
58
|
.sidebar-section {
|
|
63
|
-
@apply
|
|
59
|
+
@apply not-first:mt-4;
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
.sidebar-section__header {
|
|
67
|
-
@apply flex items-center w-full text-start p-2 pb-1 gap-2 rounded-lg text-content-secondary text-base font-medium leading-6
|
|
63
|
+
@apply flex items-center w-full text-start p-2 pb-1 gap-2 rounded-lg text-content-secondary text-base font-medium leading-6 border-none bg-transparent;
|
|
68
64
|
}
|
|
69
65
|
|
|
70
66
|
.sidebar-section__header-inner {
|
|
@@ -25,10 +25,12 @@ kbd {
|
|
|
25
25
|
border: 1px solid currentColor;
|
|
26
26
|
background-color: color-mix(in srgb, currentColor 8%, transparent); */
|
|
27
27
|
box-shadow: var(--box-shadow-search-input-shortcut);
|
|
28
|
+
transition: transform 0.08s ease-out, box-shadow 0.08s ease-out;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
kbd.kbd--called {
|
|
31
|
-
|
|
32
|
+
transform: scale(0.75) translateY(2px);
|
|
33
|
+
box-shadow: none;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
@layer base {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
action: component.action,
|
|
24
24
|
actions_dropdown_button: @resource.model_key,
|
|
25
25
|
tippy: "tooltip",
|
|
26
|
-
tippy_content: @title
|
|
26
|
+
tippy_content: @title,
|
|
27
|
+
**(@hotkey.present? ? (@as_row_control ? {hotkey_original: @hotkey} : {hotkey: @hotkey}) : {})
|
|
27
28
|
} do %>
|
|
28
29
|
<%= @label %>
|
|
29
30
|
<% end %>
|
|
@@ -16,16 +16,47 @@ class Avo::BaseComponent < ViewComponent::Base
|
|
|
16
16
|
def component_name = self.class.name.to_s.underscore
|
|
17
17
|
|
|
18
18
|
# Renders a <kbd> badge for a hotkey string.
|
|
19
|
-
#
|
|
19
|
+
# Supports modifier tokens rendered in a friendly OS-aware way.
|
|
20
20
|
def hotkey_badge(hotkey, **html_options)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
# `@github/hotkey` uses:
|
|
22
|
+
# - `+` for modifier combos (e.g. "Mod+Enter")
|
|
23
|
+
# - spaces for sequences/alternatives (e.g. "g n" or "Meta+Enter Control+Enter")
|
|
24
|
+
#
|
|
25
|
+
# Render key parts for the badge, mapping supported modifiers to OS-aware glyphs.
|
|
26
|
+
keys = hotkey.to_s.split(/[+\s]+/).reject(&:blank?)
|
|
27
|
+
|
|
28
|
+
first_key = keys.first
|
|
29
|
+
return if first_key.blank?
|
|
30
|
+
|
|
31
|
+
content_tag(:span, **html_options) do
|
|
32
|
+
# Render multiple keys (e.g. "g n") inside a wrapper so any provided
|
|
33
|
+
# classes (like `ms-auto`) are applied once.
|
|
34
|
+
safe_join(keys.map { |key| render_hotkey_badge_key(key) }, " ")
|
|
35
|
+
end
|
|
25
36
|
end
|
|
26
37
|
|
|
27
38
|
private
|
|
28
39
|
|
|
40
|
+
def render_hotkey_badge_key(key)
|
|
41
|
+
token = key.to_s.strip
|
|
42
|
+
|
|
43
|
+
case token.downcase
|
|
44
|
+
when "mod"
|
|
45
|
+
tag.kbd do
|
|
46
|
+
helpers.safe_join([
|
|
47
|
+
tag.abbr("⌘", title: "Command", class: "no-underline os-pc:hidden"),
|
|
48
|
+
tag.abbr("CTRL", title: "CTRL", class: "no-underline os-mac:hidden")
|
|
49
|
+
])
|
|
50
|
+
end
|
|
51
|
+
when "enter", "return"
|
|
52
|
+
tag.kbd("↵")
|
|
53
|
+
when "escape"
|
|
54
|
+
tag.kbd("Esc")
|
|
55
|
+
else
|
|
56
|
+
tag.kbd(token.upcase)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
29
60
|
# Use the @parent_resource to fetch the field using the @reflection name.
|
|
30
61
|
def field
|
|
31
62
|
find_association_field(resource: @parent_resource, association: params[:related_name] || @reflection.name)
|
|
@@ -79,6 +79,6 @@ class Avo::ButtonComponent < Avo::BaseComponent
|
|
|
79
79
|
def render_content
|
|
80
80
|
concat helpers.svg(@icon, class: class_names("button__icon", @icon_class)) if @icon.present?
|
|
81
81
|
concat content if content.present?
|
|
82
|
-
concat hotkey_badge(@args.dig(:data, :hotkey)) if @args.dig(:data, :hotkey)
|
|
82
|
+
concat hotkey_badge(@args.dig(:data, :hotkey)) if @args.dig(:data, :hotkey) && @args.dig(:data, :show_hotkey_badge) != false
|
|
83
83
|
end
|
|
84
84
|
end
|
|
@@ -29,13 +29,34 @@ class Avo::KeyboardShortcutsComponent < Avo::BaseComponent
|
|
|
29
29
|
"Show view",
|
|
30
30
|
[
|
|
31
31
|
shortcut(action: "Delete record", keys: ["D"]),
|
|
32
|
-
shortcut(action: "Edit record", keys: ["E"])
|
|
32
|
+
shortcut(action: "Edit record", keys: ["E"]),
|
|
33
|
+
shortcut(action: "Open actions", keys: ["A"])
|
|
34
|
+
]
|
|
35
|
+
),
|
|
36
|
+
build_section(
|
|
37
|
+
"Action",
|
|
38
|
+
[
|
|
39
|
+
shortcut(action: "Run action", keys: {mac: ["Cmd", "↵"], other: ["Ctrl", "↵"]}),
|
|
40
|
+
shortcut(action: "Cancel action", keys: ["Esc"])
|
|
33
41
|
]
|
|
34
42
|
),
|
|
35
43
|
build_section(
|
|
36
44
|
"Index view",
|
|
37
45
|
[
|
|
38
|
-
shortcut(action: "
|
|
46
|
+
shortcut(action: "Focus search", keys: ["/"]),
|
|
47
|
+
shortcut(action: "Create new record", keys: ["C"]),
|
|
48
|
+
shortcut(action: "Open actions", keys: ["A"]),
|
|
49
|
+
shortcut(
|
|
50
|
+
action: "Navigate rows / actions",
|
|
51
|
+
any_of: [["↑"], ["↓"]],
|
|
52
|
+
keys_aria_label: "Up arrow or down arrow"
|
|
53
|
+
),
|
|
54
|
+
shortcut(action: "Open record", keys: ["↵"]),
|
|
55
|
+
shortcut(action: "Select / deselect row", keys: ["Space"]),
|
|
56
|
+
shortcut(action: "Deselect rows", keys: ["Esc"]),
|
|
57
|
+
shortcut(action: "Table view", keys: ["V", "T"]),
|
|
58
|
+
shortcut(action: "Grid view", keys: ["V", "G"]),
|
|
59
|
+
shortcut(action: "Map view", keys: ["V", "M"])
|
|
39
60
|
]
|
|
40
61
|
)
|
|
41
62
|
]
|
|
@@ -126,6 +126,12 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
126
126
|
def render_actions_list(actions_list)
|
|
127
127
|
return unless can_see_the_actions_button?
|
|
128
128
|
|
|
129
|
+
# Actions button hotkey "a" only on index pages (non-nested).
|
|
130
|
+
# Pass as_row_control so the template can use hotkey_original for row controls,
|
|
131
|
+
# allowing the index-row-navigator controller to manage hotkey visibility.
|
|
132
|
+
as_row_control = @item.present?
|
|
133
|
+
hotkey = "a" if instance_of?(Avo::Views::ResourceIndexComponent) && @reflection.nil?
|
|
134
|
+
|
|
129
135
|
render Avo::ActionsComponent.new(
|
|
130
136
|
actions: @actions,
|
|
131
137
|
resource: @resource,
|
|
@@ -139,7 +145,8 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
139
145
|
icon: actions_list.icon,
|
|
140
146
|
icon_class: actions_list.icon_class,
|
|
141
147
|
title: actions_list.title,
|
|
142
|
-
as_row_control
|
|
148
|
+
as_row_control:,
|
|
149
|
+
hotkey: hotkey
|
|
143
150
|
)
|
|
144
151
|
end
|
|
145
152
|
|
|
@@ -151,6 +158,13 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
151
158
|
policy_method = is_a_related_resource? ? :can_delete? : :can_see_the_destroy_button?
|
|
152
159
|
return unless send policy_method
|
|
153
160
|
|
|
161
|
+
# Row hotkeys: detect if we're rendering in a row control and use data-hotkey-original
|
|
162
|
+
# so the index-row-navigator controller can manage the hotkey visibility.
|
|
163
|
+
# Same as edit button: prevents @github/hotkey from registering all row buttons at once.
|
|
164
|
+
is_row_control = row_controls_context?
|
|
165
|
+
hotkey_attr = is_row_control ? :hotkey_original : :hotkey
|
|
166
|
+
data_attrs = {hotkey_attr => "d"}
|
|
167
|
+
|
|
154
168
|
a_link destroy_path,
|
|
155
169
|
style: :text,
|
|
156
170
|
color: :red,
|
|
@@ -158,7 +172,7 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
158
172
|
title: control.title,
|
|
159
173
|
aria_label: control.title,
|
|
160
174
|
data: {
|
|
161
|
-
|
|
175
|
+
**data_attrs,
|
|
162
176
|
turbo_confirm: t("avo.are_you_sure", item: @resource.record.model_name.name.downcase),
|
|
163
177
|
turbo_method: :delete,
|
|
164
178
|
target: "control:destroy",
|
|
@@ -193,12 +207,21 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
193
207
|
def render_edit_button(control)
|
|
194
208
|
return unless can_see_the_edit_button?
|
|
195
209
|
|
|
210
|
+
# Row hotkeys are handled by index-row-navigator controller:
|
|
211
|
+
# - Use data-hotkey-original for index row controls (controller moves it to data-hotkey when row is focused)
|
|
212
|
+
# - Use data-hotkey directly for show page buttons (always available)
|
|
213
|
+
# This prevents the @github/hotkey library from registering all row buttons at once,
|
|
214
|
+
# which would cause the "last-registered wins" problem.
|
|
215
|
+
is_row_control = row_controls_context?
|
|
216
|
+
hotkey_attr = is_row_control ? :hotkey_original : :hotkey
|
|
217
|
+
data_attrs = {hotkey_attr => "e"}
|
|
218
|
+
|
|
196
219
|
a_link edit_path,
|
|
197
220
|
color: :accent,
|
|
198
221
|
style: :primary,
|
|
199
222
|
title: control.title,
|
|
200
223
|
data: {
|
|
201
|
-
|
|
224
|
+
**data_attrs,
|
|
202
225
|
tippy: control.title ? :tooltip : nil
|
|
203
226
|
}.compact,
|
|
204
227
|
icon: "tabler/outline/edit" do
|
|
@@ -223,12 +246,14 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
223
246
|
def render_create_button(control)
|
|
224
247
|
return unless can_see_the_create_button?
|
|
225
248
|
|
|
249
|
+
hotkey = "c" if instance_of?(Avo::Views::ResourceIndexComponent) && @reflection.nil?
|
|
250
|
+
|
|
226
251
|
a_link create_path,
|
|
227
252
|
color: :accent,
|
|
228
253
|
style: :primary,
|
|
229
254
|
icon: "tabler/outline/plus",
|
|
230
255
|
data: {
|
|
231
|
-
hotkey
|
|
256
|
+
hotkey:,
|
|
232
257
|
target: :create
|
|
233
258
|
} do
|
|
234
259
|
control.label
|
|
@@ -249,6 +274,10 @@ class Avo::ResourceComponent < Avo::BaseComponent
|
|
|
249
274
|
end
|
|
250
275
|
end
|
|
251
276
|
|
|
277
|
+
def row_controls_context?
|
|
278
|
+
is_a?(Avo::Index::ResourceControlsComponent)
|
|
279
|
+
end
|
|
280
|
+
|
|
252
281
|
def render_link_to(link)
|
|
253
282
|
a_link link.path,
|
|
254
283
|
color: link.color,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<% if @collapsable %>
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
class="<%= class_names("sidebar-section__header justify-between", "cursor-pointer": @collapsable) %>"
|
|
5
|
+
data-action="click->menu#triggerCollapse"
|
|
6
|
+
data-menu-target="trigger"
|
|
7
|
+
aria-expanded="<%= !@collapsed %>"
|
|
8
|
+
>
|
|
9
|
+
<div class="sidebar-section__header-inner">
|
|
10
|
+
<%= helpers.svg @icon, class: "sidebar-section__icon sidebar-icon" if @icon.present? %>
|
|
11
|
+
<span><%= @title %></span>
|
|
12
|
+
</div>
|
|
13
|
+
<span class="sidebar-section__icon sidebar-icon <%= 'sidebar-icon--collapsed' if @collapsed %>"
|
|
14
|
+
data-menu-target="svg"
|
|
15
|
+
>
|
|
16
|
+
<%= helpers.svg 'tabler/outline/chevron-down' %>
|
|
17
|
+
</span>
|
|
18
|
+
</button>
|
|
19
|
+
<% else %>
|
|
20
|
+
<div class="sidebar-section__header">
|
|
21
|
+
<%= helpers.svg @icon, class: "sidebar-section__icon sidebar-icon" if @icon.present? %>
|
|
22
|
+
<span><%= @title %></span>
|
|
23
|
+
</div>
|
|
24
|
+
<% end %>
|
|
@@ -1,30 +1,10 @@
|
|
|
1
1
|
<%= tag.div class: "sidebar-section", data: data do %>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
data-menu-target="trigger"
|
|
9
|
-
aria-expanded="<%= !collapsed %>"
|
|
10
|
-
>
|
|
11
|
-
<div class="sidebar-section__header-inner">
|
|
12
|
-
<%= helpers.svg icon, class: "sidebar-section__icon sidebar-icon" if icon.present? %>
|
|
13
|
-
<span><%= item.name %></span>
|
|
14
|
-
</div>
|
|
15
|
-
<span class="sidebar-section__icon sidebar-icon <%= 'sidebar-icon--collapsed' if collapsed %>"
|
|
16
|
-
data-menu-target="svg"
|
|
17
|
-
>
|
|
18
|
-
<%= helpers.svg 'tabler/outline/chevron-down' %>
|
|
19
|
-
</span>
|
|
20
|
-
</button>
|
|
21
|
-
<% else %>
|
|
22
|
-
<div class="sidebar-section__header">
|
|
23
|
-
<%= helpers.svg icon, class: "sidebar-section__icon sidebar-icon" if icon.present? %>
|
|
24
|
-
<span><%= item.name %></span>
|
|
25
|
-
</div>
|
|
26
|
-
<% end %>
|
|
27
|
-
<% end %>
|
|
2
|
+
<%= render Avo::Sidebar::HeadingComponent.new(
|
|
3
|
+
title: item.name,
|
|
4
|
+
icon: icon,
|
|
5
|
+
collapsable: collapsable,
|
|
6
|
+
collapsed: collapsed
|
|
7
|
+
) %>
|
|
28
8
|
|
|
29
9
|
<%= content_tag :div,
|
|
30
10
|
hidden: collapsed,
|
|
@@ -17,23 +17,27 @@
|
|
|
17
17
|
<%= render custom_sidebar_component %>
|
|
18
18
|
<% else %>
|
|
19
19
|
<% if dashboards.present? %>
|
|
20
|
-
<div>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
<div class="sidebar-section">
|
|
21
|
+
<%= render Avo::Sidebar::HeadingComponent.new title: t('avo.dashboards'), icon: "tabler/outline/layout-dashboard" %>
|
|
22
|
+
|
|
23
|
+
<% dashboards.sort_by { |r| r.navigation_label }.each do |dashboard| %>
|
|
24
|
+
<%= render Avo::Sidebar::LinkComponent.new label: dashboard.navigation_label, path: helpers.avo_dashboards.dashboard_path(dashboard), hotkey: dashboard.try(:hotkey).presence %>
|
|
25
|
+
<% end %>
|
|
26
26
|
</div>
|
|
27
27
|
<% end %>
|
|
28
28
|
|
|
29
|
-
<div class="
|
|
29
|
+
<div class="sidebar-section">
|
|
30
|
+
<%= render Avo::Sidebar::HeadingComponent.new title: t('avo.resources'), icon: "tabler/outline/topology-star-3" %>
|
|
31
|
+
|
|
30
32
|
<% resources.sort_by { |r| r.navigation_label }.each do |resource| %>
|
|
31
33
|
<%= render Avo::Sidebar::LinkComponent.new label: resource.navigation_label, path: helpers.resources_path(resource: resource), icon: resource.icon, hotkey: resource.try(:hotkey).presence %>
|
|
32
34
|
<% end %>
|
|
33
35
|
</div>
|
|
34
36
|
|
|
35
37
|
<% if tools.present? %>
|
|
36
|
-
<div class="
|
|
38
|
+
<div class="sidebar-section">
|
|
39
|
+
<%= render Avo::Sidebar::HeadingComponent.new title: t('avo.tools'), icon: "tabler/outline/tool" %>
|
|
40
|
+
|
|
37
41
|
<% tools.each do |partial| %>
|
|
38
42
|
<%= render partial: "/avo/sidebar/items/#{partial}" %>
|
|
39
43
|
<% end %>
|
|
@@ -41,7 +45,9 @@
|
|
|
41
45
|
<% end %>
|
|
42
46
|
|
|
43
47
|
<% if pages.present? %>
|
|
44
|
-
<div class="
|
|
48
|
+
<div class="sidebar-section">
|
|
49
|
+
<%= render Avo::Sidebar::HeadingComponent.new title: t('avo.pages'), icon: "tabler/outline/forms" %>
|
|
50
|
+
|
|
45
51
|
<% pages.each do |page| %>
|
|
46
52
|
<%= render Avo::Sidebar::LinkComponent.new label: page.get_navigation_label, path: page.path %>
|
|
47
53
|
<% end %>
|
|
@@ -49,7 +55,7 @@
|
|
|
49
55
|
<% end %>
|
|
50
56
|
|
|
51
57
|
<% if Avo::MediaLibrary.configuration.visible? %>
|
|
52
|
-
<%= render Avo::Sidebar::LinkComponent.new label: 'Media Library', path: helpers.avo.media_library_index_path, active: :exclusive %>
|
|
58
|
+
<%= render Avo::Sidebar::LinkComponent.new label: 'Media Library', path: helpers.avo.media_library_index_path, active: :exclusive, icon: "tabler/outline/photo-star" %>
|
|
53
59
|
<% end %>
|
|
54
60
|
<% end %>
|
|
55
61
|
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
id: @id,
|
|
4
4
|
placeholder: @placeholder,
|
|
5
5
|
class: class_names(
|
|
6
|
+
@classes,
|
|
6
7
|
"search-input__input",
|
|
7
|
-
|
|
8
|
-
@
|
|
8
|
+
"search-input__input--with-shortcut": @shortcut == :slash,
|
|
9
|
+
"search-input__input--with-two-key-shortcut": @shortcut == :cmd_k
|
|
9
10
|
),
|
|
10
11
|
disabled: @disabled,
|
|
11
12
|
autocomplete: "off",
|
|
@@ -16,7 +17,11 @@
|
|
|
16
17
|
<%= helpers.svg "tabler/outline/search" %>
|
|
17
18
|
</span>
|
|
18
19
|
|
|
19
|
-
<% if @
|
|
20
|
+
<% if @shortcut == :slash %>
|
|
21
|
+
<span class="search-input__suffix" aria-hidden="true">
|
|
22
|
+
<kbd>/</kbd>
|
|
23
|
+
</span>
|
|
24
|
+
<% elsif @shortcut == :cmd_k %>
|
|
20
25
|
<span class="search-input__suffix" aria-hidden="true">
|
|
21
26
|
<kbd>
|
|
22
27
|
<abbr title="Command" class="no-underline os-pc:hidden">⌘</abbr>
|
|
@@ -6,7 +6,7 @@ class Avo::UI::SearchInputComponent < Avo::BaseComponent
|
|
|
6
6
|
prop :value
|
|
7
7
|
prop :placeholder
|
|
8
8
|
prop :disabled, default: false
|
|
9
|
-
prop :
|
|
9
|
+
prop :shortcut, default: nil # :slash or :cmd_k
|
|
10
10
|
prop :classes
|
|
11
11
|
prop :data, default: -> { {} }
|
|
12
12
|
end
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
model_name: @resource.model_name.to_s,
|
|
6
6
|
resource_name: @resource.class.to_s,
|
|
7
7
|
**@resource.stimulus_data_attributes
|
|
8
|
-
} do %>
|
|
8
|
+
}.tap { |d| d[:controller] = "#{d[:controller]} index-row-navigator" if @reflection.blank? } do %>
|
|
9
9
|
|
|
10
10
|
<%= render_cards_component %>
|
|
11
11
|
|
|
@@ -86,8 +86,9 @@
|
|
|
86
86
|
value: params[:q],
|
|
87
87
|
id: nil,
|
|
88
88
|
placeholder: t('avo.search.placeholder'),
|
|
89
|
+
shortcut: :slash,
|
|
89
90
|
data: {
|
|
90
|
-
action: "input->resource-search#search",
|
|
91
|
+
action: "input->resource-search#search keydown->resource-search#blurOnEscape",
|
|
91
92
|
"resource-search-target": "input"
|
|
92
93
|
}) %>
|
|
93
94
|
<% end %>
|
|
@@ -10,7 +10,7 @@ import { install } from '@github/hotkey'
|
|
|
10
10
|
import tippy from 'tippy.js'
|
|
11
11
|
|
|
12
12
|
import { LocalStorageService } from './js/local-storage-service'
|
|
13
|
-
import { installGlobalHotkeys } from './js/global_hotkeys'
|
|
13
|
+
import { attachHotkeyFeedback, installGlobalHotkeys } from './js/global_hotkeys'
|
|
14
14
|
|
|
15
15
|
import './js/active-storage'
|
|
16
16
|
import './js/controllers'
|
|
@@ -20,6 +20,7 @@ import './js/custom-stream-actions'
|
|
|
20
20
|
function installHotkeys(root = document) {
|
|
21
21
|
root.querySelectorAll('[data-hotkey]').forEach((el) => {
|
|
22
22
|
install(el)
|
|
23
|
+
attachHotkeyFeedback(el)
|
|
23
24
|
})
|
|
24
25
|
}
|
|
25
26
|
|