iobroker.mywebui 1.42.19 → 1.42.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/io-package.json
CHANGED
package/package.json
CHANGED
|
@@ -1712,6 +1712,46 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
1712
1712
|
// ── Interaction binding ──
|
|
1713
1713
|
const nodeKey = (node?.userData?.assetData === asset || !node?.name) ? '__root__' : (node?.name || nodeName);
|
|
1714
1714
|
const inter = asset?.interactions?.[nodeKey];
|
|
1715
|
+
|
|
1716
|
+
// ── Events ──
|
|
1717
|
+
const nodeEvents = asset?.events?.[nodeKey] ?? {};
|
|
1718
|
+
const nodeEventEntries = Object.entries(nodeEvents);
|
|
1719
|
+
const presetEvents = [
|
|
1720
|
+
{ key: 'click', label: '🖱️ click' },
|
|
1721
|
+
{ key: 'mouseenter', label: '↗️ mouseenter' },
|
|
1722
|
+
{ key: 'mouseleave', label: '↙️ mouseleave' },
|
|
1723
|
+
{ key: 'animationEnd', label: '🎬 animationEnd (any clip)' },
|
|
1724
|
+
...( (asset?.availableClips ?? []).map(c => ({ key: `animationEnd:${c}`, label: `🎬 animationEnd:${c}` })) ),
|
|
1725
|
+
...( (asset?.drives ?? []).flatMap((_, i) => [
|
|
1726
|
+
{ key: `driveAtMax:${i}`, label: `⬆️ driveAtMax:${i}` },
|
|
1727
|
+
{ key: `driveAtMin:${i}`, label: `⬇️ driveAtMin:${i}` },
|
|
1728
|
+
]) ),
|
|
1729
|
+
].filter(p => !nodeEvents[p.key]);
|
|
1730
|
+
const presetOptsHtml = presetEvents.map(p => `<option value="${p.key}">${p.label}</option>`).join('');
|
|
1731
|
+
const eventsHtml = nodeEventEntries.length > 0
|
|
1732
|
+
? nodeEventEntries.map(([evtKey, script]) => `
|
|
1733
|
+
<div class="evt-row" style="background:#1a1a2e;border:1px solid #2a2a4a;border-radius:3px;padding:5px;margin-bottom:4px;">
|
|
1734
|
+
<div style="display:flex;align-items:center;gap:4px;margin-bottom:3px;">
|
|
1735
|
+
<span style="color:#dcdcaa;font-size:10px;flex:1;font-family:monospace;">${evtKey}</span>
|
|
1736
|
+
<button class="evt-del-btn tb-btn" data-evtkey="${evtKey}" style="padding:0px 5px;font-size:9px;background:#5a1a1a;color:#f44747;border-color:#8a2a2a;">✕</button>
|
|
1737
|
+
</div>
|
|
1738
|
+
<textarea class="evt-script" data-evtkey="${evtKey}" rows="3"
|
|
1739
|
+
style="width:100%;box-sizing:border-box;background:#0d0d1a;border:1px solid #3c3c5c;color:#d4d4d4;font-family:'Consolas',monospace;font-size:10px;padding:4px;resize:vertical;line-height:1.4;"
|
|
1740
|
+
spellcheck="false"
|
|
1741
|
+
placeholder="// context: node, assetData, THREE, scene, camera // setState(signal, val), getState(signal), subscribe(signal, cb) // assets Map, mixers Map, driveEngine"
|
|
1742
|
+
>${this._escHtml(script)}</textarea>
|
|
1743
|
+
</div>`).join('')
|
|
1744
|
+
: `<div style="color:#555;font-size:10px;font-style:italic;padding:4px 0;">No events configured</div>`;
|
|
1745
|
+
const eventsAddHtml = presetEvents.length > 0
|
|
1746
|
+
? `<div style="display:flex;gap:4px;margin-top:4px;">
|
|
1747
|
+
<select class="evt-preset-sel" style="flex:1;background:#3c3c3c;border:1px solid #555;color:#ccc;padding:2px 4px;border-radius:3px;font-size:10px;">
|
|
1748
|
+
<option value="">— select event type —</option>
|
|
1749
|
+
${presetOptsHtml}
|
|
1750
|
+
<option value="__custom__">Custom event name...</option>
|
|
1751
|
+
</select>
|
|
1752
|
+
<button class="evt-add-btn tb-btn" style="padding:1px 10px;font-size:10px;">+ Add</button>
|
|
1753
|
+
</div>`
|
|
1754
|
+
: `<div style="color:#555;font-size:9px;margin-top:4px;">All event types configured</div>`;
|
|
1715
1755
|
const interHtml = inter
|
|
1716
1756
|
? `<div style="background:#1a2a1a;border:1px solid #2a4a2a;border-radius:3px;padding:6px;font-size:10px;">
|
|
1717
1757
|
<div>Signal: <span style="color:#9cdcfe;">${inter.signal}</span></div>
|
|
@@ -1771,6 +1811,19 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
1771
1811
|
<div class="pg-header">🖱️ Interaction (onClick) <button class="inter-add-btn tb-btn" style="padding:1px 8px;font-size:9px;">+ Set</button></div>
|
|
1772
1812
|
${interHtml}
|
|
1773
1813
|
</div>
|
|
1814
|
+
<div class="pg-group">
|
|
1815
|
+
<div class="pg-header">⚡ Events
|
|
1816
|
+
<span style="color:#555;font-size:9px;font-weight:normal;">node: ${nodeKey}</span>
|
|
1817
|
+
</div>
|
|
1818
|
+
${eventsHtml}
|
|
1819
|
+
${eventsAddHtml}
|
|
1820
|
+
<div style="color:#555;font-size:9px;margin-top:6px;line-height:1.5;">
|
|
1821
|
+
Script context:<br>
|
|
1822
|
+
<code style="color:#888;">node, assetData, THREE, scene</code><br>
|
|
1823
|
+
<code style="color:#888;">setState(sig,val) · getState(sig)</code><br>
|
|
1824
|
+
<code style="color:#888;">assets(Map) · mixers · driveEngine</code>
|
|
1825
|
+
</div>
|
|
1826
|
+
</div>
|
|
1774
1827
|
</div>
|
|
1775
1828
|
`;
|
|
1776
1829
|
|
|
@@ -1856,92 +1909,8 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
1856
1909
|
}
|
|
1857
1910
|
});
|
|
1858
1911
|
|
|
1859
|
-
//
|
|
1860
|
-
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
_escHtml(str) {
|
|
1864
|
-
return (str || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
_show3DEventsInDock(node, asset, nodeKey, onChange) {
|
|
1868
|
-
const dock = this._getDomElement('eventsDock');
|
|
1869
|
-
if (!dock) return;
|
|
1870
|
-
|
|
1871
|
-
// Get or create the 3D events overlay container
|
|
1872
|
-
let d3 = dock.querySelector('#_3dEventsOverlay');
|
|
1873
|
-
if (!d3) {
|
|
1874
|
-
d3 = document.createElement('div');
|
|
1875
|
-
d3.id = '_3dEventsOverlay';
|
|
1876
|
-
d3.style.cssText = 'width:100%;height:100%;overflow:auto;box-sizing:border-box;background:#1e1e1e;color:#ccc;font-family:"Segoe UI",sans-serif;font-size:12px;';
|
|
1877
|
-
dock.insertBefore(d3, dock.firstChild);
|
|
1878
|
-
}
|
|
1879
|
-
d3.style.display = 'block';
|
|
1880
|
-
|
|
1881
|
-
// Hide native event assignment
|
|
1882
|
-
const nativeEvt = this._getDomElement('eventsList');
|
|
1883
|
-
if (nativeEvt) nativeEvt.style.display = 'none';
|
|
1884
|
-
|
|
1885
|
-
// Build events HTML
|
|
1886
|
-
const nodeEvents = asset?.events?.[nodeKey] ?? {};
|
|
1887
|
-
const nodeEventEntries = Object.entries(nodeEvents);
|
|
1888
|
-
const presetEvents = [
|
|
1889
|
-
{ key: 'click', label: '🖱️ click' },
|
|
1890
|
-
{ key: 'mouseenter', label: '↗️ mouseenter' },
|
|
1891
|
-
{ key: 'mouseleave', label: '↙️ mouseleave' },
|
|
1892
|
-
{ key: 'animationEnd', label: '🎬 animationEnd' },
|
|
1893
|
-
...((asset?.availableClips ?? []).map(c => ({ key: `animationEnd:${c}`, label: `🎬 animationEnd:${c}` }))),
|
|
1894
|
-
...((asset?.drives ?? []).flatMap((_, i) => [
|
|
1895
|
-
{ key: `driveAtMax:${i}`, label: `⬆️ driveAtMax:${i}` },
|
|
1896
|
-
{ key: `driveAtMin:${i}`, label: `⬇️ driveAtMin:${i}` }
|
|
1897
|
-
])),
|
|
1898
|
-
].filter(p => !nodeEvents[p.key]);
|
|
1899
|
-
|
|
1900
|
-
const evtRowsHtml = nodeEventEntries.length > 0
|
|
1901
|
-
? nodeEventEntries.map(([evtKey, script]) => `
|
|
1902
|
-
<div style="background:#1a1a2e;border:1px solid #2a2a4a;border-radius:3px;padding:5px;margin-bottom:6px;">
|
|
1903
|
-
<div style="display:flex;align-items:center;gap:4px;margin-bottom:3px;">
|
|
1904
|
-
<span style="color:#dcdcaa;font-size:11px;flex:1;font-family:monospace;">${evtKey}</span>
|
|
1905
|
-
<button class="evt-del-btn" data-evtkey="${evtKey}" style="padding:1px 6px;font-size:10px;background:#5a1a1a;color:#f44747;border:1px solid #8a2a2a;border-radius:3px;cursor:pointer;">✕</button>
|
|
1906
|
-
</div>
|
|
1907
|
-
<textarea class="evt-script" data-evtkey="${evtKey}" rows="4"
|
|
1908
|
-
style="width:100%;box-sizing:border-box;background:#0d0d1a;border:1px solid #3c3c5c;color:#d4d4d4;font-family:'Consolas',monospace;font-size:11px;padding:4px;resize:vertical;line-height:1.4;border-radius:2px;"
|
|
1909
|
-
spellcheck="false"
|
|
1910
|
-
placeholder="// context: node, assetData, THREE, scene // setState(sig,val) getState(sig) // assets(Map) mixers driveEngine"
|
|
1911
|
-
>${this._escHtml(script)}</textarea>
|
|
1912
|
-
</div>`).join('')
|
|
1913
|
-
: `<div style="color:#555;font-size:11px;font-style:italic;padding:8px 0;">No events configured</div>`;
|
|
1914
|
-
|
|
1915
|
-
const presetOptsHtml = presetEvents.map(p => `<option value="${p.key}">${p.label}</option>`).join('');
|
|
1916
|
-
const addRowHtml = presetEvents.length > 0
|
|
1917
|
-
? `<div style="display:flex;gap:4px;margin-top:6px;">
|
|
1918
|
-
<select class="evt-preset-sel" style="flex:1;background:#3c3c3c;border:1px solid #555;color:#ccc;padding:3px 4px;border-radius:3px;font-size:11px;">
|
|
1919
|
-
<option value="">— select event type —</option>
|
|
1920
|
-
${presetOptsHtml}
|
|
1921
|
-
<option value="__custom__">Custom event name...</option>
|
|
1922
|
-
</select>
|
|
1923
|
-
<button class="evt-add-btn" style="padding:2px 12px;background:#3c3c3c;color:#ccc;border:1px solid #555;border-radius:3px;cursor:pointer;font-size:11px;">+ Add</button>
|
|
1924
|
-
</div>`
|
|
1925
|
-
: `<div style="color:#555;font-size:10px;margin-top:6px;">All standard events configured</div>`;
|
|
1926
|
-
|
|
1927
|
-
d3.innerHTML = `
|
|
1928
|
-
<div style="padding:6px 8px;border-bottom:1px solid #3c3c3c;background:#252526;display:flex;align-items:center;gap:6px;">
|
|
1929
|
-
<span style="color:#dcdcaa;font-size:11px;font-weight:bold;">⚡ 3D Events</span>
|
|
1930
|
-
<span style="color:#555;font-size:10px;">node: ${nodeKey}</span>
|
|
1931
|
-
</div>
|
|
1932
|
-
<div style="padding:8px;">
|
|
1933
|
-
${evtRowsHtml}
|
|
1934
|
-
${addRowHtml}
|
|
1935
|
-
<div style="color:#555;font-size:10px;margin-top:8px;line-height:1.6;">
|
|
1936
|
-
Context: <code style="color:#888;">node, assetData, THREE, scene</code><br>
|
|
1937
|
-
<code style="color:#888;">setState(sig,val) · getState(sig)</code><br>
|
|
1938
|
-
<code style="color:#888;">assets(Map) · mixers · driveEngine</code>
|
|
1939
|
-
</div>
|
|
1940
|
-
</div>
|
|
1941
|
-
`;
|
|
1942
|
-
|
|
1943
|
-
// Wire textarea auto-save
|
|
1944
|
-
d3.querySelectorAll('.evt-script').forEach(ta => {
|
|
1912
|
+
// ── Events wiring ──
|
|
1913
|
+
p.querySelectorAll('.evt-script').forEach(ta => {
|
|
1945
1914
|
ta.addEventListener('blur', () => {
|
|
1946
1915
|
const evtKey = ta.dataset.evtkey;
|
|
1947
1916
|
if (!asset.events) asset.events = {};
|
|
@@ -1950,23 +1919,19 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
1950
1919
|
if (onChange) onChange(asset);
|
|
1951
1920
|
});
|
|
1952
1921
|
});
|
|
1953
|
-
|
|
1954
|
-
// Wire delete buttons
|
|
1955
|
-
d3.querySelectorAll('.evt-del-btn').forEach(btn => {
|
|
1922
|
+
p.querySelectorAll('.evt-del-btn').forEach(btn => {
|
|
1956
1923
|
btn.addEventListener('click', () => {
|
|
1957
1924
|
const evtKey = btn.dataset.evtkey;
|
|
1958
1925
|
if (asset?.events?.[nodeKey]) {
|
|
1959
1926
|
delete asset.events[nodeKey][evtKey];
|
|
1960
1927
|
if (Object.keys(asset.events[nodeKey]).length === 0) delete asset.events[nodeKey];
|
|
1961
1928
|
if (onChange) onChange(asset);
|
|
1962
|
-
this.
|
|
1929
|
+
this.show3DNodeProperties(node, asset, mixerInfo, onChange);
|
|
1963
1930
|
}
|
|
1964
1931
|
});
|
|
1965
1932
|
});
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
d3.querySelector('.evt-add-btn')?.addEventListener('click', () => {
|
|
1969
|
-
const sel = d3.querySelector('.evt-preset-sel');
|
|
1933
|
+
p.querySelector('.evt-add-btn')?.addEventListener('click', () => {
|
|
1934
|
+
const sel = p.querySelector('.evt-preset-sel');
|
|
1970
1935
|
let evtKey = sel?.value;
|
|
1971
1936
|
if (!evtKey) return;
|
|
1972
1937
|
if (evtKey === '__custom__') {
|
|
@@ -1977,22 +1942,15 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
1977
1942
|
if (!asset.events[nodeKey]) asset.events[nodeKey] = {};
|
|
1978
1943
|
if (!asset.events[nodeKey][evtKey]) asset.events[nodeKey][evtKey] = '// event: ' + evtKey + '\n';
|
|
1979
1944
|
if (onChange) onChange(asset);
|
|
1980
|
-
this.
|
|
1945
|
+
this.show3DNodeProperties(node, asset, mixerInfo, onChange);
|
|
1981
1946
|
});
|
|
1982
|
-
|
|
1983
|
-
// Activate the events dock panel
|
|
1984
|
-
this.activateDockById('eventsDock');
|
|
1985
1947
|
}
|
|
1986
1948
|
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
if (!dock) return;
|
|
1990
|
-
const d3 = dock.querySelector('#_3dEventsOverlay');
|
|
1991
|
-
if (d3) d3.style.display = 'none';
|
|
1992
|
-
const nativeEvt = this._getDomElement('eventsList');
|
|
1993
|
-
if (nativeEvt) nativeEvt.style.display = '';
|
|
1949
|
+
_escHtml(str) {
|
|
1950
|
+
return (str || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
1994
1951
|
}
|
|
1995
1952
|
|
|
1953
|
+
|
|
1996
1954
|
_showAddDriveDialog(obj, onChange) {
|
|
1997
1955
|
// Build a modal dialog for drive configuration
|
|
1998
1956
|
const overlay = document.createElement('div');
|