diff --git a/src/ui.js b/src/ui.js index 0744ebd..9d010bf 100644 --- a/src/ui.js +++ b/src/ui.js @@ -8,6 +8,12 @@ const STYLE = ` font: 13px/1.4 Tahoma, Verdana, sans-serif; padding: 12px 14px; } +.tat-root.tat-anchored { + position: static; + margin: 0 auto 12px; + max-width: 720px; + box-shadow: none; +} .tat-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 8px; margin-bottom: 10px; @@ -117,17 +123,37 @@ export class Dialog { setMode(mode, anchorInfo) { this.mode = mode; if (!this.root) return; + // Clear all position styles this.root.style.transform = ''; this.root.style.top = ''; this.root.style.bottom = ''; this.root.style.left = ''; this.root.style.right = ''; + this.root.classList.remove('tat-anchored'); + if (mode === 'free') { + // Floating mode: ensure dialog is in body and position at bottom-left. + if (this.root.parentNode !== document.body) { + document.body.appendChild(this.root); + } this.root.style.bottom = '20px'; this.root.style.left = '20px'; } else if (anchorInfo && anchorInfo.canAnchor) { - this._positionAnchored(anchorInfo.rect); + if (anchorInfo.insertBefore) { + // Docked mode: insert the dialog into the page flow before the + // given element, and add the tat-anchored class to switch to + // static positioning. + anchorInfo.insertBefore.parentNode.insertBefore(this.root, anchorInfo.insertBefore); + this.root.classList.add('tat-anchored'); + } else if (anchorInfo.rect) { + // Fallback: position fixed above the rect (old behavior, used when + // no insertion point is available but a rect was given). + this._positionAnchored(anchorInfo.rect); + } + // If neither insertBefore nor rect, leave the dialog where it is + // (the caller will show an anchorError note). } else { + // Top-center fallback (used when mode is anchored but no anchor info). this.root.style.top = '20px'; this.root.style.left = '50%'; this.root.style.transform = 'translateX(-50%)';