Skip to content

Commit 0dc5608

Browse files
Render docker context quick actions as icon-only top row
1 parent 37cc702 commit 0dc5608

7 files changed

Lines changed: 129 additions & 3 deletions

File tree

archive/folderview.plus-2026.03.19.16.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+
948bbe275b3556713fadcf31d8f7840d0528de2eb0e80f75884ff2327f834fcc folderview.plus-2026.03.20.12.txz

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;/dev/folderview.plus.plg">
9-
<!ENTITY version "2026.03.20.11">
10-
<!ENTITY md5 "fce4cb8d9ae6540de09ea38d87ecf076">
9+
<!ENTITY version "2026.03.20.12">
10+
<!ENTITY md5 "53ab38d43d84b573e23ca2fa27198c3f">
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.20.12
17+
- UX: Changed Docker folder click menu so `Focus`, `Pin`, and `Lock` render as icon-only quick actions on one top row.
18+
- Regression guard: Added runtime/test coverage for context quick-action strip detection and styling.
19+
20+
1621
###2026.03.20.11
1722
- Fix: Restored Docker folder rows after row-action regression that hid folder names.
1823
- UX: Moved `Focus`, `Pin/Unpin`, and `Lock/Unlock` into the top of the existing folder click menu.

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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4066,6 +4066,84 @@ const folderCustomAction = async (id, actionIndex) => {
40664066
};
40674067

40684068

4069+
const DOCKER_CONTEXT_QUICK_ACTION_LABELS = new Set([
4070+
'focus folder',
4071+
'clear focus',
4072+
'pin folder',
4073+
'unpin folder',
4074+
'lock folder',
4075+
'unlock folder'
4076+
]);
4077+
4078+
const normalizeDockerContextActionLabel = (value) => String(value || '').trim().replace(/\s+/g, ' ').toLowerCase();
4079+
4080+
const findVisibleDockerContextMenu = () => {
4081+
const selectors = [
4082+
'ul.context-menu-list:visible',
4083+
'ul.contextMenuPlugin:visible',
4084+
'ul.context-menu:visible',
4085+
'ul.dropdown-menu:visible'
4086+
];
4087+
for (const selector of selectors) {
4088+
const $menus = $(selector);
4089+
for (let idx = $menus.length - 1; idx >= 0; idx--) {
4090+
const $menu = $($menus.get(idx));
4091+
const quickCount = $menu.children('li').filter((_, item) => {
4092+
const label = normalizeDockerContextActionLabel($(item).text());
4093+
return DOCKER_CONTEXT_QUICK_ACTION_LABELS.has(label);
4094+
}).length;
4095+
if (quickCount >= 3) {
4096+
return $menu;
4097+
}
4098+
}
4099+
}
4100+
return $();
4101+
};
4102+
4103+
const styleDockerFolderContextQuickIcons = () => {
4104+
const $menu = findVisibleDockerContextMenu();
4105+
if (!$menu.length) {
4106+
return false;
4107+
}
4108+
const $quickItems = $menu.children('li').filter((_, item) => {
4109+
const label = normalizeDockerContextActionLabel($(item).text());
4110+
return DOCKER_CONTEXT_QUICK_ACTION_LABELS.has(label);
4111+
}).slice(0, 3);
4112+
if ($quickItems.length < 3) {
4113+
return false;
4114+
}
4115+
$menu.addClass('fvplus-docker-context-menu');
4116+
$quickItems.each((_, item) => {
4117+
const $item = $(item);
4118+
const label = String($item.text() || '').trim().replace(/\s+/g, ' ');
4119+
$item.addClass('fvplus-docker-quick-item');
4120+
const $interactive = $item.find('a, .context-menu-item').first();
4121+
if ($interactive.length) {
4122+
$interactive.addClass('fvplus-docker-quick-link');
4123+
$interactive.attr('title', label);
4124+
$interactive.attr('aria-label', label);
4125+
} else {
4126+
$item.attr('title', label);
4127+
$item.attr('aria-label', label);
4128+
}
4129+
});
4130+
const $firstNonQuick = $menu.children('li').not('.fvplus-docker-quick-item').first();
4131+
if ($firstNonQuick.length) {
4132+
$firstNonQuick.addClass('fvplus-docker-quick-clear');
4133+
}
4134+
return true;
4135+
};
4136+
4137+
const queueDockerFolderContextQuickIcons = (attempt = 0) => {
4138+
if (styleDockerFolderContextQuickIcons()) {
4139+
return;
4140+
}
4141+
if (attempt >= 8) {
4142+
return;
4143+
}
4144+
setTimeout(() => queueDockerFolderContextQuickIcons(attempt + 1), 18 * (attempt + 1));
4145+
};
4146+
40694147
/**
40704148
* Atach the menu when clicking the folder icon
40714149
* @param {string} id the id of the folder
@@ -4412,6 +4490,7 @@ const addDockerFolderContext = (id) => {
44124490
folderEvents.dispatchEvent(new CustomEvent('docker-folder-context', {detail: { id, opts }}));
44134491

44144492
context.attach('#' + id, opts);
4493+
queueDockerFolderContextQuickIcons();
44154494
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] addDockerFolderContext (id: ${id}): Context menu attached to #${id}. Exit.`);
44164495
};
44174496

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,37 @@ div.folder-preview-4 span.outer {
716716
background-color: var(--fvplus-hover-bg);
717717
}
718718

719+
/* Docker folder context quick actions: icon-only strip on first row. */
720+
.fvplus-docker-context-menu > li.fvplus-docker-quick-item {
721+
display: inline-flex !important;
722+
width: 34px;
723+
margin: 0 4px 0 0;
724+
vertical-align: top;
725+
}
726+
727+
.fvplus-docker-context-menu > li.fvplus-docker-quick-item > a,
728+
.fvplus-docker-context-menu > li.fvplus-docker-quick-item .context-menu-item {
729+
width: 30px;
730+
min-width: 30px;
731+
height: 26px;
732+
padding: 2px 0 !important;
733+
display: inline-flex;
734+
align-items: center;
735+
justify-content: center;
736+
font-size: 0 !important;
737+
line-height: 1;
738+
}
739+
740+
.fvplus-docker-context-menu > li.fvplus-docker-quick-item > a i,
741+
.fvplus-docker-context-menu > li.fvplus-docker-quick-item .context-menu-item i,
742+
.fvplus-docker-context-menu > li.fvplus-docker-quick-item::before {
743+
font-size: 14px !important;
744+
}
745+
746+
.fvplus-docker-context-menu > li.fvplus-docker-quick-clear {
747+
clear: both;
748+
}
749+
719750
/* Touch/mobile behavior: avoid hover-only interactions. */
720751
@media (hover: none), (pointer: coarse) {
721752
.hover div.folder-preview div {

tests/docker-folder-row-quick-actions.test.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ const dockerScript = fs.readFileSync(
88
path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js'),
99
'utf8'
1010
);
11+
const dockerCss = fs.readFileSync(
12+
path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/docker.css'),
13+
'utf8'
14+
);
1115

1216
test('docker context menu keeps focus/pin/lock quick actions at the top', () => {
1317
assert.match(dockerScript, /text:\s*focused \? 'Clear focus' : 'Focus folder'/);
@@ -16,6 +20,8 @@ test('docker context menu keeps focus/pin/lock quick actions at the top', () =>
1620
assert.match(dockerScript, /toggleDockerFolderFocus\(id\)/);
1721
assert.match(dockerScript, /toggleDockerFolderPin\(id\)/);
1822
assert.match(dockerScript, /toggleDockerFolderLock\(id\)/);
23+
assert.match(dockerScript, /queueDockerFolderContextQuickIcons\(/);
24+
assert.match(dockerScript, /fvplus-docker-context-menu/);
1925
assert.doesNotMatch(dockerScript, /fv-folder-row-actions/);
2026
});
2127

@@ -25,3 +31,8 @@ test('docker runtime exposes and applies focus\/lock state guards', () => {
2531
assert.match(dockerScript, /ensureDockerFolderUnlocked/);
2632
assert.match(dockerScript, /Folder locked/);
2733
});
34+
35+
test('docker context menu quick-action strip styles remain defined', () => {
36+
assert.match(dockerCss, /\.fvplus-docker-context-menu > li\.fvplus-docker-quick-item/);
37+
assert.match(dockerCss, /\.fvplus-docker-context-menu > li\.fvplus-docker-quick-item > a/);
38+
});

0 commit comments

Comments
 (0)