yummy-guide-generic-administrate 0.6.2 → 0.7.1
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/app/assets/javascripts/yummy_guide_administrate/resizable_navigation.js +242 -0
- data/app/assets/javascripts/yummy_guide_administrate/sticky_left_columns.js +12 -1
- data/app/assets/javascripts/yummy_guide_administrate/sticky_table_headers.js +13 -1
- data/app/assets/stylesheets/yummy_guide_administrate/_resizable_navigation.scss +156 -0
- data/app/assets/stylesheets/yummy_guide_administrate/components.scss +23 -0
- data/app/dashboards/yummy_guide/administrate/application_dashboard.rb +12 -1
- data/app/helpers/yummy_guide/administrate/collection_helper.rb +27 -9
- data/app/helpers/yummy_guide/administrate/filter_controls_helper.rb +22 -4
- data/app/views/yummy_guide/administrate/administrate/application/_collection.html.erb +1 -0
- data/lib/yummy_guide/administrate/engine.rb +1 -0
- data/lib/yummy_guide/administrate/version.rb +1 -1
- data/spec/yummy_guide/administrate/application_dashboard_spec.rb +24 -0
- data/spec/yummy_guide/administrate/collection_helper_spec.rb +17 -0
- data/spec/yummy_guide/administrate/filter_controls_helper_spec.rb +4 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a0a5f41bc5f403a7d4da80d9a2b66c7313a9370374e171d2916b96de8aea2a8d
|
|
4
|
+
data.tar.gz: 77c5494b19b7c2364477e394c954e5640ac737658e05ec481ecef62aafc30c59
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73ea4e2fb99ffc3e16393fc105e93b39e0812ad313f4e88f95a333ae18a10132e7207e482c956f37dad5391c8ceb6ab246724adc6e413fe8d5b012c3d45c03fc
|
|
7
|
+
data.tar.gz: e891e0b581251db97ac188c934bef264d77790b47b154e44dd57f4d82cc57e4bd1f8d5a48e1537cf710e0e585935ff10eb889c3bac690a1455d16477b979b069
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
var STORAGE_KEY = "yummyGuideAdministrate.navigationWidthPx";
|
|
3
|
+
var LEGACY_STORAGE_KEYS = ["wowTokyo.adminNavigationWidthPx"];
|
|
4
|
+
var WIDTH_VARIABLE = "--admin-navigation-width";
|
|
5
|
+
var NAVIGATION_SELECTOR = ".app-container > .navigation, body.admin-body > aside";
|
|
6
|
+
var HANDLE_CLASS = "admin-navigation-resize-handle";
|
|
7
|
+
var SCROLL_AREA_CLASS = "admin-navigation-scroll-area";
|
|
8
|
+
var TOOLTIP_CLASS = "admin-navigation-tooltip";
|
|
9
|
+
var TOOLTIP_VISIBLE_CLASS = "admin-navigation-tooltip--visible";
|
|
10
|
+
var RESIZING_BODY_CLASS = "admin-navigation-is-resizing";
|
|
11
|
+
var RESIZING_NAVIGATION_CLASS = "admin-navigation--resizing";
|
|
12
|
+
var DESKTOP_MEDIA_QUERY = "(min-width: 768px)";
|
|
13
|
+
var MIN_WIDTH_PX = 25;
|
|
14
|
+
var MAX_WIDTH_PX = 250;
|
|
15
|
+
|
|
16
|
+
function ready(callback) {
|
|
17
|
+
if (document.readyState === "loading") {
|
|
18
|
+
document.addEventListener("DOMContentLoaded", callback, { once: true });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
callback();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function toArray(nodeList) {
|
|
26
|
+
return Array.prototype.slice.call(nodeList || []);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isDesktop() {
|
|
30
|
+
return window.matchMedia(DESKTOP_MEDIA_QUERY).matches;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parseStoredWidth(value) {
|
|
34
|
+
if (!value) return null;
|
|
35
|
+
|
|
36
|
+
var width = parseFloat(value);
|
|
37
|
+
return Number.isFinite(width) ? width : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function storedWidthForKey(key) {
|
|
41
|
+
try {
|
|
42
|
+
return parseStoredWidth(window.localStorage.getItem(key));
|
|
43
|
+
} catch (_error) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readStoredWidth() {
|
|
49
|
+
var width = storedWidthForKey(STORAGE_KEY);
|
|
50
|
+
if (width !== null) return width;
|
|
51
|
+
|
|
52
|
+
for (var index = 0; index < LEGACY_STORAGE_KEYS.length; index += 1) {
|
|
53
|
+
width = storedWidthForKey(LEGACY_STORAGE_KEYS[index]);
|
|
54
|
+
if (width !== null) return width;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function saveWidth(width) {
|
|
61
|
+
try {
|
|
62
|
+
window.localStorage.setItem(STORAGE_KEY, Math.round(width) + "px");
|
|
63
|
+
} catch (_error) {
|
|
64
|
+
// localStorage may be unavailable in private browsing or restricted contexts.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function clampWidth(width) {
|
|
69
|
+
return Math.min(Math.max(width, MIN_WIDTH_PX), MAX_WIDTH_PX);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function applyWidth(width, body) {
|
|
73
|
+
var clampedWidth = clampWidth(width);
|
|
74
|
+
body.style.setProperty(WIDTH_VARIABLE, Math.round(clampedWidth) + "px");
|
|
75
|
+
|
|
76
|
+
return clampedWidth;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function applyStoredWidth(body) {
|
|
80
|
+
var storedWidth = readStoredWidth();
|
|
81
|
+
if (storedWidth === null) return null;
|
|
82
|
+
|
|
83
|
+
return applyWidth(storedWidth, body);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resizeWidthFromPointer(event, body, navigation) {
|
|
87
|
+
var navigationLeft = navigation.getBoundingClientRect().left;
|
|
88
|
+
return applyWidth(event.clientX - navigationLeft, body);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function childWithClass(element, className) {
|
|
92
|
+
return toArray(element.children).find(function(child) {
|
|
93
|
+
return child.classList.contains(className);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function setupNavigationScrollArea(navigation) {
|
|
98
|
+
var existingScrollArea = childWithClass(navigation, SCROLL_AREA_CLASS);
|
|
99
|
+
if (existingScrollArea) return existingScrollArea;
|
|
100
|
+
|
|
101
|
+
var scrollArea = document.createElement("div");
|
|
102
|
+
var handle = childWithClass(navigation, HANDLE_CLASS);
|
|
103
|
+
|
|
104
|
+
scrollArea.className = SCROLL_AREA_CLASS;
|
|
105
|
+
toArray(navigation.childNodes).forEach(function(node) {
|
|
106
|
+
if (node === handle) return;
|
|
107
|
+
scrollArea.appendChild(node);
|
|
108
|
+
});
|
|
109
|
+
navigation.insertBefore(scrollArea, handle || null);
|
|
110
|
+
|
|
111
|
+
return scrollArea;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function setupNavigationTooltips(container) {
|
|
115
|
+
var tooltipTargets = toArray(container.querySelectorAll("a, button"));
|
|
116
|
+
if (tooltipTargets.length === 0) return;
|
|
117
|
+
|
|
118
|
+
var tooltip = document.createElement("div");
|
|
119
|
+
var activeTarget = null;
|
|
120
|
+
|
|
121
|
+
tooltip.className = TOOLTIP_CLASS;
|
|
122
|
+
tooltip.setAttribute("role", "tooltip");
|
|
123
|
+
document.body.appendChild(tooltip);
|
|
124
|
+
|
|
125
|
+
function hideTooltip() {
|
|
126
|
+
tooltip.classList.remove(TOOLTIP_VISIBLE_CLASS);
|
|
127
|
+
activeTarget = null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function positionTooltip() {
|
|
131
|
+
if (!activeTarget || !isDesktop()) {
|
|
132
|
+
hideTooltip();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
var targetRect = activeTarget.getBoundingClientRect();
|
|
137
|
+
var tooltipRect = tooltip.getBoundingClientRect();
|
|
138
|
+
var top = Math.min(
|
|
139
|
+
Math.max(targetRect.top + (targetRect.height / 2) - (tooltipRect.height / 2), 8),
|
|
140
|
+
window.innerHeight - tooltipRect.height - 8
|
|
141
|
+
);
|
|
142
|
+
var left = Math.min(targetRect.right + 8, window.innerWidth - tooltipRect.width - 8);
|
|
143
|
+
|
|
144
|
+
tooltip.style.left = Math.max(left, 8) + "px";
|
|
145
|
+
tooltip.style.top = top + "px";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function showTooltip(target) {
|
|
149
|
+
var label = target.textContent.trim();
|
|
150
|
+
if (!label || !isDesktop()) return;
|
|
151
|
+
|
|
152
|
+
activeTarget = target;
|
|
153
|
+
tooltip.textContent = label;
|
|
154
|
+
tooltip.classList.add(TOOLTIP_VISIBLE_CLASS);
|
|
155
|
+
positionTooltip();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
tooltipTargets.forEach(function(target) {
|
|
159
|
+
target.addEventListener("mouseenter", function() {
|
|
160
|
+
showTooltip(target);
|
|
161
|
+
});
|
|
162
|
+
target.addEventListener("mouseleave", hideTooltip);
|
|
163
|
+
target.addEventListener("focus", function() {
|
|
164
|
+
showTooltip(target);
|
|
165
|
+
});
|
|
166
|
+
target.addEventListener("blur", hideTooltip);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
window.addEventListener("resize", positionTooltip);
|
|
170
|
+
window.addEventListener("scroll", positionTooltip, true);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function addResizeHandle(body, navigation) {
|
|
174
|
+
if (navigation.querySelector("." + HANDLE_CLASS)) return;
|
|
175
|
+
|
|
176
|
+
var handle = document.createElement("div");
|
|
177
|
+
var latestWidth = null;
|
|
178
|
+
|
|
179
|
+
handle.className = HANDLE_CLASS;
|
|
180
|
+
handle.setAttribute("role", "separator");
|
|
181
|
+
handle.setAttribute("aria-orientation", "vertical");
|
|
182
|
+
handle.setAttribute("aria-label", "Resize navigation");
|
|
183
|
+
navigation.appendChild(handle);
|
|
184
|
+
|
|
185
|
+
handle.addEventListener("pointerdown", function(event) {
|
|
186
|
+
if (!isDesktop() || (event.pointerType === "mouse" && event.button !== 0)) return;
|
|
187
|
+
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
latestWidth = navigation.getBoundingClientRect().width;
|
|
190
|
+
body.classList.add(RESIZING_BODY_CLASS);
|
|
191
|
+
navigation.classList.add(RESIZING_NAVIGATION_CLASS);
|
|
192
|
+
handle.setPointerCapture(event.pointerId);
|
|
193
|
+
|
|
194
|
+
function onPointerMove(moveEvent) {
|
|
195
|
+
latestWidth = resizeWidthFromPointer(moveEvent, body, navigation);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function stopResize() {
|
|
199
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
200
|
+
document.removeEventListener("pointerup", stopResize);
|
|
201
|
+
document.removeEventListener("pointercancel", stopResize);
|
|
202
|
+
body.classList.remove(RESIZING_BODY_CLASS);
|
|
203
|
+
navigation.classList.remove(RESIZING_NAVIGATION_CLASS);
|
|
204
|
+
|
|
205
|
+
if (latestWidth !== null) saveWidth(latestWidth);
|
|
206
|
+
latestWidth = null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
document.addEventListener("pointermove", onPointerMove);
|
|
210
|
+
document.addEventListener("pointerup", stopResize);
|
|
211
|
+
document.addEventListener("pointercancel", stopResize);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function setupResizableNavigation() {
|
|
216
|
+
var body = document.querySelector("body.admin-body") || document.body;
|
|
217
|
+
var navigation = document.querySelector(NAVIGATION_SELECTOR);
|
|
218
|
+
var scrollArea = null;
|
|
219
|
+
var resizeAnimationFrame = null;
|
|
220
|
+
|
|
221
|
+
if (!body || !navigation || navigation.dataset.resizableNavigationInitialized === "true") return;
|
|
222
|
+
|
|
223
|
+
navigation.dataset.resizableNavigationInitialized = "true";
|
|
224
|
+
applyStoredWidth(body);
|
|
225
|
+
scrollArea = setupNavigationScrollArea(navigation);
|
|
226
|
+
setupNavigationTooltips(scrollArea);
|
|
227
|
+
addResizeHandle(body, navigation);
|
|
228
|
+
|
|
229
|
+
window.addEventListener("resize", function() {
|
|
230
|
+
if (resizeAnimationFrame) {
|
|
231
|
+
window.cancelAnimationFrame(resizeAnimationFrame);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
resizeAnimationFrame = window.requestAnimationFrame(function() {
|
|
235
|
+
applyStoredWidth(body);
|
|
236
|
+
resizeAnimationFrame = null;
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
ready(setupResizableNavigation);
|
|
242
|
+
})();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
(function() {
|
|
2
2
|
var TABLE_SELECTOR = "table[data-fixed-columns-count]";
|
|
3
|
+
var MOBILE_MEDIA_QUERY = "(max-width: 767px)";
|
|
3
4
|
var resizeObservers = new WeakMap();
|
|
4
5
|
|
|
5
6
|
function directCells(row) {
|
|
@@ -31,10 +32,20 @@
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
function fixedColumnsCount(table) {
|
|
34
|
-
var
|
|
35
|
+
var rawCount = table.dataset.fixedColumnsCount || "0";
|
|
36
|
+
|
|
37
|
+
if (isMobile() && table.dataset.mobileFixedColumnsCount) {
|
|
38
|
+
rawCount = table.dataset.mobileFixedColumnsCount;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var parsedCount = parseInt(rawCount, 10);
|
|
35
42
|
return Number.isNaN(parsedCount) ? 0 : Math.max(parsedCount, 0);
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
function isMobile() {
|
|
46
|
+
return window.matchMedia && window.matchMedia(MOBILE_MEDIA_QUERY).matches;
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
function stickyOffsets(table, count) {
|
|
39
50
|
var headerRow = table.querySelector("thead tr");
|
|
40
51
|
if (!headerRow) return [];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
(function() {
|
|
2
2
|
var TABLE_SELECTOR = 'table[data-fixed-columns-count]';
|
|
3
3
|
var WRAPPER_SELECTOR = '[data-fixed-header-scroll], .scroll-table, .home-table__wrapper, .table-wrap, .af__table__content';
|
|
4
|
+
var MOBILE_MEDIA_QUERY = '(max-width: 767px)';
|
|
4
5
|
var resizeObservers = new WeakMap();
|
|
5
6
|
var observedScrolls = new WeakMap();
|
|
6
7
|
var observedFixedScrolls = new WeakMap();
|
|
@@ -36,10 +37,20 @@
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
function fixedColumnsCount(table) {
|
|
39
|
-
var
|
|
40
|
+
var rawCount = table.dataset.fixedColumnsCount || '0';
|
|
41
|
+
|
|
42
|
+
if (isMobile() && table.dataset.mobileFixedColumnsCount) {
|
|
43
|
+
rawCount = table.dataset.mobileFixedColumnsCount;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var parsedCount = parseInt(rawCount, 10);
|
|
40
47
|
return Number.isNaN(parsedCount) ? 0 : Math.max(parsedCount, 0);
|
|
41
48
|
}
|
|
42
49
|
|
|
50
|
+
function isMobile() {
|
|
51
|
+
return window.matchMedia && window.matchMedia(MOBILE_MEDIA_QUERY).matches;
|
|
52
|
+
}
|
|
53
|
+
|
|
43
54
|
function stickyOffsets(widths) {
|
|
44
55
|
var offsets = [];
|
|
45
56
|
var currentLeft = 0;
|
|
@@ -555,6 +566,7 @@
|
|
|
555
566
|
|
|
556
567
|
fixedTable.className = fixedHeaderTableClassName(sourceTable);
|
|
557
568
|
fixedTable.dataset.fixedColumnsCount = sourceTable.dataset.fixedColumnsCount || '0';
|
|
569
|
+
fixedTable.dataset.mobileFixedColumnsCount = sourceTable.dataset.mobileFixedColumnsCount || fixedTable.dataset.fixedColumnsCount;
|
|
558
570
|
fixedTable.style.width = '';
|
|
559
571
|
fixedTable.innerHTML = sourceHead.outerHTML;
|
|
560
572
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
@media screen and (min-width: 768px) {
|
|
2
|
+
.app-container > .navigation,
|
|
3
|
+
body.admin-body > aside {
|
|
4
|
+
--admin-navigation-default-width: 240px;
|
|
5
|
+
align-self: flex-start;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
flex: 0 0 clamp(25px, var(--admin-navigation-width, var(--admin-navigation-default-width)), 250px);
|
|
8
|
+
max-height: calc(100vh - 2rem);
|
|
9
|
+
min-width: 25px;
|
|
10
|
+
max-width: 250px;
|
|
11
|
+
overflow: visible;
|
|
12
|
+
position: sticky;
|
|
13
|
+
top: 1rem;
|
|
14
|
+
width: clamp(25px, var(--admin-navigation-width, var(--admin-navigation-default-width)), 250px);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body.admin-body > aside:not(.navigation) {
|
|
18
|
+
--admin-navigation-default-width: 13rem;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.app-container > .navigation {
|
|
22
|
+
max-height: calc(100vh - 3em);
|
|
23
|
+
max-height: calc(100dvh - 3em);
|
|
24
|
+
top: 1.5em;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.app-container > .main-content,
|
|
28
|
+
body.admin-body > main.admin-main {
|
|
29
|
+
flex: 1 1 auto;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
position: relative;
|
|
32
|
+
z-index: 1;
|
|
33
|
+
width: auto;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
body.admin-body > main.main-content {
|
|
37
|
+
width: auto;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.admin-navigation-resize-handle {
|
|
41
|
+
bottom: 0;
|
|
42
|
+
cursor: col-resize;
|
|
43
|
+
position: absolute;
|
|
44
|
+
right: 0;
|
|
45
|
+
top: 0;
|
|
46
|
+
touch-action: none;
|
|
47
|
+
width: 8px;
|
|
48
|
+
z-index: 2;
|
|
49
|
+
|
|
50
|
+
&::after {
|
|
51
|
+
background-color: transparent;
|
|
52
|
+
bottom: 0;
|
|
53
|
+
content: "";
|
|
54
|
+
left: 3px;
|
|
55
|
+
position: absolute;
|
|
56
|
+
top: 0;
|
|
57
|
+
transition: background-color 0.15s ease;
|
|
58
|
+
width: 2px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&:hover::after {
|
|
62
|
+
background-color: #9aa8ba;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.admin-navigation-scroll-area {
|
|
67
|
+
max-height: inherit;
|
|
68
|
+
overflow-x: hidden;
|
|
69
|
+
overflow-y: auto;
|
|
70
|
+
overscroll-behavior: contain;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.admin-navigation--resizing .admin-navigation-resize-handle::after {
|
|
74
|
+
background-color: #5d7596;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.admin-navigation-scroll-area .menubar,
|
|
78
|
+
.admin-navigation-scroll-area .menubar a,
|
|
79
|
+
.admin-navigation-scroll-area .menubar__group-label,
|
|
80
|
+
.admin-navigation-scroll-area .navigation__link,
|
|
81
|
+
.admin-navigation-scroll-area .navigation__link__secondary {
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.admin-navigation-scroll-area .menubar {
|
|
86
|
+
margin-right: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.admin-navigation-scroll-area .menubar li.menubar__item {
|
|
90
|
+
padding-left: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.admin-navigation-scroll-area .menubar li a,
|
|
94
|
+
.admin-navigation-scroll-area .navigation__link {
|
|
95
|
+
box-sizing: border-box;
|
|
96
|
+
margin-left: 0;
|
|
97
|
+
margin-right: 0;
|
|
98
|
+
overflow: hidden;
|
|
99
|
+
width: 100%;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.admin-navigation-scroll-area .menubar li.menubar__item a {
|
|
103
|
+
padding-left: 36px;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.admin-navigation-scroll-area .button_to {
|
|
107
|
+
margin: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.admin-navigation-scroll-area .btn_public,
|
|
111
|
+
.admin-navigation-scroll-area .btn_logout {
|
|
112
|
+
box-sizing: border-box;
|
|
113
|
+
margin-left: 0;
|
|
114
|
+
margin-right: 0;
|
|
115
|
+
min-width: 0;
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
text-overflow: clip;
|
|
118
|
+
white-space: nowrap;
|
|
119
|
+
width: 100%;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.admin-navigation-tooltip {
|
|
123
|
+
background-color: #1f2933;
|
|
124
|
+
border-radius: 4px;
|
|
125
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
|
|
126
|
+
color: #fff;
|
|
127
|
+
font-size: 0.85rem;
|
|
128
|
+
line-height: 1.4;
|
|
129
|
+
max-width: 260px;
|
|
130
|
+
opacity: 0;
|
|
131
|
+
padding: 6px 8px;
|
|
132
|
+
pointer-events: none;
|
|
133
|
+
position: fixed;
|
|
134
|
+
transform: translateX(-2px);
|
|
135
|
+
transition: opacity 0.12s ease, transform 0.12s ease;
|
|
136
|
+
white-space: nowrap;
|
|
137
|
+
z-index: 1000;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.admin-navigation-tooltip--visible {
|
|
141
|
+
opacity: 1;
|
|
142
|
+
transform: translateX(0);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
body.admin-navigation-is-resizing,
|
|
146
|
+
body.admin-navigation-is-resizing * {
|
|
147
|
+
cursor: col-resize !important;
|
|
148
|
+
user-select: none;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@media screen and (max-width: 767px) {
|
|
153
|
+
.admin-navigation-resize-handle {
|
|
154
|
+
display: none;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -661,6 +661,27 @@ table[data-fixed-columns-count] [data-fixed-header-column-min-width] {
|
|
|
661
661
|
}
|
|
662
662
|
|
|
663
663
|
@media (max-width: 767px) {
|
|
664
|
+
.scroll-table,
|
|
665
|
+
.sticky-table-scroll,
|
|
666
|
+
.home-table__wrapper,
|
|
667
|
+
.table-wrap,
|
|
668
|
+
.af__table__content {
|
|
669
|
+
max-width: 100%;
|
|
670
|
+
overflow-x: auto;
|
|
671
|
+
overflow-y: hidden;
|
|
672
|
+
-webkit-overflow-scrolling: touch;
|
|
673
|
+
overscroll-behavior-x: contain;
|
|
674
|
+
touch-action: pan-x pan-y;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.scroll-table table[data-fixed-columns-count],
|
|
678
|
+
.sticky-table-scroll table[data-fixed-columns-count],
|
|
679
|
+
.home-table__wrapper table[data-fixed-columns-count],
|
|
680
|
+
.table-wrap table[data-fixed-columns-count],
|
|
681
|
+
.af__table__content table[data-fixed-columns-count] {
|
|
682
|
+
min-width: max-content;
|
|
683
|
+
}
|
|
684
|
+
|
|
664
685
|
.yummy-guide-administrate-filter-form__title {
|
|
665
686
|
padding: 16px 16px 12px;
|
|
666
687
|
}
|
|
@@ -679,3 +700,5 @@ table[data-fixed-columns-count] [data-fixed-header-column-min-width] {
|
|
|
679
700
|
grid-template-columns: 1fr;
|
|
680
701
|
}
|
|
681
702
|
}
|
|
703
|
+
|
|
704
|
+
@import "resizable_navigation";
|
|
@@ -25,6 +25,18 @@ module YummyGuide
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def self.index_mobile_fixed_columns_count
|
|
29
|
+
if const_defined?(:INDEX_MOBILE_FIXED_COLUMNS_COUNT, false)
|
|
30
|
+
const_get(:INDEX_MOBILE_FIXED_COLUMNS_COUNT)
|
|
31
|
+
elsif const_defined?(:INDEX_FIXED_COLUMNS_COUNT, false)
|
|
32
|
+
[index_fixed_columns_count.to_i, 1].min
|
|
33
|
+
elsif superclass.respond_to?(:index_mobile_fixed_columns_count)
|
|
34
|
+
superclass.index_mobile_fixed_columns_count
|
|
35
|
+
else
|
|
36
|
+
[index_fixed_columns_count.to_i, 1].min
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
28
40
|
def self.collection_sortable_attributes
|
|
29
41
|
if const_defined?(:COLLECTION_SORTABLE_ATTRIBUTES, false)
|
|
30
42
|
const_get(:COLLECTION_SORTABLE_ATTRIBUTES)
|
|
@@ -43,4 +55,3 @@ module YummyGuide
|
|
|
43
55
|
end
|
|
44
56
|
end
|
|
45
57
|
end
|
|
46
|
-
|
|
@@ -9,16 +9,19 @@ module YummyGuide
|
|
|
9
9
|
].freeze
|
|
10
10
|
|
|
11
11
|
def yummy_guide_administrate_collection_table_fixed_columns_count(page:, collection_presenter:)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
fixed_columns_count = 0 if fixed_columns_count.negative?
|
|
12
|
+
yummy_guide_administrate_collection_fixed_columns_count_for(
|
|
13
|
+
page: page,
|
|
14
|
+
collection_presenter: collection_presenter,
|
|
15
|
+
method_name: :index_fixed_columns_count
|
|
16
|
+
)
|
|
17
|
+
end
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
def yummy_guide_administrate_collection_table_mobile_fixed_columns_count(page:, collection_presenter:)
|
|
20
|
+
yummy_guide_administrate_collection_fixed_columns_count_for(
|
|
21
|
+
page: page,
|
|
22
|
+
collection_presenter: collection_presenter,
|
|
23
|
+
method_name: :index_mobile_fixed_columns_count
|
|
24
|
+
)
|
|
22
25
|
rescue NoMethodError
|
|
23
26
|
0
|
|
24
27
|
end
|
|
@@ -87,6 +90,21 @@ module YummyGuide
|
|
|
87
90
|
|
|
88
91
|
private
|
|
89
92
|
|
|
93
|
+
def yummy_guide_administrate_collection_fixed_columns_count_for(page:, collection_presenter:, method_name:)
|
|
94
|
+
return 0 unless page.respond_to?(:instance_variable_defined?) && page.instance_variable_defined?(:@dashboard)
|
|
95
|
+
|
|
96
|
+
dashboard = page.instance_variable_get(:@dashboard)
|
|
97
|
+
return 0 unless dashboard&.class&.respond_to?(method_name)
|
|
98
|
+
|
|
99
|
+
fixed_columns_count = dashboard.class.public_send(method_name).to_i
|
|
100
|
+
fixed_columns_count = 0 if fixed_columns_count.negative?
|
|
101
|
+
|
|
102
|
+
attribute_count = collection_presenter.attribute_types.size
|
|
103
|
+
[fixed_columns_count, attribute_count].min
|
|
104
|
+
rescue NoMethodError
|
|
105
|
+
0
|
|
106
|
+
end
|
|
107
|
+
|
|
90
108
|
def yummy_guide_administrate_collection_link(content, href:, target: nil, html_class: "action-show", aria: nil, data: nil)
|
|
91
109
|
return content if href.blank?
|
|
92
110
|
|
|
@@ -17,7 +17,8 @@ module YummyGuide
|
|
|
17
17
|
extra_actions: [],
|
|
18
18
|
button_label: "Filter",
|
|
19
19
|
title: "Filter Options",
|
|
20
|
-
submit_label: "Filter"
|
|
20
|
+
submit_label: "Filter",
|
|
21
|
+
close_label: "Close"
|
|
21
22
|
)
|
|
22
23
|
dashboard ||= admin_filter_dashboard_from_page(page)
|
|
23
24
|
scope = form.to_s
|
|
@@ -63,7 +64,8 @@ module YummyGuide
|
|
|
63
64
|
locals: locals,
|
|
64
65
|
extra_actions: extra_actions,
|
|
65
66
|
title: title,
|
|
66
|
-
submit_label: submit_label
|
|
67
|
+
submit_label: submit_label,
|
|
68
|
+
close_label: close_label
|
|
67
69
|
)
|
|
68
70
|
])
|
|
69
71
|
end
|
|
@@ -104,7 +106,7 @@ module YummyGuide
|
|
|
104
106
|
.select { |field| field.visible?(self, locals) }
|
|
105
107
|
end
|
|
106
108
|
|
|
107
|
-
def admin_filter_form(fields:, scope:, path:, clear_path:, method:, current_values:, hidden_fields:, root_hidden_fields:, locals:, extra_actions:, title:, submit_label:)
|
|
109
|
+
def admin_filter_form(fields:, scope:, path:, clear_path:, method:, current_values:, hidden_fields:, root_hidden_fields:, locals:, extra_actions:, title:, submit_label:, close_label:)
|
|
108
110
|
form_with(
|
|
109
111
|
url: path,
|
|
110
112
|
scope: scope,
|
|
@@ -119,7 +121,7 @@ module YummyGuide
|
|
|
119
121
|
) do |f|
|
|
120
122
|
safe_join([
|
|
121
123
|
admin_filter_hidden_fields(scope, hidden_fields, root_hidden_fields),
|
|
122
|
-
|
|
124
|
+
admin_filter_form_header(title: title, close_label: close_label),
|
|
123
125
|
content_tag(:div, class: "filter-form__body") do
|
|
124
126
|
content_tag(:table, class: "filter_table") do
|
|
125
127
|
safe_join(fields.map { |field| field.row(self, f, scope, current_values, locals) })
|
|
@@ -136,6 +138,22 @@ module YummyGuide
|
|
|
136
138
|
end
|
|
137
139
|
end
|
|
138
140
|
|
|
141
|
+
def admin_filter_form_header(title:, close_label:)
|
|
142
|
+
content_tag(:div, class: "filter-form__header") do
|
|
143
|
+
safe_join([
|
|
144
|
+
content_tag(:h2, title, class: "filter-form__title"),
|
|
145
|
+
button_tag(
|
|
146
|
+
"x",
|
|
147
|
+
type: "button",
|
|
148
|
+
class: "filter-form__close",
|
|
149
|
+
data: { behavior: "filter-form-close" },
|
|
150
|
+
aria: { label: close_label },
|
|
151
|
+
title: close_label
|
|
152
|
+
)
|
|
153
|
+
])
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
139
157
|
def admin_filter_hidden_fields(scope, hidden_fields, root_hidden_fields)
|
|
140
158
|
root_tags = admin_filter_evaluated_hash(root_hidden_fields).map do |key, value|
|
|
141
159
|
hidden_field_tag(key, value)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<table
|
|
3
3
|
aria-labelledby="<%= table_title %>"
|
|
4
4
|
data-fixed-columns-count="<%= yummy_guide_administrate_collection_table_fixed_columns_count(page: page, collection_presenter: collection_presenter) %>"
|
|
5
|
+
data-mobile-fixed-columns-count="<%= yummy_guide_administrate_collection_table_mobile_fixed_columns_count(page: page, collection_presenter: collection_presenter) %>"
|
|
5
6
|
data-fixed-header-source
|
|
6
7
|
>
|
|
7
8
|
<thead>
|
|
@@ -14,6 +14,7 @@ module YummyGuide
|
|
|
14
14
|
yummy_guide_administrate/filter_controls.js
|
|
15
15
|
yummy_guide_administrate/filter_form.js
|
|
16
16
|
yummy_guide_administrate/sticky_left_columns.js
|
|
17
|
+
yummy_guide_administrate/resizable_navigation.js
|
|
17
18
|
yummy_guide_administrate/sticky_table_headers.js
|
|
18
19
|
]
|
|
19
20
|
end
|
|
@@ -18,6 +18,30 @@ RSpec.describe YummyGuide::Administrate::ApplicationDashboard do
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
describe ".index_mobile_fixed_columns_count" do
|
|
22
|
+
it "uses the subclass mobile constant when defined" do
|
|
23
|
+
subclass = Class.new(described_class)
|
|
24
|
+
subclass.const_set(:INDEX_FIXED_COLUMNS_COUNT, 5)
|
|
25
|
+
subclass.const_set(:INDEX_MOBILE_FIXED_COLUMNS_COUNT, 2)
|
|
26
|
+
|
|
27
|
+
expect(subclass.index_mobile_fixed_columns_count).to eq(2)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "caps a subclass desktop fixed column count to one by default" do
|
|
31
|
+
subclass = Class.new(described_class)
|
|
32
|
+
subclass.const_set(:INDEX_FIXED_COLUMNS_COUNT, 5)
|
|
33
|
+
|
|
34
|
+
expect(subclass.index_mobile_fixed_columns_count).to eq(1)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "keeps zero fixed columns on mobile when desktop fixed columns are zero" do
|
|
38
|
+
subclass = Class.new(described_class)
|
|
39
|
+
subclass.const_set(:INDEX_FIXED_COLUMNS_COUNT, 0)
|
|
40
|
+
|
|
41
|
+
expect(subclass.index_mobile_fixed_columns_count).to eq(0)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
21
45
|
describe ".collection_attribute_sortable?" do
|
|
22
46
|
it "uses collection attributes when explicit sortable attributes are absent" do
|
|
23
47
|
subclass = Class.new(described_class)
|
|
@@ -37,6 +37,23 @@ RSpec.describe YummyGuide::Administrate::CollectionHelper do
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
describe "#yummy_guide_administrate_collection_table_mobile_fixed_columns_count" do
|
|
41
|
+
# dashboard のモバイル固定列数も表示列数を超えないことを確認する
|
|
42
|
+
it "caps the mobile fixed column count by the number of visible attributes" do
|
|
43
|
+
dashboard_class = Class.new do
|
|
44
|
+
def self.index_mobile_fixed_columns_count
|
|
45
|
+
4
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
page = Object.new
|
|
50
|
+
page.instance_variable_set(:@dashboard, dashboard_class.new)
|
|
51
|
+
collection_presenter = Struct.new(:attribute_types).new({ id: :integer, name: :string })
|
|
52
|
+
|
|
53
|
+
expect(helper_host.yummy_guide_administrate_collection_table_mobile_fixed_columns_count(page: page, collection_presenter: collection_presenter)).to eq(2)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
40
57
|
describe "#yummy_guide_administrate_build_collection_cell" do
|
|
41
58
|
let(:present_path) { "/admin/articles/test-article" }
|
|
42
59
|
|
|
@@ -42,8 +42,12 @@ RSpec.describe YummyGuide::Administrate::FilterControlsHelper do
|
|
|
42
42
|
search_options: { keyword: "tokyo", status: "closed" }
|
|
43
43
|
)
|
|
44
44
|
document = fragment(html)
|
|
45
|
+
close_button = document.at_css('button.filter-form__close[data-behavior="filter-form-close"]')
|
|
45
46
|
|
|
46
47
|
expect(document.at_css("#reserv-filter-options > a.button").text).to eq("Filter")
|
|
48
|
+
expect(document.at_css(".filter-form__header .filter-form__title").text).to eq("Filter Options")
|
|
49
|
+
expect(close_button["aria-label"]).to eq("Close")
|
|
50
|
+
expect(close_button["type"]).to eq("button")
|
|
47
51
|
expect(document.at_css("form.filter-form")["action"]).to eq("/admin/resources")
|
|
48
52
|
expect(document.at_css('input[name="search_options[keyword]"]')["value"]).to eq("tokyo")
|
|
49
53
|
expect(document.at_css('select[name="search_options[status]"] option[selected]')["value"]).to eq("closed")
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yummy-guide-generic-administrate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- akatsuki-kk
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: administrate
|
|
@@ -95,10 +95,12 @@ files:
|
|
|
95
95
|
- app/assets/javascripts/yummy_guide_administrate/filter_controls.js
|
|
96
96
|
- app/assets/javascripts/yummy_guide_administrate/filter_form.js
|
|
97
97
|
- app/assets/javascripts/yummy_guide_administrate/fixed_submit_actions.js
|
|
98
|
+
- app/assets/javascripts/yummy_guide_administrate/resizable_navigation.js
|
|
98
99
|
- app/assets/javascripts/yummy_guide_administrate/sticky_left_columns.js
|
|
99
100
|
- app/assets/javascripts/yummy_guide_administrate/sticky_table_headers.js
|
|
100
101
|
- app/assets/stylesheets/yummy_guide_administrate/_datetime_input.scss
|
|
101
102
|
- app/assets/stylesheets/yummy_guide_administrate/_fixed_submit_actions.scss
|
|
103
|
+
- app/assets/stylesheets/yummy_guide_administrate/_resizable_navigation.scss
|
|
102
104
|
- app/assets/stylesheets/yummy_guide_administrate/components.scss
|
|
103
105
|
- app/controllers/concerns/yummy_guide/administrate/datetime_filter_parameters.rb
|
|
104
106
|
- app/controllers/concerns/yummy_guide/administrate/default_sorting.rb
|