Skip to content

Commit d9564dc

Browse files
Fix multi-row Docker preview default menu trigger
1 parent 6f0f84a commit d9564dc

6 files changed

Lines changed: 122 additions & 9 deletions

File tree

archive/folderview.plus-2026.04.04.02.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+
db93101e7def3116900049aef67465606a46adcdf54957edc5d12890b617300f folderview.plus-2026.04.04.27.txz

folderview.plus.plg

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
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.04.04.26">
10-
<!ENTITY md5 "d85902f6ab1c39a8cfbd1dbca3046eba">
9+
<!ENTITY version "2026.04.04.27">
10+
<!ENTITY md5 "8721331d4b662126ea60a3f662212c94">
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.04.04.27
17+
- Fix: Default-context multi-row Docker previews now clone and compact the native container row trigger, so clicking visible preview cards opens the normal Docker menu again when preview rows are set above 1 or Unlimited.
18+
19+
1620
###2026.04.04.26
1721
- Fix: Docker runtime rows, folder state, and container interactions.
1822

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

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,98 @@ const bindCompactPreviewDefaultContext = ($item, $sourceRow) => {
10061006
$appLink.attr('href', '#');
10071007
}
10081008
};
1009+
const buildCompactPreviewDefaultContextItem = ($sourceRow, settings = {}, autostart = false) => {
1010+
if (!$sourceRow || !$sourceRow.length) {
1011+
return null;
1012+
}
1013+
const previewMode = Number(settings?.preview || 0);
1014+
const autostartClass = autostart ? ' autostart' : '';
1015+
const $sourceOuter = $sourceRow.find('td.ct-name > span.outer').first();
1016+
if (!$sourceOuter.length) {
1017+
return null;
1018+
}
1019+
const $item = $sourceOuter.clone();
1020+
const compactMode = previewMode >= 1 && previewMode <= 4 ? previewMode : 1;
1021+
$item.addClass(`fv-docker-preview-card fv-docker-preview-card-compact fv-docker-preview-mode-${compactMode}${autostartClass}`);
1022+
$item.removeAttr('id');
1023+
$item.find('br').remove();
1024+
$item.find('i[id^="load-"]').each((_, node) => {
1025+
const $node = $(node);
1026+
const currentId = String($node.attr('id') || '').trim();
1027+
if (currentId) {
1028+
$node.attr('id', `folder-${currentId}`);
1029+
}
1030+
});
1031+
const $hand = $item.children('span.hand').first();
1032+
const $inner = $item.children('span.inner').first();
1033+
if (!$inner.length) {
1034+
return $item;
1035+
}
1036+
const $appName = $inner.children('span.appname').first();
1037+
const $meta = $('<span class="fv-preview-meta-compact"></span>');
1038+
const $status = $('<span class="fv-preview-status-compact"></span>');
1039+
const $trailingNodes = $appName.length ? $appName.nextAll().detach() : $inner.contents().detach();
1040+
$trailingNodes.each((_, node) => {
1041+
const $node = $(node);
1042+
if ($node.is('.folder-element-custom-btn, .fv-preview-webui-placeholder')) {
1043+
return;
1044+
}
1045+
$status.append($node);
1046+
});
1047+
if ($status.children().length) {
1048+
$meta.append($status);
1049+
}
1050+
if (compactMode !== 2) {
1051+
$meta.append('<span class="fv-preview-actions-compact"></span>');
1052+
}
1053+
if (compactMode === 2) {
1054+
$inner.remove();
1055+
} else {
1056+
if (compactMode === 3 || compactMode === 4) {
1057+
$hand.remove();
1058+
}
1059+
$inner.append($meta);
1060+
}
1061+
return $item;
1062+
};
1063+
const bindCompactPreviewDefaultContextProxy = ($item) => {
1064+
if (!$item || !$item.length) {
1065+
return;
1066+
}
1067+
const $menuTrigger = $item.find('span.hand, span.appname > a.exec').filter(function() {
1068+
return String($(this).attr('onclick') || '').trim().length > 0
1069+
|| String($(this).attr('oncontextmenu') || '').trim().length > 0
1070+
|| $(this).hasClass('hand')
1071+
|| $(this).hasClass('exec');
1072+
}).first();
1073+
if (!$menuTrigger.length) {
1074+
return;
1075+
}
1076+
const usingAppNameTrigger = $menuTrigger.is('span.appname > a.exec');
1077+
const interactiveSelector = usingAppNameTrigger
1078+
? 'span.appname, span.appname > a.exec, span.folder-element-custom-btn, span.folder-element-custom-btn > a, .fv-preview-actions-compact, .fv-preview-actions-compact *'
1079+
: '.hand, span.folder-element-custom-btn, span.folder-element-custom-btn > a, .fv-preview-actions-compact, .fv-preview-actions-compact *';
1080+
$item
1081+
.off('.fvCompactDefaultContextProxy')
1082+
.on('click.fvCompactDefaultContextProxy', function(event) {
1083+
const $target = $(event.target);
1084+
if ($target.closest(interactiveSelector).length) {
1085+
return;
1086+
}
1087+
event.preventDefault();
1088+
event.stopPropagation();
1089+
$menuTrigger.trigger('click');
1090+
})
1091+
.on('contextmenu.fvCompactDefaultContextProxy', function(event) {
1092+
const $target = $(event.target);
1093+
if ($target.closest(interactiveSelector).length) {
1094+
return;
1095+
}
1096+
event.preventDefault();
1097+
event.stopPropagation();
1098+
$menuTrigger.trigger('contextmenu');
1099+
});
1100+
};
10091101
const decorateDockerFolderMemberRow = ($row, folderId, containerName) => {
10101102
if (!$row || !$row.length) {
10111103
return;
@@ -3236,14 +3328,25 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
32363328
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] createFolder (id: ${id}): Selecting addPreview function based on folder.settings.preview = ${folder.settings.preview}. Context setting: ${folder.settings.context}`);
32373329
const compactMultiRowPreview = isCompactMultiRowPreview(folder.settings);
32383330
const appendCompactPreview = (folderTrId, ctid, autostart, previewEntry, $sourceRow = null) => {
3239-
const { $item, $tooltipTrigger } = buildDockerPreviewItem({
3240-
entry: previewEntry || {},
3241-
settings: folder.settings,
3242-
autostart
3243-
});
3331+
let compactPreviewItem = null;
3332+
if (folder.settings.context === 1) {
3333+
compactPreviewItem = buildCompactPreviewDefaultContextItem($sourceRow, folder.settings, autostart);
3334+
}
3335+
const builtPreview = compactPreviewItem
3336+
? { $item: compactPreviewItem, $tooltipTrigger: null }
3337+
: buildDockerPreviewItem({
3338+
entry: previewEntry || {},
3339+
settings: folder.settings,
3340+
autostart
3341+
});
3342+
const { $item, $tooltipTrigger } = builtPreview;
32443343
$(`tr.folder-id-${folderTrId} div.folder-preview`).append($item);
32453344
if (folder.settings.context === 1) {
3246-
bindCompactPreviewDefaultContext($item, $sourceRow);
3345+
if (compactPreviewItem) {
3346+
bindCompactPreviewDefaultContextProxy($item);
3347+
} else {
3348+
bindCompactPreviewDefaultContext($item, $sourceRow);
3349+
}
32473350
return null;
32483351
}
32493352
if (folder.settings.context === 2 || folder.settings.context === 0) {

tests/docker-preview-overflow-feature.test.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ test('docker runtime applies preview row layout limits and keeps compact preview
5050
assert.match(dockerJs, /fv-preview-webui-placeholder/);
5151
assert.match(dockerJs, /const buildDockerPreviewItem = \(\{ entry = \{\}, settings = \{\}, autostart = false \}\) =>/);
5252
assert.match(dockerJs, /const bindCompactPreviewDefaultContext = \(\$item, \$sourceRow\) =>/);
53+
assert.match(dockerJs, /const buildCompactPreviewDefaultContextItem = \(\$sourceRow, settings = \{\}, autostart = false\) =>/);
54+
assert.match(dockerJs, /const \$sourceOuter = \$sourceRow\.find\('td\.ct-name > span\.outer'\)\.first\(\)/);
55+
assert.match(dockerJs, /const bindCompactPreviewDefaultContextProxy = \(\$item\) =>/);
56+
assert.match(dockerJs, /\$menuTrigger\.trigger\('click'\);/);
5357
assert.match(dockerJs, /\$sourceRow\.find\('td\.ct-name > span\.outer > span\.hand'\)\.first\(\)/);
5458
assert.match(dockerJs, /const inlineClick = String\(\$nativeTrigger\.attr\('onclick'\) \|\| ''\)\.trim\(\);/);
5559
assert.match(dockerJs, /const targets = \[/);
@@ -62,6 +66,8 @@ test('docker runtime applies preview row layout limits and keeps compact preview
6266
assert.match(dockerJs, /\$tooltipTrigger:\s*triggerSelector === '\.fv-docker-preview-card'\s*\?\s*\$compactItem/);
6367
assert.match(dockerJs, /const appendCompactPreview = \(folderTrId, ctid, autostart, previewEntry, \$sourceRow = null\) =>/);
6468
assert.match(dockerJs, /if \(folder\.settings\.context === 1\) \{/);
69+
assert.match(dockerJs, /compactPreviewItem = buildCompactPreviewDefaultContextItem\(\$sourceRow, folder\.settings, autostart\);/);
70+
assert.match(dockerJs, /bindCompactPreviewDefaultContextProxy\(\$item\);/);
6571
assert.match(dockerJs, /bindCompactPreviewDefaultContext\(\$item, \$sourceRow\);/);
6672
assert.match(dockerJs, /\$target\.data\('fvTooltipEnsureInitialized', ensureInitialized\);/);
6773
assert.match(dockerJs, /const layoutFolderPreviewRows = \(\$preview, settings = \{\}\) =>/);

0 commit comments

Comments
 (0)