Skip to content

Commit 9d1cd6b

Browse files
Harden docker folder vertical centering for fixed-column clones
1 parent 34962ac commit 9d1cd6b

4 files changed

Lines changed: 108 additions & 28 deletions

File tree

208 KB
Binary file not shown.

folderview.plus.plg

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
<!ENTITY launch "Settings/FolderViewPlus">
77
<!ENTITY plugdir "/usr/local/emhttp/plugins/&name;">
88
<!ENTITY pluginURL "https://raw.githubusercontent.com/&github;/main/folderview.plus.plg">
9-
<!ENTITY version "2026.03.07.8">
10-
<!ENTITY md5 "afea9c4354cc2b161a80161aecc791dd">
9+
<!ENTITY version "2026.03.07.9">
10+
<!ENTITY md5 "58513ecfa1d0ea947e4f9b76afef07af">
1111
]>
1212

1313
<PLUGIN name="&name;" author="&author;" version="&version;" launch="&launch;" pluginURL="&pluginURL;" icon="folder-open-o" support="https://github.com/alexphillips-dev/FolderView-Plus/issues" min="7.0.0">
1414
<CHANGES>
1515

16+
###2026.03.07.9
17+
- Harden Docker folder vertical centering for fixed-column render paths:
18+
- center all folder name cells (`td.ct-name.folder-name`) regardless of table clone/source,
19+
- add MutationObserver + deferred re-apply to catch late DOM/table clone rebuilds.
20+
1621
###2026.03.07.8
1722
- Fix Docker folder row vertical alignment in fixed/clone table layouts:
1823
- widen centering selectors to all folder rows instead of only `#docker_list`,

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

Lines changed: 96 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,40 +64,110 @@ const escapeClassToken = (value) => {
6464
return input.replace(/[^a-zA-Z0-9_-]/g, '\\$&');
6565
};
6666

67-
const forceFolderRowVerticalCenter = (id) => {
68-
const escapedId = escapeClassToken(id);
69-
const rows = Array.from(document.querySelectorAll(`tr.folder.folder-id-${escapedId}`));
70-
if (!rows.length) {
71-
return;
67+
const applyFolderCellCentering = (cell) => {
68+
if (!cell) {
69+
return false;
70+
}
71+
72+
let sub = null;
73+
Array.from(cell.children || []).forEach((child) => {
74+
if (!sub && child && child.classList && child.classList.contains('folder-name-sub')) {
75+
sub = child;
76+
}
77+
});
78+
if (!sub) {
79+
sub = cell.querySelector('.folder-name-sub');
80+
}
81+
if (!sub) {
82+
return false;
7283
}
7384

74-
rows.forEach((row) => {
85+
const row = cell.parentElement;
86+
if (row && row.tagName === 'TR') {
7587
Array.from(row.children).forEach((td) => {
7688
if (td && td.tagName === 'TD') {
7789
td.style.setProperty('vertical-align', 'middle', 'important');
7890
}
7991
});
92+
}
8093

81-
const cell = row.querySelector('td.ct-name.folder-name');
82-
if (!cell) {
83-
return;
84-
}
85-
cell.style.setProperty('position', 'relative', 'important');
86-
cell.style.setProperty('padding-top', '0px', 'important');
87-
cell.style.setProperty('padding-bottom', '0px', 'important');
94+
cell.style.setProperty('vertical-align', 'middle', 'important');
95+
cell.style.setProperty('position', 'relative', 'important');
96+
cell.style.setProperty('padding-top', '0px', 'important');
97+
cell.style.setProperty('padding-bottom', '0px', 'important');
8898

89-
const sub = cell.querySelector('.folder-name-sub');
90-
if (!sub) {
91-
return;
99+
sub.style.setProperty('position', 'absolute', 'important');
100+
sub.style.setProperty('top', '50%', 'important');
101+
sub.style.setProperty('left', '8px', 'important');
102+
sub.style.setProperty('right', '8px', 'important');
103+
sub.style.setProperty('transform', 'translateY(-50%)', 'important');
104+
sub.style.setProperty('display', 'flex', 'important');
105+
sub.style.setProperty('align-items', 'center', 'important');
106+
107+
return true;
108+
};
109+
110+
const forceAllFolderRowsVerticalCenter = () => {
111+
document.querySelectorAll('td.ct-name.folder-name').forEach((cell) => {
112+
applyFolderCellCentering(cell);
113+
});
114+
};
115+
116+
const forceFolderRowVerticalCenter = (id) => {
117+
const escapedId = escapeClassToken(id);
118+
let centered = false;
119+
document.querySelectorAll(`tr.folder-id-${escapedId} td.ct-name.folder-name`).forEach((cell) => {
120+
centered = applyFolderCellCentering(cell) || centered;
121+
});
122+
if (!centered) {
123+
forceAllFolderRowsVerticalCenter();
124+
}
125+
};
126+
127+
let folderRowCenterObserver = null;
128+
let folderRowCenterRaf = 0;
129+
130+
const queueForceAllFolderRowsVerticalCenter = () => {
131+
if (folderRowCenterRaf) {
132+
return;
133+
}
134+
folderRowCenterRaf = window.requestAnimationFrame(() => {
135+
folderRowCenterRaf = 0;
136+
forceAllFolderRowsVerticalCenter();
137+
});
138+
};
139+
140+
const startFolderRowCenterObserver = () => {
141+
if (folderRowCenterObserver || !document.body) {
142+
return;
143+
}
144+
145+
folderRowCenterObserver = new MutationObserver((mutations) => {
146+
for (const mutation of mutations) {
147+
if (!mutation || !mutation.addedNodes || mutation.addedNodes.length === 0) {
148+
continue;
149+
}
150+
for (const node of mutation.addedNodes) {
151+
if (!node || node.nodeType !== 1) {
152+
continue;
153+
}
154+
const element = node;
155+
if (
156+
(element.matches && element.matches('td.ct-name.folder-name, tr[class*="folder-id-"]'))
157+
|| (element.querySelector && element.querySelector('td.ct-name.folder-name, tr[class*="folder-id-"]'))
158+
) {
159+
queueForceAllFolderRowsVerticalCenter();
160+
return;
161+
}
162+
}
92163
}
93-
sub.style.setProperty('position', 'absolute', 'important');
94-
sub.style.setProperty('top', '50%', 'important');
95-
sub.style.setProperty('left', '8px', 'important');
96-
sub.style.setProperty('right', '8px', 'important');
97-
sub.style.setProperty('transform', 'translateY(-50%)', 'important');
98-
sub.style.setProperty('display', 'flex', 'important');
99-
sub.style.setProperty('align-items', 'center', 'important');
100164
});
165+
166+
folderRowCenterObserver.observe(document.body, { childList: true, subtree: true });
167+
queueForceAllFolderRowsVerticalCenter();
168+
setTimeout(queueForceAllFolderRowsVerticalCenter, 50);
169+
setTimeout(queueForceAllFolderRowsVerticalCenter, 250);
170+
setTimeout(queueForceAllFolderRowsVerticalCenter, 1000);
101171
};
102172

103173
/**
@@ -268,9 +338,12 @@ const createFolders = async () => {
268338
globalFolders = foldersDone;
269339
if (FOLDER_VIEW_DEBUG_MODE) console.log('[FV3_DEBUG] createFolders: Assigned foldersDone to globalFolders:', {...globalFolders});
270340

341+
startFolderRowCenterObserver();
271342
Object.keys(globalFolders).forEach((folderId) => forceFolderRowVerticalCenter(folderId));
343+
queueForceAllFolderRowsVerticalCenter();
272344
setTimeout(() => {
273345
Object.keys(globalFolders).forEach((folderId) => forceFolderRowVerticalCenter(folderId));
346+
queueForceAllFolderRowsVerticalCenter();
274347
}, 50);
275348

276349
folderDebugMode = false; // Existing flag

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,21 @@
2828
}
2929

3030
/* Keep folder rows vertically centered against the preview row content */
31-
tr.folder > td {
31+
tr.folder > td,
32+
tr[class*="folder-id-"] > td {
3233
vertical-align: middle !important;
3334
}
3435

3536
/* Unraid table styles can force top visual placement.
3637
Center the left folder block explicitly within the row height. */
37-
tr.folder > td.ct-name.folder-name {
38+
td.ct-name.folder-name {
3839
position: relative;
3940
padding-top: 0 !important;
4041
padding-bottom: 0 !important;
42+
vertical-align: middle !important;
4143
}
4244

43-
tr.folder > td.ct-name.folder-name > .folder-name-sub {
45+
td.ct-name.folder-name > .folder-name-sub {
4446
position: absolute;
4547
top: 50%;
4648
left: 8px;

0 commit comments

Comments
 (0)