import {Component_A} from './basic/as-component.js';
import {ErrorCode} from '../exception/exceptionDesc.js';
/**
* @typedef TComponents.PageProps
* @prop {Function} [onCreated] Function to be called when the page is created.
* @prop {Function} [onMounted] Function to be called when the page is mounted.
* @prop {string} [position] CSS position property of the page.
* @prop {number} [width] Width of the page.
* @prop {number} [height] Height of the page.
* @prop {number} [top] Top offset of the page.
* @prop {number} [left] Left offset of the page.
* @prop {number} [zIndex] Z-index of the page.
* @prop {string} [backgroundColor] Background color of the page.
* @prop {string} [id] Unique identifier for the page.
* @prop {string} [name] Name of the page.
* @prop {any[]} [children] Child components of the page.
* @memberof TComponents
*/
/**
* @ignore
*/
const logModule = 'as-page';
/**
* @description Page component that extends the base Component_A class. This component handles the layout and rendering of a page.
* This class focuses on the specific properties of the Page component.
* Since it inherits from Accessor_A, all basic properties (e.g., height, width) are available but documented in the Accessor_A part.
* @class TComponents.Page
* @extends TComponents.Component_A
* @memberof TComponents
* @param {HTMLElement} parent - The parent HTML element that will contain the Page component.
* @param {TComponents.PageProps} [props={}] - The properties to initialize the Page component.
* @example
* const page = new TComponents.Page(document.body,{
* position: 'absolute',
* zIndex: 1000,
* width: 200,
* height: 150,
* backgroundColor: 'blue',
* })
*
* // Render the component.
* page.render();
*/
export class Page extends Component_A {
constructor(parent, props = {}) {
super(parent, props);
/**
* @instance
* @private
* @type {TComponents.PageProps}
*/
this._props;
this._children = new Map();
}
/**
* @description Returns the default values of class properties (excluding parent properties).
* @member TComponents.Page#defaultProps
* @method
* @protected
* @returns {TComponents.PageProps}
*/
defaultProps() {
return {
// life cycle
onCreated: '',
onMounted: '',
onDispose: '',
// X/Y/W/H/B/R
position: 'absolute',
width: 1024,
height: 680,
top: 0,
left: 0,
zIndex: 0,
// Special properties
backgroundColor: 'white',
id: '',
name: '',
children: [],
};
}
/**
* @description Initializes the Page component.
* This method is called during the component's initialization phase.
* @member TComponents.Page#onInit
* @method
* @throws {Error} Throws an error if initializing fails.
* @returns {void}
*/
onInit() {
try {
this._children.clear();
const childrenArray = Object.values(this._props.children || []);
childrenArray.forEach((child, idx) => {
if (typeof child === 'string') {
// check if child has # and remove it
const elementId = child.replace(/^#/, '');
child = elementId.startsWith('.') ? document.querySelector(elementId) : document.getElementById(elementId);
if (!child) {
throw new Error(`Container_A: Could not find element with selector/id: ${elementId} in the DOM.
Check the selector or if inside a TComponent, then try adding the child as Element or Component_A instance,
since this may not be yet available in the DOM.`);
}
if (!child.id) {
child.id = this._childId(idx);
}
this._children.set(child, child.id);
} else if (Component_A.isTComponent(child)) {
this._children.set(child, child.compId);
} else if (Component_A._isHTMLElement(child)) {
if (!child.id) {
child.id = this._childId(idx);
}
this._children.set(child, child.id);
} else throw new Error(`Unexpected type of child detected: ${typeof child}`);
});
} catch (e) {
Logger.e(logModule, ErrorCode.FailedToRunOnInit, `Error happens on onInit of Page component ${this.compId}.`, e);
throw ErrorCode.FailedToRunOnInit;
}
}
/**
* @description Maps the child components of the Page.
* @member TComponents.Page#mapComponents
* @method
* @returns {object}
*/
mapComponents() {
return {children: [...this._children.keys()]};
}
/**
* @description Renders the Page component. This method is responsible for cleaning up events and updating the DOM.
* @member TComponents.Page#onRender
* @method
* @throws {Error} Throws an error if rendering fails.
* @returns {void}
*/
onRender() {
try {
this.removeAllEventListeners();
if (Component_A._isHTMLElement(this.parent)) {
this.parent.style.height = `${this._props.height}`;
this.parent.style.width = `${this._props.width}`;
this.parent.style.top = `${this._props.top}`;
this.parent.style.left = `${this._props.left}`;
this.parent.style.zIndex = `${this._props.zIndex}`;
this.parent.style.backgroundColor = `${this._props.backgroundColor}`;
if (this._props.id !== '') {
const realPageElem = this.parent.querySelector(`#${this._props.id}`);
if (Component_A._isHTMLElement(realPageElem)) {
realPageElem.style.cssText = this.container.style.cssText;
realPageElem.className = this.container.className;
}
}
this.parent.removeChild(this.container);
}
} catch (e) {
// Runtime errors: write specific content to the log, throw error code
Logger.e(
logModule,
ErrorCode.FailedToRunOnRender,
`Error happens on onRender of Page component ${this.compId}.`,
e,
);
throw new Error(ErrorCode.FailedToRunOnRender, {cause: e});
}
}
/**
* @description Generates the HTML markup for the Page component.
* @member TComponents.Page#markup
* @method
* @returns {string} The HTML markup for the Page component.
*/
markup() {
return /*html*/ ``;
}
/**
* @description Generates the CSS text for the Page component based on its properties.
* @member TComponents.Page#getCssText
* @method
* @returns {string} The CSS text for the Page component.
*/
getCssText() {
const cssText = `position: ${this._props.position};
left: ${this._props.left}px;
top: ${this._props.top}px;
height: ${this._props.height}px;
width: ${this._props.width}px;
background-color: ${this._props.backgroundColor};
z-index: ${this._props.zIndex};`
.replace(/\s+/g, ' ')
.trim();
return cssText;
}
/**
* @returns {object}
*/
get properties() {
return this._props;
}
/**
* Sets the properties of the Page component.
* **Note that changing this property will not cause the component to refresh directly.**
* @member {TComponents.PageProps} TComponents.Page#properties
* @instance
* @param {TComponents.PageProps} t - The new properties of the Page component.
* @example
* const page = new TComponents.Page(document.body,{
* position: 'absolute',
* zIndex: 1000,
* width: 200,
* height: 150,
* backgroundColor: 'blue',
* })
*
* // Render the component.
* page.render();
*
* page.properties = {backgroundColor: 'red'};
*/
set properties(t) {
this._props = t;
}
/**
* @description Appends a child component to the Page.
* @member TComponents.Page#appendChild
* @method
* @param {TComponents.Component_A} t - The child component to be appended.
* @example
* const page = new TComponents.Page(document.body,{
* position: 'absolute',
* zIndex: 1000,
* width: 200,
* height: 150,
* backgroundColor: 'blue',
* })
*
* // Render the component.
* page.render();
*
* // Create a button
* const button = new TComponents.Button(null, {text: 'Click me'});
*
* page.appendChild(button);
*/
appendChild(t) {
try {
if (typeof t == 'object' && typeof t.render == 'function' && typeof t.destroy == 'function') {
t.destroy();
t.parent = this.parent;
t.render();
}
} catch (e) {
Logger.e(
logModule,
ErrorCode.FailedToAppendChild,
`Error happens on appendChild of Page component ${this.compId}.`,
e,
);
throw new Error(ErrorCode.FailedToAppendChild, {cause: e});
}
}
/**
* @description Removes a child component from the Page.
* @member TComponents.Page#removeChild
* @method
* @param {TComponents.Component_A} t - The child component to be removed.
* @example
* const page = new TComponents.Page(document.body,{ ... });
*
* // Remove the button.
* page.removeChild(button)
*/
removeChild(t) {
try {
if (typeof t == 'object' && typeof t.destroy == 'function') {
t.destroy();
}
} catch (e) {
Logger.e(
logModule,
ErrorCode.FailedToRemoveChild,
`Error happens on removeChild of Page component ${this.compId}.`,
e,
);
throw new Error(ErrorCode.FailedToRemoveChild, {cause: e});
}
}
}
/**
* @description Add css properties to the component
* @alias loadCssClassFromString
* @member TComponent.Page.loadCssClassFromString
* @method
* @static
* @param {string} css - The css string to be loaded into style tag
* @returns {void}
* @example
* TComponent.Page.loadCssClassFromString(`
* .tc-page {
* background-color: 'red';
* }
* `)
*/
Page.loadCssClassFromString(``);