163 lines
4.8 KiB
JavaScript
163 lines
4.8 KiB
JavaScript
/**
|
|
* StripOverlayLayer — Single overlay container for all positioned overlays.
|
|
*
|
|
* Owns: DOM element with position: absolute; inset: 0; pointer-events: none; overflow: visible
|
|
* Children restore pointer-events: auto
|
|
* Used by: ActionPopover (Story 1.5), ConfirmationBar (Story 3.2)
|
|
*
|
|
* Import rule: may import from src/core/, src/contracts/, src/utils/ ONLY.
|
|
* Constructors are side-effect free — call init() from module.js Hooks.once('ready').
|
|
*
|
|
* Story 1.5: Original creation for ActionPopover support
|
|
* Story 3.2: Extended to support ConfirmationBar
|
|
*
|
|
* @module ui/shared/StripOverlayLayer
|
|
*/
|
|
|
|
/**
|
|
* Single overlay container for all positioned overlays.
|
|
* Provides a common parent element with pointer-events: none that allows
|
|
* children to restore pointer-events: auto for specific interactive areas.
|
|
*/
|
|
export class StripOverlayLayer {
|
|
/**
|
|
* @param {import('../../foundry/FoundryAdapter.js').FoundryAdapter} adapter
|
|
* Injected FoundryAdapter surface.
|
|
*/
|
|
constructor(adapter) {
|
|
this._adapter = adapter;
|
|
/** @type {HTMLElement|null} The overlay container element */
|
|
this._element = null;
|
|
/** @type {Map<string, HTMLElement>} Track rendered overlays by key */
|
|
this._overlays = new Map();
|
|
}
|
|
|
|
/**
|
|
* Initializes the StripOverlayLayer by creating the DOM element.
|
|
* Side-effect: Creates and appends the overlay container to the ScryingPoolStrip.
|
|
*/
|
|
init() {
|
|
// Create overlay container element
|
|
this._element = document.createElement('div');
|
|
this._element.className = 'sp-strip__overlay-layer';
|
|
this._element.setAttribute('aria-hidden', 'true');
|
|
|
|
// Critical styles per UX-DR6
|
|
this._element.style.cssText = `
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
overflow: visible;
|
|
`;
|
|
|
|
// Try to find the ScryingPoolStrip element to append to
|
|
// The strip is created in Story 1.5 as a floating ApplicationV2 window
|
|
const stripElement = document.querySelector?.('.scrying-pool__roster-strip');
|
|
if (stripElement) {
|
|
stripElement.appendChild(this._element);
|
|
} else {
|
|
// Fallback: if strip not found, append to body (shouldn't happen in normal flow)
|
|
console.warn('[ScryingPool] StripOverlayLayer: ScryingPoolStrip not found, appending to body');
|
|
document.body.appendChild(this._element);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the overlay container element.
|
|
* @returns {HTMLElement|null} The overlay element.
|
|
*/
|
|
get element() {
|
|
return this._element;
|
|
}
|
|
|
|
/**
|
|
* Renders content into the overlay layer.
|
|
* The content will have pointer-events: auto to allow interaction.
|
|
*
|
|
* @param {string|HTMLElement} content - HTML string or DOM element to render.
|
|
* @param {string} [key] - Optional key to track this overlay for replacement.
|
|
* @returns {HTMLElement|null} The rendered element, or null if failed.
|
|
*/
|
|
render(content, key = null) {
|
|
if (!this._element) {
|
|
console.warn('[ScryingPool] StripOverlayLayer: Cannot render, element not initialized');
|
|
return null;
|
|
}
|
|
|
|
// Remove previous overlay if key is provided
|
|
if (key && this._overlays.has(key)) {
|
|
const previous = this._overlays.get(key);
|
|
if (previous && previous.parentNode) {
|
|
previous.parentNode.removeChild(previous);
|
|
}
|
|
this._overlays.delete(key);
|
|
}
|
|
|
|
// Create container for the content
|
|
const container = document.createElement('div');
|
|
container.style.pointerEvents = 'auto';
|
|
|
|
// Set content
|
|
if (typeof content === 'string') {
|
|
container.innerHTML = content;
|
|
} else if (content instanceof HTMLElement) {
|
|
container.appendChild(content);
|
|
} else {
|
|
console.warn('[ScryingPool] StripOverlayLayer: Invalid content type');
|
|
return null;
|
|
}
|
|
|
|
// Append to overlay layer
|
|
this._element.appendChild(container);
|
|
|
|
// Track by key if provided
|
|
if (key) {
|
|
this._overlays.set(key, container);
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
/**
|
|
* Removes an overlay by key.
|
|
*
|
|
* @param {string} key - The key of the overlay to remove.
|
|
*/
|
|
remove(key) {
|
|
if (!this._overlays.has(key)) {
|
|
return;
|
|
}
|
|
|
|
const overlay = this._overlays.get(key);
|
|
if (overlay && overlay.parentNode) {
|
|
overlay.parentNode.removeChild(overlay);
|
|
}
|
|
this._overlays.delete(key);
|
|
}
|
|
|
|
/**
|
|
* Removes all overlays from the layer.
|
|
*/
|
|
clearAll() {
|
|
for (const [, overlay] of this._overlays) {
|
|
if (overlay && overlay.parentNode) {
|
|
overlay.parentNode.removeChild(overlay);
|
|
}
|
|
}
|
|
this._overlays.clear();
|
|
}
|
|
|
|
/**
|
|
* Cleans up the StripOverlayLayer by removing the DOM element.
|
|
* Safe to call multiple times.
|
|
*/
|
|
teardown() {
|
|
this.clearAll();
|
|
|
|
if (this._element && this._element.parentNode) {
|
|
this._element.parentNode.removeChild(this._element);
|
|
}
|
|
this._element = null;
|
|
}
|
|
}
|