|
|
@@ -83,14 +83,25 @@
|
|
|
pop.addEventListener('pointerleave', scheduleClose);
|
|
|
}
|
|
|
|
|
|
+ // Read bounds via getAttribute so an absent `max` reports as NaN
|
|
|
+ // instead of being coerced through Number("") === 0. Without this,
|
|
|
+ // task-assignment cells (which set min="0" but have no max) would
|
|
|
+ // be clamped to [0, 0] and refuse to increment in any table but
|
|
|
+ // the Arbeitstage grid.
|
|
|
function readBounds() {
|
|
|
- const step = Number(boundInput.step);
|
|
|
- const min = Number(boundInput.min);
|
|
|
- const max = Number(boundInput.max);
|
|
|
+ function parseAttr(name) {
|
|
|
+ const raw = boundInput.getAttribute(name);
|
|
|
+ if (raw === null || raw === '') { return NaN; }
|
|
|
+ const n = Number(raw);
|
|
|
+ return Number.isFinite(n) ? n : NaN;
|
|
|
+ }
|
|
|
+ const step = parseAttr('step');
|
|
|
+ const min = parseAttr('min');
|
|
|
+ const max = parseAttr('max');
|
|
|
return {
|
|
|
step: Number.isFinite(step) && step > 0 ? step : 1,
|
|
|
- min: Number.isFinite(min) ? min : NaN,
|
|
|
- max: Number.isFinite(max) ? max : NaN,
|
|
|
+ min: min,
|
|
|
+ max: max,
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -131,8 +142,8 @@
|
|
|
} else {
|
|
|
top = rect.bottom + GAP;
|
|
|
}
|
|
|
- // Horizontal: align to the input's left edge, clamped to viewport.
|
|
|
- let left = rect.left;
|
|
|
+ // Horizontal: centre over the input's midpoint, clamped to viewport.
|
|
|
+ let left = rect.left + (rect.width - pw) / 2;
|
|
|
const MARGIN = 4;
|
|
|
if (left + pw > vw - MARGIN) { left = vw - pw - MARGIN; }
|
|
|
if (left < MARGIN) { left = MARGIN; }
|