Skip to content

Commit 59a6f35

Browse files
Fix docker tooltip first-open trigger mode
1 parent 4ebc748 commit 59a6f35

7 files changed

Lines changed: 175 additions & 7 deletions

File tree

archive/folderview.plus-2026.03.29.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+
ebd1269aed18cc6b23511c59d02979224515cbcb37ec7d69a0b82d10acbb73ba folderview.plus-2026.03.30.16.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.30.15">
10-
<!ENTITY md5 "ecf8e0393142976b043da0f21b0b885d">
9+
<!ENTITY version "2026.03.30.16">
10+
<!ENTITY md5 "f6601bdadd11448fdc6a1e62c302d0a2">
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.30.16
17+
- Fix: Stop Docker advanced preview context popups configured for click from eager-opening on the first hover interaction.
18+
- Regression guard: Cover lazy tooltip initialization so click-trigger folders only eager-open on click or touch, while hover-trigger folders still open on hover.
19+
20+
1621
###2026.03.30.15
1722
- Maintenance: Fold docker update state into hydration signature.
1823
- Maintenance: Trim docker update signature helper.

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,7 +1692,7 @@ const buildDockerTooltipContent = (ct) => {
16921692
return $content;
16931693
};
16941694

1695-
const initializeDockerTooltipOnDemand = ($target, init) => {
1695+
const initializeDockerTooltipOnDemand = ($target, init, options = {}) => {
16961696
if (DOCKER_PREVIEW_POPUP_ENABLED !== true) {
16971697
return;
16981698
}
@@ -1703,13 +1703,18 @@ const initializeDockerTooltipOnDemand = ($target, init) => {
17031703
return;
17041704
}
17051705
$target.data('fvTooltipsterDeferred', true);
1706+
const eagerOpenEventTypes = new Set(
1707+
(Array.isArray(options?.openOnEventTypes) ? options.openOnEventTypes : ['mouseenter', 'click', 'touchstart'])
1708+
.map((eventType) => String(eventType || '').trim().toLowerCase())
1709+
.filter(Boolean)
1710+
);
17061711
const ensureInitialized = (eventType = '') => {
17071712
if ($target.data('fvTooltipsterInitialized') === true) {
17081713
return;
17091714
}
17101715
$target.data('fvTooltipsterInitialized', true);
17111716
init();
1712-
if (eventType === 'mouseenter' || eventType === 'click' || eventType === 'touchstart') {
1717+
if (eagerOpenEventTypes.has(eventType)) {
17131718
setTimeout(() => {
17141719
try {
17151720
$target.tooltipster('open');
@@ -3952,7 +3957,11 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
39523957
}
39533958
},
39543959
content: $('<div class="fv-tooltip-lazy-loading">Loading preview...</div>')
3955-
}));
3960+
}), {
3961+
openOnEventTypes: triggerMode === 'hover'
3962+
? ['mouseenter', 'click', 'touchstart']
3963+
: ['click', 'touchstart']
3964+
});
39563965
} else if (FOLDER_VIEW_DEBUG_MODE && tooltip_trigger_element && tooltip_trigger_element.length > 0) {
39573966
console.log(`[FV3_DEBUG] createFolder (id: ${id}), container ${ct.shortId}: FolderView preview popup runtime is disabled; skipping tooltip initialization.`);
39583967
} else {
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import test from 'node:test';
2+
import assert from 'node:assert/strict';
3+
import fs from 'node:fs';
4+
import path from 'node:path';
5+
6+
const repoRoot = path.resolve(process.cwd());
7+
const dockerJs = fs.readFileSync(
8+
path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js'),
9+
'utf8'
10+
);
11+
12+
const extractArrowFunctionBody = (source, signature) => {
13+
const startIndex = source.indexOf(signature);
14+
assert.ok(startIndex >= 0, `Missing function signature: ${signature}`);
15+
const braceStart = source.indexOf('{', startIndex + signature.length);
16+
assert.ok(braceStart >= 0, `Missing opening brace for: ${signature}`);
17+
let depth = 0;
18+
for (let index = braceStart; index < source.length; index += 1) {
19+
const char = source[index];
20+
if (char === '{') {
21+
depth += 1;
22+
} else if (char === '}') {
23+
depth -= 1;
24+
if (depth === 0) {
25+
return source.slice(braceStart + 1, index);
26+
}
27+
}
28+
}
29+
throw new Error(`Failed to extract function body for: ${signature}`);
30+
};
31+
32+
const initializeDockerTooltipOnDemandBody = extractArrowFunctionBody(
33+
dockerJs,
34+
'const initializeDockerTooltipOnDemand = ($target, init, options = {}) => '
35+
);
36+
37+
const initializeDockerTooltipOnDemand = new Function(
38+
'$target',
39+
'init',
40+
'options',
41+
'DOCKER_PREVIEW_POPUP_ENABLED',
42+
'setTimeout',
43+
initializeDockerTooltipOnDemandBody
44+
);
45+
46+
const createLazyTooltipTarget = () => {
47+
const dataStore = new Map();
48+
const handlers = new Map();
49+
const tooltipsterCalls = [];
50+
const target = {
51+
length: 1,
52+
data(key, value) {
53+
if (arguments.length === 1) {
54+
return dataStore.get(key);
55+
}
56+
dataStore.set(key, value);
57+
return this;
58+
},
59+
one(events, handler) {
60+
String(events || '')
61+
.trim()
62+
.split(/\s+/)
63+
.filter(Boolean)
64+
.forEach((eventName) => {
65+
handlers.set(eventName.split('.')[0], handler);
66+
});
67+
return this;
68+
},
69+
tooltipster(action) {
70+
tooltipsterCalls.push(action);
71+
return this;
72+
}
73+
};
74+
75+
return {
76+
target,
77+
emit(type) {
78+
const handler = handlers.get(type);
79+
handlers.clear();
80+
if (handler) {
81+
handler({ type });
82+
}
83+
},
84+
getTooltipsterCalls: () => [...tooltipsterCalls],
85+
getData: (key) => dataStore.get(key)
86+
};
87+
};
88+
89+
const runImmediateTimeout = (callback) => {
90+
callback();
91+
return 0;
92+
};
93+
94+
test('docker lazy tooltip init does not eager-open click-trigger tooltips on first hover', () => {
95+
const lazyTarget = createLazyTooltipTarget();
96+
let initCount = 0;
97+
98+
initializeDockerTooltipOnDemand(
99+
lazyTarget.target,
100+
() => {
101+
initCount += 1;
102+
},
103+
{ openOnEventTypes: ['click', 'touchstart'] },
104+
true,
105+
runImmediateTimeout
106+
);
107+
108+
lazyTarget.emit('mouseenter');
109+
110+
assert.equal(initCount, 1);
111+
assert.equal(lazyTarget.getData('fvTooltipsterInitialized'), true);
112+
assert.deepEqual(lazyTarget.getTooltipsterCalls(), []);
113+
});
114+
115+
test('docker lazy tooltip init still eager-opens hover-trigger tooltips on first hover', () => {
116+
const lazyTarget = createLazyTooltipTarget();
117+
let initCount = 0;
118+
119+
initializeDockerTooltipOnDemand(
120+
lazyTarget.target,
121+
() => {
122+
initCount += 1;
123+
},
124+
{ openOnEventTypes: ['mouseenter', 'click', 'touchstart'] },
125+
true,
126+
runImmediateTimeout
127+
);
128+
129+
lazyTarget.emit('mouseenter');
130+
131+
assert.equal(initCount, 1);
132+
assert.deepEqual(lazyTarget.getTooltipsterCalls(), ['open']);
133+
});
134+
135+
test('docker lazy tooltip init eager-opens click-trigger tooltips on first click', () => {
136+
const lazyTarget = createLazyTooltipTarget();
137+
let initCount = 0;
138+
139+
initializeDockerTooltipOnDemand(
140+
lazyTarget.target,
141+
() => {
142+
initCount += 1;
143+
},
144+
{ openOnEventTypes: ['click', 'touchstart'] },
145+
true,
146+
runImmediateTimeout
147+
);
148+
149+
lazyTarget.emit('click');
150+
151+
assert.equal(initCount, 1);
152+
assert.deepEqual(lazyTarget.getTooltipsterCalls(), ['open']);
153+
});

tests/performance-optimizations.test.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ test('docker preview popup runtime stays enabled behind lazy advanced-preview in
228228
assert.match(dockerJs, /const DOCKER_PREVIEW_POPUP_ENABLED = true;/);
229229
assert.match(dockerJs, /fvTooltipLazyBuilt/);
230230
assert.match(dockerJs, /Loading preview\.\.\./);
231-
assert.match(dockerJs, /const initializeDockerTooltipOnDemand = \(\$target,\s*init\) =>/);
231+
assert.match(dockerJs, /const initializeDockerTooltipOnDemand = \(\$target,\s*init,\s*options = \{\}\) =>/);
232+
assert.match(dockerJs, /openOnEventTypes: triggerMode === 'hover'/);
232233
assert.match(dockerJs, /if \(DOCKER_PREVIEW_POPUP_ENABLED !== true\) \{\s*return;\s*\}/);
233234
assert.match(dockerJs, /if\(DOCKER_PREVIEW_POPUP_ENABLED && tooltip_trigger_element && tooltip_trigger_element\.length > 0\) \{/);
234235
});

0 commit comments

Comments
 (0)