Skip to content

Commit ef95a82

Browse files
Add legacy dashboard layout mode
1 parent 2327b53 commit ef95a82

12 files changed

Lines changed: 66 additions & 17 deletions

archive/folderview.plus-2026.03.22.33.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+
8b6f1c62218f74972557e4af36cc6e25e30296af8f23f3226e74adafdbfe4520 folderview.plus-2026.03.23.05.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.04">
10-
<!ENTITY md5 "0ee22b997d6cb07349d6530412a340ac">
9+
<!ENTITY version "2026.03.23.05">
10+
<!ENTITY md5 "127b706e598c5d4fe5ae754e0be01ff0">
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.05
17+
- Feature: Added `Legacy` as a Dashboard widget layout for Docker and VM widgets so they can show native Unraid rows without FolderView Plus folder grouping.
18+
- Quality: Persisted the new `Legacy` layout across settings, runtime normalization, and manifest-backed preferences, with a safe widget reload when switching in or out of bypass mode.
19+
- Test: Added regression coverage for `Legacy` dashboard layout normalization and runtime wiring.
20+
21+
1622
###2026.03.23.04
1723
- UX: Refined settings and on-screen update messaging for clarity and consistency.
1824
- Quality: Strengthened release automation and regression guards to prevent note drift.

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/FolderViewPlus.page

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ if (!empty($fvplusRuntimeConflicts)) {
174174
<span>Layout mode</span>
175175
<select id="docker-dashboard-layout" onchange="changeDashboardPref('docker', 'layout', this.value)">
176176
<option value="classic">Classic</option>
177+
<option value="legacy">Legacy</option>
177178
<option value="fullwidth">Fullwidth</option>
178179
<option value="accordion">Accordion</option>
179180
<option value="inset">Inset</option>
@@ -406,6 +407,7 @@ if (!empty($fvplusRuntimeConflicts)) {
406407
<span>Layout mode</span>
407408
<select id="vm-dashboard-layout" onchange="changeDashboardPref('vm', 'layout', this.value)">
408409
<option value="classic">Classic</option>
410+
<option value="legacy">Legacy</option>
409411
<option value="fullwidth">Fullwidth</option>
410412
<option value="accordion">Accordion</option>
411413
<option value="inset">Inset</option>

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

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,10 @@ const dashboardDebugLog = (...args) => {
156156
console.log(...args);
157157
}
158158
};
159-
const DASHBOARD_LAYOUT_MODES = ['classic', 'fullwidth', 'accordion', 'inset', 'compactmatrix'];
159+
const DASHBOARD_LAYOUT_MODES = ['classic', 'legacy', 'fullwidth', 'accordion', 'inset', 'compactmatrix'];
160160
const DASHBOARD_LAYOUT_LABELS = Object.freeze({
161161
classic: 'Classic',
162+
legacy: 'Legacy',
162163
fullwidth: 'Full Width',
163164
accordion: 'Accordion',
164165
inset: 'Inset',
@@ -280,6 +281,7 @@ const writeDashboardCompactDensityStateForType = (type, enabled) => {
280281
}
281282
};
282283
const getDashboardStartedOnlySelectorForType = (type) => (type === 'vm' ? 'input#vms' : 'input#apps');
284+
const isDashboardLegacyLayoutForType = (type) => normalizeDashboardPrefsForType(type).layout === 'legacy';
283285
const isDashboardStartedOnlyEnabledForType = (type) => {
284286
const selector = getDashboardStartedOnlySelectorForType(type);
285287
const $toggle = $(selector).first();
@@ -464,7 +466,7 @@ const syncDashboardWidgetQuickRailVisibilityForType = (type) => {
464466
const shouldShow = !!parentNode
465467
&& isDashboardNodeVisible(parentNode)
466468
&& isDashboardWidgetCollapsedForType(resolvedType) !== true
467-
&& hasVisibleDashboardFolderCardsForType(resolvedType);
469+
&& (hasVisibleDashboardFolderCardsForType(resolvedType) || isDashboardLegacyLayoutForType(resolvedType));
468470
$host.toggleClass('is-hidden', !shouldShow);
469471
if (shouldShow) {
470472
syncDashboardWidgetQuickRailAlignmentForType(resolvedType);
@@ -623,6 +625,7 @@ const handleDashboardWidgetLayoutQuickSwitch = async (type, value) => {
623625
const nextLayout = normalizeDashboardLayoutMode(value);
624626
const previousPrefs = utils.normalizePrefs(folderTypePrefs?.[resolvedType] || {});
625627
const previousDashboard = normalizeDashboardPrefsForType(resolvedType);
628+
const requiresStructureReload = previousDashboard.layout === 'legacy' || nextLayout === 'legacy';
626629
if (previousDashboard.layout === nextLayout) {
627630
syncDashboardWidgetLayoutQuickControlForType(resolvedType);
628631
return;
@@ -650,6 +653,11 @@ const handleDashboardWidgetLayoutQuickSwitch = async (type, value) => {
650653
throw new Error(response?.error || 'Failed to save dashboard preferences.');
651654
}
652655
folderTypePrefs[resolvedType] = utils.normalizePrefs(response.prefs || nextPrefs);
656+
if (requiresStructureReload && typeof window.loadlist === 'function') {
657+
loadedFolder = false;
658+
window.loadlist();
659+
return;
660+
}
653661
scheduleDashboardLayoutApplyForType(resolvedType);
654662
syncDashboardWidgetLayoutQuickControlForType(resolvedType);
655663
} catch (_error) {
@@ -905,13 +913,13 @@ const applyDashboardLayoutStateForType = (type) => {
905913
}
906914
const dashboardPrefs = normalizeDashboardPrefsForType(meta.type);
907915
const layout = normalizeDashboardLayoutMode(dashboardPrefs.layout);
908-
const nonClassicLayout = layout !== 'classic';
916+
const folderCardLayout = !['classic', 'legacy'].includes(layout);
909917
$tbody.attr('data-fv-dashboard-layout', layout);
910-
$tbody.removeClass('fv-dashboard-layout-classic fv-dashboard-layout-fullwidth fv-dashboard-layout-accordion fv-dashboard-layout-inset fv-dashboard-layout-compactmatrix');
918+
$tbody.removeClass('fv-dashboard-layout-classic fv-dashboard-layout-legacy fv-dashboard-layout-fullwidth fv-dashboard-layout-accordion fv-dashboard-layout-inset fv-dashboard-layout-compactmatrix');
911919
$tbody.addClass(`fv-dashboard-layout-${layout}`);
912-
$tbody.toggleClass('fv-dashboard-show-expand-toggle', nonClassicLayout && dashboardPrefs.expandToggle === true);
913-
$tbody.toggleClass('fv-dashboard-greyscale-enabled', nonClassicLayout && dashboardPrefs.greyscale === true);
914-
$tbody.toggleClass('fv-dashboard-hide-folder-label', nonClassicLayout && dashboardPrefs.folderLabel === false);
920+
$tbody.toggleClass('fv-dashboard-show-expand-toggle', folderCardLayout && dashboardPrefs.expandToggle === true);
921+
$tbody.toggleClass('fv-dashboard-greyscale-enabled', folderCardLayout && dashboardPrefs.greyscale === true);
922+
$tbody.toggleClass('fv-dashboard-hide-folder-label', folderCardLayout && dashboardPrefs.folderLabel === false);
915923
$tbody.toggleClass('fv-dashboard-health-emphasis-enabled', readDashboardHealthEmphasisStateForType(meta.type));
916924
$tbody.toggleClass('fv-dashboard-density-compact', readDashboardCompactDensityStateForType(meta.type));
917925
ensureDashboardWidgetLayoutQuickSwitchForType(meta.type);
@@ -1390,6 +1398,12 @@ const createFolders = async () => {
13901398
unraidOrder = reorderFolderSlotsInBaseOrder(unraidOrder, folders, folderTypePrefs.docker);
13911399
applyDashboardRuntimePrefs();
13921400
lastDashboardStateSignatures.docker = buildDockerStateSignature(containersInfo, false);
1401+
if (isDashboardLegacyLayoutForType('docker')) {
1402+
globalFolders.docker = {};
1403+
scheduleDashboardLayoutApplyForType('docker');
1404+
syncDashboardWidgetLayoutQuickControlForType('docker');
1405+
return;
1406+
}
13931407

13941408
// Filter the order to get the container that aren't in the order, this happen when a new container is created
13951409
let newOnes = order.filter(x => !unraidOrder.includes(x));
@@ -1590,6 +1604,12 @@ const createFolders = async () => {
15901604
unraidOrder = reorderFolderSlotsInBaseOrder(unraidOrder, folders, folderTypePrefs.vm);
15911605
applyDashboardRuntimePrefs();
15921606
lastDashboardStateSignatures.vm = buildVmStateSignature(vmInfo, false);
1607+
if (isDashboardLegacyLayoutForType('vm')) {
1608+
globalFolders.vms = {};
1609+
scheduleDashboardLayoutApplyForType('vm');
1610+
syncDashboardWidgetLayoutQuickControlForType('vm');
1611+
return;
1612+
}
15931613

15941614
// Filter the webui order to get the container that aren't in the order, this happen when a new container is created
15951615
let newOnes = order.filter(x => !unraidOrder.includes(x));

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7979,7 +7979,7 @@ const normalizeDashboardPrefsForType = (type, prefsOverride = null) => {
79797979
? utils.normalizeDashboardLayout
79807980
: ((value) => {
79817981
const normalized = String(value || '').trim().toLowerCase();
7982-
return ['classic', 'fullwidth', 'accordion', 'inset', 'compactmatrix'].includes(normalized) ? normalized : 'classic';
7982+
return ['classic', 'legacy', 'fullwidth', 'accordion', 'inset', 'compactmatrix'].includes(normalized) ? normalized : 'classic';
79837983
});
79847984
return {
79857985
layout: normalizeLayout(dashboard.layout),
@@ -7991,7 +7991,7 @@ const normalizeDashboardPrefsForType = (type, prefsOverride = null) => {
79917991

79927992
const syncDashboardDependentFields = (type) => {
79937993
const prefs = normalizeDashboardPrefsForType(type);
7994-
const showNonClassicControls = prefs.layout !== 'classic';
7994+
const showNonClassicControls = !['classic', 'legacy'].includes(prefs.layout);
79957995
$(`#${type}-dashboard-expand-toggle-row`).toggleClass('is-hidden', !showNonClassicControls);
79967996
$(`#${type}-dashboard-greyscale-row`).toggleClass('is-hidden', !showNonClassicControls);
79977997
$(`#${type}-dashboard-folder-label-row`).toggleClass('is-hidden', !showNonClassicControls);
@@ -9731,7 +9731,7 @@ const changeDashboardPref = async (type, key, value) => {
97319731
? utils.normalizeDashboardLayout
97329732
: ((layoutValue) => {
97339733
const normalized = String(layoutValue || '').trim().toLowerCase();
9734-
return ['classic', 'fullwidth', 'accordion', 'inset', 'compactmatrix'].includes(normalized) ? normalized : 'classic';
9734+
return ['classic', 'legacy', 'fullwidth', 'accordion', 'inset', 'compactmatrix'].includes(normalized) ? normalized : 'classic';
97359735
});
97369736
nextDashboard.layout = normalizeLayout(value);
97379737
} else if (key === 'expandToggle') {

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@
387387

388388
const normalizeDashboardLayout = (value) => {
389389
const normalized = String(value || '').trim().toLowerCase();
390-
return ['classic', 'fullwidth', 'accordion', 'inset', 'compactmatrix'].includes(normalized)
390+
return ['classic', 'legacy', 'fullwidth', 'accordion', 'inset', 'compactmatrix'].includes(normalized)
391391
? normalized
392392
: DEFAULT_DASHBOARD_PREFS.layout;
393393
};

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1728,7 +1728,7 @@ function normalizeAppColumnWidth($value): string {
17281728

17291729
function normalizeDashboardLayout($value): string {
17301730
$normalized = strtolower(trim((string)$value));
1731-
if (in_array($normalized, ['classic', 'fullwidth', 'accordion', 'inset', 'compactmatrix'], true)) {
1731+
if (in_array($normalized, ['classic', 'legacy', 'fullwidth', 'accordion', 'inset', 'compactmatrix'], true)) {
17321732
return $normalized;
17331733
}
17341734
return 'classic';

src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/dashboard.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
--fvplus-dashboard-accent-soft-active: rgba(240, 163, 10, 0.2);
1212
--fvplus-dashboard-accent-border-active: rgba(240, 163, 10, 0.6);
1313
--fvplus-dashboard-layout-fullwidth: #6fb1ff;
14+
--fvplus-dashboard-layout-legacy: #b7c0cf;
1415
--fvplus-dashboard-layout-accordion: #ffd36c;
1516
--fvplus-dashboard-layout-inset: #8ddf9f;
1617
--fvplus-dashboard-layout-compactmatrix: #7ce4ff;
@@ -171,6 +172,10 @@
171172
color: var(--fvplus-dashboard-layout-fullwidth);
172173
}
173174

175+
.fv-dashboard-layout-quick[data-fv-layout="legacy"] {
176+
color: var(--fvplus-dashboard-layout-legacy);
177+
}
178+
174179
.fv-dashboard-layout-quick[data-fv-layout="accordion"] {
175180
color: var(--fvplus-dashboard-layout-accordion);
176181
}

0 commit comments

Comments
 (0)