feat: move sensor details to sheet, implement placement on floor plan
This commit is contained in:
+34
-73
@@ -1,7 +1,4 @@
|
||||
/* globals: Konva, preact, preactHooks, htm */
|
||||
const { h, render } = preact;
|
||||
const { useState } = preactHooks;
|
||||
const html = htm.bind(h);
|
||||
/* globals: Konva */
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants & mutable state
|
||||
@@ -20,9 +17,6 @@ let _dragging = false; // true while any room group is being dragged
|
||||
let _dimRoom = null;
|
||||
let _dimGroup = null;
|
||||
|
||||
// Exposed so the Preact overlay can update its tooltip imperatively.
|
||||
let _setTooltip = (_v) => {};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stage & layers
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -365,27 +359,23 @@ function renderSensors() {
|
||||
listening: false,
|
||||
}));
|
||||
|
||||
group.on('click tap', () => _onSensorTap(sensor, group, label));
|
||||
group.on('dragend', () => _onSensorDragEnd(sensor, group, room));
|
||||
let pressTimer = null;
|
||||
group.on('mousedown touchstart', () => {
|
||||
pressTimer = setTimeout(() => {
|
||||
notifyFlutter({ type: 'sensorTapped', id: String(sensor.id) });
|
||||
pressTimer = null;
|
||||
}, 500);
|
||||
});
|
||||
group.on('mouseup touchend touchcancel dragstart', () => {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = null;
|
||||
});
|
||||
group.on('dragend', () => _onSensorDragEnd(sensor, group, room));
|
||||
sensorsLayer.add(group);
|
||||
});
|
||||
sensorsLayer.batchDraw();
|
||||
}
|
||||
|
||||
function _onSensorTap(sensor, group, label) {
|
||||
if (mode === 'view') {
|
||||
const scale = stage.scaleX();
|
||||
_setTooltip({
|
||||
id: String(sensor.id),
|
||||
name: label,
|
||||
x: group.x() * scale + stage.x(),
|
||||
y: group.y() * scale + stage.y(),
|
||||
});
|
||||
} else {
|
||||
notifyFlutter({ type: 'sensorTapped', id: String(sensor.id) });
|
||||
}
|
||||
}
|
||||
|
||||
function _onSensorDragEnd(sensor, group, originalRoom) {
|
||||
const newAbsX = group.x() / PPM;
|
||||
const newAbsY = group.y() / PPM;
|
||||
@@ -461,57 +451,9 @@ function _fitToRooms() {
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Preact overlay — sensor tooltip
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function Tooltip({ id, name, x, y, onClose }) {
|
||||
return html`
|
||||
<div style=${{
|
||||
position: 'absolute',
|
||||
left: `${x + 14}px`,
|
||||
top: `${y - 48}px`,
|
||||
background: '#ffffff',
|
||||
borderRadius: '8px',
|
||||
padding: '8px 12px',
|
||||
boxShadow: '0 2px 12px rgba(0,0,0,0.18)',
|
||||
pointerEvents: 'auto',
|
||||
minWidth: '130px',
|
||||
fontFamily: 'Roboto, sans-serif',
|
||||
}}>
|
||||
<div style=${{ fontSize: '13px', fontWeight: '500', color: '#212121', marginBottom: '6px' }}>
|
||||
${name}
|
||||
</div>
|
||||
<div style=${{ display: 'flex', gap: '10px' }}>
|
||||
<button
|
||||
style=${{ fontSize: '12px', border: 'none', background: 'none', color: '#1565C0', cursor: 'pointer', padding: 0 }}
|
||||
onClick=${() => { notifyFlutter({ type: 'sensorTapped', id }); onClose(); }}>
|
||||
View details
|
||||
</button>
|
||||
<button
|
||||
style=${{ fontSize: '12px', border: 'none', background: 'none', color: '#9e9e9e', cursor: 'pointer', padding: 0 }}
|
||||
onClick=${onClose}>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [tooltip, setTip] = useState(null);
|
||||
_setTooltip = setTip;
|
||||
return tooltip
|
||||
? html`<${Tooltip} ...${tooltip} onClose=${() => setTip(null)} />`
|
||||
: null;
|
||||
}
|
||||
|
||||
render(html`<${App} />`, document.getElementById('overlay'));
|
||||
|
||||
// Dismiss tooltip and dimensions when tapping the stage background.
|
||||
// Dismiss dimensions when tapping the stage background.
|
||||
stage.on('click tap', (e) => {
|
||||
if (e.target === stage) {
|
||||
_setTooltip(null);
|
||||
_clearDimensions();
|
||||
notifyFlutter({ type: 'selectionCleared' });
|
||||
}
|
||||
@@ -572,7 +514,6 @@ window.companion = {
|
||||
tagsLayer.visible(!edit);
|
||||
particlesLayer.visible(!edit);
|
||||
_clearDimensions();
|
||||
_setTooltip(null);
|
||||
stage.batchDraw();
|
||||
},
|
||||
|
||||
@@ -602,4 +543,24 @@ window.companion = {
|
||||
fitToRooms() {
|
||||
_fitToRooms();
|
||||
},
|
||||
|
||||
getPositionAtCenter() {
|
||||
const s = stage.scaleX();
|
||||
const cx = (stage.width() / 2 - stage.x()) / s / PPM;
|
||||
const cy = (stage.height() / 2 - stage.y()) / s / PPM;
|
||||
let result = { type: 'positionAtCenter', roomId: null };
|
||||
for (const room of rooms) {
|
||||
if (cx >= room.x && cx <= room.x + room.width &&
|
||||
cy >= room.y && cy <= room.y + room.height) {
|
||||
result = {
|
||||
type: 'positionAtCenter',
|
||||
roomId: room.id,
|
||||
x: cx - room.x,
|
||||
y: cy - room.y,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
FlutterBridge.postMessage(JSON.stringify(result));
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user