feat(ui): render the Display panel's Draw controls from the store

This commit is contained in:
Sameh Khamis committed 2026-10-30 23:45:03 -07:00
1 parent bca7495cbf
commit b870df4199
9 files changed
+92 -42

No files matched your search

+13 -16
View File
@@ -67,17 +67,17 @@ const PREVIEW = {
/**
* Wire the Draw control. Returns a handle with a `destroy()` the application
* lifetime owns, plus the console/test seam (`window.__gevDrawTool`).
* @param {{viewer: Cesium.Viewer, annotations: {annotate: Function, clear: Function}}} deps
* The tool keeps its listeners on the controls and hands what they show
* (`{ active, shape, hint }`) to `publish`, which the view renders.
* @param {{viewer: Cesium.Viewer, annotations: {annotate: Function, clear: Function}, publish?: Function}} deps
* @returns {{destroy: Function}|null}
*/
export function initDrawTool({ viewer, annotations }) {
export function initDrawTool({ viewer, annotations, publish = () => {} }) {
const toggle = document.getElementById('draw-toggle');
const modeRow = document.getElementById('draw-mode-row');
const labelRow = document.getElementById('draw-label-row');
const labelInput = document.getElementById('draw-label-input');
const colorSelect = document.getElementById('draw-color-select');
const clearButton = document.getElementById('draw-clear');
const hint = document.getElementById('draw-hint');
if (!viewer || !annotations || !toggle) return null;
let active = false;
@@ -85,6 +85,7 @@ export function initDrawTool({ viewer, annotations }) {
let session = null;
let shape = 'area';
let color = 'primary';
let hint = '';
let handler = null;
let lease = null;
let savedDoubleClick = null;
@@ -124,8 +125,10 @@ export function initDrawTool({ viewer, annotations }) {
() => null,
);
const present = () => publish({ active, shape, hint });
const setHint = (text) => {
if (hint) hint.textContent = text;
hint = text;
present();
};
// ---- preview ---------------------------------------------------------
@@ -354,10 +357,7 @@ export function initDrawTool({ viewer, annotations }) {
}
}
active = next;
toggle.classList.toggle('active', active);
toggle.setAttribute('aria-pressed', String(active));
modeRow?.classList.toggle('visible', active);
labelRow?.classList.toggle('visible', active);
present();
document.body.classList.toggle('gev-drawing', active);
if (active) {
session = createDrawSession(shape);
@@ -433,13 +433,13 @@ export function initDrawTool({ viewer, annotations }) {
const shapeButtons = [
...(modeRow?.querySelectorAll('.pp-mode-btn[data-shape]') || []),
];
const shapeControl = createSegmentedControl({
createSegmentedControl({
buttons: shapeButtons,
valueOf: (button) => button.dataset.shape,
listen,
onChange: (value) => {
shape = normalizeShape(value);
shapeControl.update(shape);
present();
if (active) {
generation += 1;
session = createDrawSession(shape);
@@ -546,11 +546,8 @@ export function initDrawTool({ viewer, annotations }) {
}
});
document.body.classList.remove('gev-drawing');
toggle.classList.remove('active');
toggle.setAttribute('aria-pressed', 'false');
modeRow?.classList.remove('visible');
labelRow?.classList.remove('visible');
if (hint) hint.textContent = '';
hint = '';
present();
if (window.__gevDrawTool === api) delete window.__gevDrawTool;
return attaching;
},
+4 -2
View File
@@ -23,7 +23,7 @@ test('the application composition wires the draw tool and owns its teardown', ()
const tools = read('src/app/tools.js');
const engineAt = tools.indexOf('const annotations = initAnnotations({');
const drawAt = tools.indexOf(
'const drawTool = initDrawTool({ viewer, annotations });',
'const drawTool = initDrawTool({\n viewer,\n annotations,\n publish: (view) => displayStore.publishDraw(view),',
);
assert.ok(engineAt >= 0, 'initAnnotations call missing');
assert.ok(
@@ -88,7 +88,9 @@ test('the Draw control markup carries the ids the tool binds to', () => {
`shape button ${shape} missing`,
);
}
for (const id of ids) {
// The rows and the hint render what the tool publishes; it binds the rest.
const bound = ids.filter((id) => id !== 'draw-label-row' && id !== 'draw-hint');
for (const id of bound) {
assert.match(
tool(),
new RegExp(`getElementById\\('${id}'\\)`),
+8 -2
View File
@@ -218,9 +218,15 @@ function harness() {
const previousWindow = globalThis.window;
globalThis.document = documentDouble;
globalThis.window = globalThis.window || {};
const tool = initDrawTool({ viewer, annotations });
const shown = { view: null };
const tool = initDrawTool({
viewer,
annotations,
publish: (view) => (shown.view = view),
});
return {
tool,
shown,
viewer,
annotations,
byId,
@@ -289,7 +295,7 @@ test('Draw refuses to start when something else holds the pointer', async () =>
assert.equal(pointerOwner(), 'directions', 'and must not steal the claim');
// Named by its tool, not by the internal owner id.
assert.equal(
h.element('draw-hint').textContent,
h.shown.view.hint,
'Directions is using the pointer — close it first.',
);
assert.equal(h.tool.diagnostics().sceneHandler, false, 'no handler was bound');