Skip to content

Commit c9895ef

Browse files
Keep dashboard quick rail visible during view transitions
1 parent cd219f5 commit c9895ef

6 files changed

Lines changed: 41 additions & 12 deletions

File tree

archive/folderview.plus-2026.03.22.35.txz.sha256

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
d54e17f1431e5c6effc6109707d783944be4904b2a933cce561ab48379f33955 folderview.plus-2026.03.23.07.txz

folderview.plus.plg

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@
66
<!ENTITY launch "Settings/FolderViewPlus">
77
<!ENTITY plugdir "/usr/local/emhttp/plugins/&name;">
88
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/dev/folderview.plus.plg">
9-
<!ENTITY version "2026.03.23.06">
10-
<!ENTITY md5 "c717f290c3bd116b976961b6ba528534">
9+
<!ENTITY version "2026.03.23.07">
10+
<!ENTITY md5 "a0c6c28a46bd8527409895ecbe64e59d">
1111
]>
1212

1313
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;" pluginURL="&pluginURL;" icon="folder-icon.png" support="https://forums.unraid.net/topic/197631-plugin-folderview-plus/" min="7.0.0">
1414
<CHANGES>
1515

16+
###2026.03.23.07
17+
- Fix: Kept the Dashboard widget action rail visible during `Legacy` view transitions by tracking per-widget layout rebuild state instead of hiding the rail while cards are temporarily absent.
18+
- Quality: Hardened the Dashboard quick-rail visibility logic so it treats local widget rebuilds as an active state rather than a collapsed/empty widget.
19+
- Test: Added regression coverage for transition-in-flight handling in the Dashboard layout runtime.
20+
21+
1622
###2026.03.23.06
1723
- Fix: Reworked Dashboard widget `Legacy` transitions to restore native rows locally instead of forcing a full `loadlist()` refresh, removing the extra delay when cycling in and out of bypass mode.
1824
- Fix: Prevented one widget entering `Legacy` from short-circuiting the other widget's folder render path by scoping dashboard rebuilds to the affected type.

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/dashboard.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ const writeDashboardCompactDensityStateForType = (type, enabled) => {
282282
};
283283
const getDashboardStartedOnlySelectorForType = (type) => (type === 'vm' ? 'input#vms' : 'input#apps');
284284
const isDashboardLegacyLayoutForType = (type) => normalizeDashboardPrefsForType(type).layout === 'legacy';
285+
const isDashboardLayoutTransitionInFlightForType = (type) => {
286+
const resolvedType = type === 'vm' ? 'vm' : 'docker';
287+
return dashboardLayoutTransitionInFlightByType?.[resolvedType] === true;
288+
};
285289
const getDashboardNativeRowSelectorForType = (type) => (
286290
type === 'vm'
287291
? 'span.outer.vms:not(.folder-vm)'
@@ -561,7 +565,11 @@ const syncDashboardWidgetQuickRailVisibilityForType = (type) => {
561565
const shouldShow = !!parentNode
562566
&& isDashboardNodeVisible(parentNode)
563567
&& isDashboardWidgetCollapsedForType(resolvedType) !== true
564-
&& (hasVisibleDashboardFolderCardsForType(resolvedType) || isDashboardLegacyLayoutForType(resolvedType));
568+
&& (
569+
hasVisibleDashboardFolderCardsForType(resolvedType)
570+
|| isDashboardLegacyLayoutForType(resolvedType)
571+
|| isDashboardLayoutTransitionInFlightForType(resolvedType)
572+
);
565573
$host.toggleClass('is-hidden', !shouldShow);
566574
if (shouldShow) {
567575
syncDashboardWidgetQuickRailAlignmentForType(resolvedType);
@@ -717,16 +725,23 @@ const saveDashboardLayoutPrefForType = async (type, prefsPayload) => {
717725
};
718726
const rerenderDashboardWidgetStructureForType = async (type) => {
719727
const resolvedType = type === 'vm' ? 'vm' : 'docker';
720-
await restoreDashboardNativeRowsForType(resolvedType);
721-
if (isDashboardLegacyLayoutForType(resolvedType)) {
722-
scheduleDashboardLayoutApplyForType(resolvedType);
723-
syncDashboardWidgetLayoutQuickControlForType(resolvedType);
728+
dashboardLayoutTransitionInFlightByType[resolvedType] = true;
729+
scheduleDashboardWidgetVisibilitySyncForType(resolvedType, 0);
730+
try {
731+
await restoreDashboardNativeRowsForType(resolvedType);
732+
if (isDashboardLegacyLayoutForType(resolvedType)) {
733+
scheduleDashboardLayoutApplyForType(resolvedType);
734+
syncDashboardWidgetLayoutQuickControlForType(resolvedType);
735+
scheduleDashboardWidgetVisibilitySyncForType(resolvedType, 0);
736+
return;
737+
}
738+
prepareDashboardFolderRequestsForType(resolvedType);
739+
await createFolders([resolvedType]);
740+
scheduleDashboardWidgetVisibilitySyncForType(resolvedType, 0);
741+
} finally {
742+
dashboardLayoutTransitionInFlightByType[resolvedType] = false;
724743
scheduleDashboardWidgetVisibilitySyncForType(resolvedType, 0);
725-
return;
726744
}
727-
prepareDashboardFolderRequestsForType(resolvedType);
728-
await createFolders([resolvedType]);
729-
scheduleDashboardWidgetVisibilitySyncForType(resolvedType, 0);
730745
};
731746
const handleDashboardWidgetLayoutQuickSwitch = async (type, value) => {
732747
const resolvedType = type === 'vm' ? 'vm' : 'docker';
@@ -3315,6 +3330,10 @@ let dashboardWidgetVisibilitySyncTimerByType = {
33153330
docker: 0,
33163331
vm: 0
33173332
};
3333+
let dashboardLayoutTransitionInFlightByType = {
3334+
docker: false,
3335+
vm: false
3336+
};
33183337
let dashboardQuickActionSyncBound = false;
33193338
let dashboardThemeReflowBound = false;
33203339
let dashboardThemeReflowObserver = null;

tests/dashboard-layout-regression.test.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ test('server normalizes compact matrix dashboard layout', () => {
8484
test('dashboard runtime supports layout classes, accordion guards, and overflow metadata', () => {
8585
assert.match(dashboardScript, /const DASHBOARD_LAYOUT_MODES = \['classic', 'legacy', 'fullwidth', 'accordion', 'inset', 'compactmatrix'\]/);
8686
assert.match(dashboardScript, /const isDashboardLegacyLayoutForType = \(type\) =>/);
87+
assert.match(dashboardScript, /const isDashboardLayoutTransitionInFlightForType = \(type\) =>/);
8788
assert.match(dashboardScript, /const restoreDashboardNativeRowsForType = async \(type\) =>/);
8889
assert.match(dashboardScript, /const rerenderDashboardWidgetStructureForType = async \(type\) =>/);
8990
assert.match(dashboardScript, /const prepareDashboardFolderRequestsForType = \(type\) =>/);
@@ -123,9 +124,12 @@ test('dashboard runtime supports layout classes, accordion guards, and overflow
123124
assert.match(dashboardScript, /const applyDashboardLayoutStateForType = \(type\) =>/);
124125
assert.match(dashboardScript, /const scheduleDashboardLayoutApplyForType = \(type\) =>/);
125126
assert.match(dashboardScript, /const requiresStructureReload = previousDashboard\.layout === 'legacy' \|\| nextLayout === 'legacy';/);
127+
assert.match(dashboardScript, /dashboardLayoutTransitionInFlightByType\[resolvedType\] = true;/);
128+
assert.match(dashboardScript, /dashboardLayoutTransitionInFlightByType\[resolvedType\] = false;/);
126129
assert.match(dashboardScript, /await rerenderDashboardWidgetStructureForType\(resolvedType\);/);
127130
assert.match(dashboardScript, /if \(isDashboardLegacyLayoutForType\('docker'\)\) \{/);
128131
assert.match(dashboardScript, /if \(isDashboardLegacyLayoutForType\('vm'\)\) \{/);
132+
assert.match(dashboardScript, /\|\| isDashboardLayoutTransitionInFlightForType\(resolvedType\)/);
129133
assert.match(dashboardScript, /\|\| isDashboardLegacyLayoutForType\(resolvedType\)/);
130134
assert.match(dashboardScript, /\$host\.parent\(\)\.is\(\$container\)/);
131135
assert.match(dashboardScript, /\$container\.prepend\(\$host\)/);

0 commit comments

Comments
 (0)