Skip to content

Commit d3a0bb1

Browse files
committed
Redesign collapsible sidebar with toggle button and vanilla AA fallback
- Fall back to vanilla ActiveAdmin build_page_content when no sidebar options are set, producing identical HTML for unmodified resources - Replace separate collapse/uncollapse buttons with a single toggle button positioned at the sidebar edge using absolute positioning - Toggle button arrow icon flips direction on collapse/expand via icono-caret class swap - Sidebar collapses to width:0 with CSS transitions instead of display:none, allowing smooth animation and proper content reflow - Add per-resource sidebar state isolation via session keys - Support collapsible: true and collapsed: true options for both left_sidebar! and right_sidebar! with backward compatibility - Add deprecation warning for old collapsed-only API
1 parent 3461082 commit d3a0bb1

7 files changed

Lines changed: 364 additions & 61 deletions

File tree

app/assets/javascripts/active_admin_sidebar.js

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
11
(function() {
22
$(document).ready(function() {
3-
var $aa_content, set_collapsed_sidebar;
3+
var $aa_content, set_collapsed_sidebar, isLeftSidebar, collapseIcon, expandIcon;
44
if ($('body').hasClass('index') && ($('#active_admin_content').hasClass('collapsible_sidebar'))) {
55
$aa_content = $('#active_admin_content');
6-
$aa_content.find('.sidebar_section:first>h3').append('<span class="collapse_btn icono-caret-left" title="Hide sidebar"></span>');
7-
$aa_content.prepend('<span class="uncollapse_btn icono-caret-right" title="Show sidebar"></span>');
6+
isLeftSidebar = $aa_content.hasClass('left_sidebar');
7+
var isCollapsed = $aa_content.hasClass('collapsed_sidebar');
8+
9+
if (isLeftSidebar) {
10+
collapseIcon = 'icono-caret-left';
11+
expandIcon = 'icono-caret-right';
12+
} else {
13+
collapseIcon = 'icono-caret-right';
14+
expandIcon = 'icono-caret-left';
15+
}
16+
17+
var initialIcon = isCollapsed ? expandIcon : collapseIcon;
18+
var $toggleBtn = $('<span class="sidebar_toggle_btn ' + initialIcon + '" title="Toggle sidebar"></span>');
19+
20+
// Place button inside #sidebar but outside .sidebar_section
21+
// so it stays visible when sections are hidden
22+
var $sidebar = $aa_content.find('#sidebar');
23+
$sidebar.prepend($toggleBtn);
24+
825
set_collapsed_sidebar = function(value) {
926
return $.getJSON(this.href, {
1027
collapsed_sidebar: value
1128
});
1229
};
13-
return $aa_content.on('click', '.collapse_btn, .uncollapse_btn', function(e) {
30+
31+
$toggleBtn.on('click', function(e) {
1432
if (!$aa_content.hasClass('collapsed_sidebar')) {
1533
set_collapsed_sidebar(true);
16-
$aa_content.removeClass('left_sidebar');
1734
$aa_content.addClass('collapsed_sidebar');
18-
return $aa_content.trigger('collapsible_sidebar:collapsed');
35+
$toggleBtn.removeClass(collapseIcon).addClass(expandIcon);
36+
$aa_content.trigger('collapsible_sidebar:collapsed');
1937
} else {
2038
set_collapsed_sidebar(false);
2139
$aa_content.removeClass('collapsed_sidebar');
22-
$aa_content.addClass('left_sidebar');
23-
return $aa_content.trigger('collapsible_sidebar:uncollapsed');
40+
$toggleBtn.removeClass(expandIcon).addClass(collapseIcon);
41+
$aa_content.trigger('collapsible_sidebar:uncollapsed');
2442
}
2543
});
2644
}
Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
@import "active_admin_sidebar_pure_icons";
22

3+
$toggle-btn-size: 19px;
4+
5+
// === Left sidebar layout ===
6+
// Moves sidebar before main content in the DOM, so we need to
7+
// reverse the default AA float layout.
38
body.active_admin {
4-
#active_admin_content.left_sidebar, #active_admin_content.collapsed_sidebar {
9+
#active_admin_content.left_sidebar {
510
#sidebar {
611
display: block;
712
margin-left: 0;
@@ -25,67 +30,110 @@ body.active_admin {
2530
float: inherit;
2631
margin-left: 298px;
2732
width: auto;
33+
2834
#main_content {
2935
float: inherit;
3036
margin: 0;
31-
.tabs .comments {
32-
.active_admin_comment {
33-
clear: none;
34-
}
37+
38+
.tabs .comments .active_admin_comment {
39+
clear: none;
3540
}
3641
}
37-
3842
}
3943

4044
.table_tools:after {
4145
clear: none;
4246
padding-bottom: 16px;
4347
}
48+
}
49+
}
4450

45-
.collapse_btn, .uncollapse_btn {
46-
background-color: #767270;
47-
border-radius: 5px;
48-
color: #ffffff;
49-
cursor: pointer;
50-
}
51+
// === Collapsible sidebar: toggle button ===
52+
body.active_admin #active_admin_content.collapsible_sidebar {
53+
#sidebar {
54+
position: relative;
55+
transition: width 0.25s ease, margin-left 0.25s ease;
56+
}
5157

52-
.collapse_btn {
53-
clear: both;
54-
display: block;
55-
float: right;
56-
}
58+
#main_content_wrapper #main_content {
59+
transition: margin 0.25s ease;
60+
}
5761

58-
.uncollapse_btn {
59-
display: none;
60-
margin-top: 5px;
61-
position: absolute;
62-
}
62+
#main_content_wrapper {
63+
transition: margin 0.25s ease;
64+
}
6365

66+
.sidebar_toggle_btn {
67+
background-color: #767270;
68+
border-radius: 3px;
69+
color: #ffffff;
70+
cursor: pointer;
71+
z-index: 10;
72+
// Override icono's position: relative so absolute works
73+
position: absolute !important;
74+
top: 5px;
75+
}
76+
77+
// Left sidebar: button flush on the right edge of sidebar
78+
&.left_sidebar .sidebar_toggle_btn {
79+
right: -$toggle-btn-size;
80+
}
81+
82+
// Right sidebar: button flush on the left edge of sidebar
83+
&:not(.left_sidebar) .sidebar_toggle_btn {
84+
left: -$toggle-btn-size;
6485
}
6586
}
6687

67-
body.active_admin #active_admin_content.collapsed_sidebar {
88+
// === Collapsed state: left sidebar ===
89+
body.active_admin #active_admin_content.collapsible_sidebar.collapsed_sidebar.left_sidebar {
90+
#sidebar {
91+
width: 0;
92+
overflow: visible;
93+
94+
.sidebar_section {
95+
display: none;
96+
}
97+
}
98+
99+
.sidebar_toggle_btn {
100+
right: -$toggle-btn-size;
101+
}
68102

69103
#main_content_wrapper {
70-
margin-left: 30px;
104+
margin-left: $toggle-btn-size + 8px;
71105
}
106+
}
72107

108+
// === Collapsed state: right sidebar (default AA position) ===
109+
body.active_admin #active_admin_content.collapsible_sidebar.collapsed_sidebar:not(.left_sidebar) {
73110
#sidebar {
74-
display: none;
111+
width: 0;
112+
margin-left: 0;
113+
overflow: visible;
114+
115+
.sidebar_section {
116+
display: none;
117+
}
75118
}
76119

77-
.uncollapse_btn {
78-
display: block;
120+
.sidebar_toggle_btn {
121+
left: -$toggle-btn-size;
79122
}
80123

124+
#main_content_wrapper #main_content {
125+
margin-right: $toggle-btn-size + 8px;
126+
}
81127
}
82128

83-
body.active_admin.index #active_admin_content.with_sidebar.collapsible_sidebar {
129+
// === Left sidebar expanded + collapsible: fix margin ===
130+
body.active_admin.index #active_admin_content.with_sidebar.left_sidebar.collapsible_sidebar {
84131
#main_content_wrapper #main_content {
85132
margin-right: 0;
86133
}
87134
}
88135

136+
// === Comments overflow fix ===
89137
.with_sidebar .comments .active_admin_comment {
90138
overflow: auto;
91139
}

lib/active_admin_sidebar/activeadmin_views_pages_base.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
module ActiveAdminSidebar::ActiveAdminViewsPagesBase
22

33
def build_page_content
4+
# When no sidebar options are set, fall back to vanilla ActiveAdmin behavior
5+
return super unless sidebar_options_present?
6+
47
build_flash_messages
58
div id: "active_admin_content", class: main_content_classes do
69
build_sidebar unless skip_sidebar? || right_sidebar?
@@ -17,12 +20,16 @@ def build_sidebar
1720
end
1821
end
1922

23+
def sidebar_options_present?
24+
assigns[:sidebar_options].present?
25+
end
26+
2027
def left_sidebar?
2128
assigns[:sidebar_options].try!(:[], :position) == :left
2229
end
2330

2431
def collapsible_sidebar?
25-
left_sidebar? && !!assigns[:sidebar_options].try!(:[], :collapsed)
32+
!!assigns[:sidebar_options].try!(:[], :collapsible)
2633
end
2734

2835
def sidebar_is_collapsed?
@@ -40,6 +47,7 @@ def main_content_classes
4047
else
4148
classes << "with_sidebar"
4249
classes << "left_sidebar" if left_sidebar?
50+
classes << "right_sidebar" if right_sidebar?
4351
if collapsible_sidebar?
4452
classes << "collapsible_sidebar"
4553
classes << "collapsed_sidebar" if sidebar_is_collapsed?

lib/active_admin_sidebar/positions.rb

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,54 @@ module ActiveAdminSidebar
22
module Positions
33
def left_sidebar!(options = {})
44
@sidebar_options = { position: :left }
5-
if options.fetch(:collapsed, false)
6-
collapsed_sidebar
7-
@sidebar_options.merge!(
8-
is_collapsed: session[:collapsed_sidebar],
9-
collapsed: true
5+
6+
collapsible = options.fetch(:collapsible, false)
7+
collapsed = options.fetch(:collapsed, false)
8+
9+
# Backward compatibility: `collapsed: true` without `collapsible:` means collapsible (not start-collapsed)
10+
if collapsed && !options.key?(:collapsible)
11+
collapsible = true
12+
collapsed = false
13+
ActiveSupport::Deprecation.warn(
14+
"left_sidebar!(collapsed: true) is deprecated. " \
15+
"Use left_sidebar!(collapsible: true) instead, or " \
16+
"left_sidebar!(collapsible: true, collapsed: true) to start collapsed."
1017
)
1118
end
19+
20+
apply_collapsible_options(collapsible, collapsed) if collapsible
1221
end
1322

14-
def right_sidebar!
23+
def right_sidebar!(options = {})
1524
@sidebar_options = { position: :right }
25+
26+
collapsible = options.fetch(:collapsible, false)
27+
collapsed = options.fetch(:collapsed, false)
28+
29+
apply_collapsible_options(collapsible, collapsed) if collapsible
30+
end
31+
32+
private
33+
34+
def apply_collapsible_options(collapsible, collapsed)
35+
session_key = :"collapsed_sidebar_#{controller_name}"
36+
handle_collapsed_sidebar_request(session_key)
37+
is_collapsed = if session[session_key].nil?
38+
collapsed
39+
else
40+
session[session_key]
41+
end
42+
@sidebar_options.merge!(
43+
collapsible: true,
44+
is_collapsed: is_collapsed
45+
)
1646
end
1747

18-
def collapsed_sidebar
48+
def handle_collapsed_sidebar_request(session_key)
1949
if request.xhr?
2050
if params[:collapsed_sidebar].present?
2151
collapsed = params[:collapsed_sidebar].to_s == 'true'
22-
session[:collapsed_sidebar] = collapsed
52+
session[session_key] = collapsed
2353
render json: { collapsed_sidebar: collapsed } and return
2454
end
2555
end

0 commit comments

Comments
 (0)