apexcharts
Version:
A JavaScript Chart Library
274 lines (250 loc) • 6.58 kB
JavaScript
// @ts-check
/**
* Browser API abstraction layer for SSR support
* Routes to either real browser APIs or SSR shims based on environment
*/
import { Environment } from '../utils/Environment.js'
import { SSRDOMShim } from './DOMShim.js'
/** @type {any} */
let shim = null
/** @type {any} */
let xmlSerializerInstance = null
/** @type {any} */
let domParserInstance = null
/**
* Browser API abstraction that works in both browser and Node.js
*/
export class BrowserAPIs {
/**
* Initialize the SSR shim if in SSR environment
* Must be called before using other methods
*/
static init() {
if (Environment.isSSR() && !shim) {
shim = new SSRDOMShim()
}
}
/**
* Create an HTML element
* @param {string} tagName - Element tag name
* @returns {HTMLElement} HTML element
*/
static createElement(tagName) {
if (Environment.isSSR()) {
if (!shim) this.init()
return shim.createElementNS(null, tagName)
}
return document.createElement(tagName)
}
/**
* Create an SVG element with namespace
* @param {string} namespaceURI - Namespace URI
* @param {string} qualifiedName - Element tag name
* @returns {HTMLElement} created element
*/
static createElementNS(namespaceURI, qualifiedName) {
if (Environment.isSSR()) {
if (!shim) this.init()
return shim.createElementNS(namespaceURI, qualifiedName)
}
return /** @type {HTMLElement} */ (
document.createElementNS(namespaceURI, qualifiedName)
)
}
/**
* Create a text node
* @param {string} data - Text content
* @returns {Text|object} Text node
*/
static createTextNode(data) {
if (Environment.isSSR()) {
if (!shim) this.init()
return shim.createTextNode(data)
}
return document.createTextNode(data)
}
/**
* Query selector
* @param {string} selector - CSS selector
* @returns {Element|null}
*/
static querySelector(selector) {
if (Environment.isSSR()) {
return null
}
return document.querySelector(selector)
}
/**
* Query selector all
* @param {string} selector - CSS selector
* @returns {NodeList|any[]}
*/
static querySelectorAll(selector) {
if (Environment.isSSR()) {
return []
}
return document.querySelectorAll(selector)
}
/**
* Get computed style for an element
* @param {Element} element - Element to get styles for
* @returns {CSSStyleDeclaration|object}
*/
static getComputedStyle(element) {
if (Environment.isSSR()) {
return {}
}
return window.getComputedStyle(element)
}
/**
* Get bounding client rect for an element
* @param {Element} element - Element to measure
* @returns {DOMRect|object}
*/
static getBoundingClientRect(element) {
if (Environment.isSSR()) {
if (!shim) this.init()
return shim.getBoundingClientRect(element)
}
return element
? element.getBoundingClientRect()
: {
width: 0,
height: 0,
top: 0,
left: 0,
right: 0,
bottom: 0,
x: 0,
y: 0,
}
}
/**
* Get XMLSerializer instance
* @returns {XMLSerializer|object}
*/
static getXMLSerializer() {
if (Environment.isSSR()) {
if (!shim) this.init()
if (!xmlSerializerInstance) {
xmlSerializerInstance = shim.createXMLSerializer()
}
return xmlSerializerInstance
}
if (!xmlSerializerInstance) {
xmlSerializerInstance = new XMLSerializer()
}
return xmlSerializerInstance
}
/**
* Get DOMParser instance
* @returns {DOMParser|object}
*/
static getDOMParser() {
if (Environment.isSSR()) {
if (!shim) this.init()
if (!domParserInstance) {
domParserInstance = shim.createDOMParser()
}
return domParserInstance
}
if (!domParserInstance) {
domParserInstance = new DOMParser()
}
return domParserInstance
}
/**
* Add event listener to window
* @param {string} event - Event name
* @param {EventListenerOrEventListenerObject} handler - Event handler
* @param {object} options - Event options
*/
static addWindowEventListener(event, handler, options) {
if (Environment.isBrowser()) {
window.addEventListener(event, handler, options)
}
// No-op in SSR
}
/**
* Remove event listener from window
* @param {string} event - Event name
* @param {EventListenerOrEventListenerObject} handler - Event handler
* @param {object} options - Event options
*/
static removeWindowEventListener(event, handler, options) {
if (Environment.isBrowser()) {
window.removeEventListener(event, handler, options)
}
// No-op in SSR
}
/**
* Request animation frame
* @param {FrameRequestCallback} callback - Callback function
* @returns {number|null}
*/
static requestAnimationFrame(callback) {
if (Environment.isBrowser()) {
return window.requestAnimationFrame(callback)
}
// Execute immediately in SSR
callback(0)
return null
}
/**
* Cancel animation frame
* @param {number} id - Animation frame ID
*/
static cancelAnimationFrame(id) {
if (Environment.isBrowser() && id) {
window.cancelAnimationFrame(id)
}
// No-op in SSR
}
/**
* Check if element exists
* @param {Element} element - Element to check
* @returns {boolean}
*/
static elementExists(element) {
if (!element) return false
if (Environment.isSSR()) {
// In SSR, element is valid if it's our mock element
// @ts-ignore — _ssrMode is an internal SSR-only property on mock elements
return element._ssrMode === true || element.nodeName !== undefined
}
// In browser, check if element is in DOM
return element.getRootNode
? element.getRootNode({ composed: true }) === document ||
element.isConnected
: false
}
/**
* Get window object (or null in SSR)
* @returns {Window|null}
*/
static getWindow() {
return Environment.isBrowser() ? window : null
}
/**
* Get document object (or null in SSR)
* @returns {Document|null}
*/
static getDocument() {
return Environment.isBrowser() ? document : null
}
/**
* Get the shim instance (for testing purposes)
* @returns {SSRDOMShim|null}
*/
static _getShim() {
return shim
}
/**
* Reset the shim instance (for testing purposes)
*/
static _resetShim() {
shim = null
xmlSerializerInstance = null
domParserInstance = null
}
}