as-text.js

import {Component_A} from './basic/as-component.js';
import {FP_Text_A} from './fp-ext/fp-text-ext.js';
import {ErrorCode} from '../exception/exceptionDesc.js';

/**
 * @typedef TComponents.TextProps
 * @prop {object} [options] Additional options for the text component.
 * Items in this object include:
 * - **responsive** (boolean, default: false): Whether the text component should be responsive.
 * @prop {string} [tips] Tooltip text for the component.
 * @prop {Function} [onCreated] Function to be called when the text component is created.
 * @prop {Function} [onMounted] Function to be called when the text component is mounted.
 * @prop {string} [textType] Type of the text content, e.g. 'body', 'header' or 'description'.
 * @prop {string} [text] Text content to display.
 * @prop {string} [backgroundColor] Background color of the text area.
 * @prop {string} [position] CSS position property.
 * @prop {number} [width] Width of the component.
 * @prop {number} [height] Height of the component.
 * @prop {number} [top] Top position of the component.
 * @prop {number} [left] Left position of the component.
 * @prop {number} [borderRadius] Border radius of the component.
 * @prop {number} [rotation] Rotation angle of the component.
 * @prop {number} [zIndex] Z-index of the component.
 * @prop {string} [color] Text color.
 * @prop {object} [font] Font configuration for the text.
 * This object controls text appearance:
 * - **fontSize** (number, default: 12): Font size in pixels.
 * - **fontFamily** (string, default: 'Segoe UI'): Font family name.
 * - **style** (object): Font style configuration, containing `fontStyle`, `fontWeight`, `textDecoration`.
 * - **textAlign** (string, default: 'left'): Text alignment of the text.
 * @prop {string} [defaultState] Default state of the component.
 * @prop {string} [dataStruct] Custom data structure metadata for integration with other systems.
 * @memberof TComponents
 */

/**
 * @ignore
 */
const logModule = 'as-text';

/**
 * @description  Text component that displays various types of text (e.g., headers, body, description) with customizable styles.
 * This class focuses on the specific properties of the Text 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.Text
 * @extends TComponents.Component_A
 * @memberof TComponents
 * @param {HTMLElement} parent - HTML element that is going to be the parent of the component
 * @param {TComponents.TextProps} props
 * @example
 * const text = new TComponents.Text(document.body, {
 *   position: 'absolute',
 *   zIndex: 1000,
 *   text: 'Hello',
 *   color: '#000',
 * });
 *
 * // Render the component.
 * text.render();
 */
export class Text extends Component_A {
  constructor(parent, props = {}) {
    super(parent, props);

    /**
     * @instance
     * @private
     * @type {TComponents.TextProps}
     */
    this._props;

    this._text = new FP_Text_A();
  }

  /**
   * @description Returns the default values of class properties (excluding parent properties).
   * @member TComponents.Text#defaultProps
   * @method
   * @protected
   * @returns {TComponents.TextProps}
   */
  defaultProps() {
    return {
      options: {
        responsive: false,
      },
      tips: '',
      // life cycle
      onCreated: '',
      onMounted: '',
      onDispose: '',
      // content & type
      textType: 'body',
      text: 'This is a Text',
      // background
      backgroundColor: 'rgba(255,255,255,0)',
      // ⭐ W/H/X/Y/B/R/Z: Component required attributes.
      position: 'static',
      width: 72,
      height: 32,
      top: 0,
      left: 0,
      borderRadius: 4,
      rotation: 0,
      zIndex: 0,
      // color
      color: '#000000',
      // font
      font: {
        fontSize: 12,
        fontFamily: 'Segoe UI',
        style: {
          fontStyle: 'normal',
          fontWeight: 'normal',
          textDecoration: 'none',
        },
        textAlign: 'left',
      },
      defaultState: 'show_enable',
      dataStruct: '',
    };
  }

  /**
   * @description Initializes the component asynchronously.
   * @memberof TComponents.Text#onInit
   * @method
   * @async
   * @returns {Promise<void>}
   */
  async onInit() {}

  /**
   * @description Renders the text component and applies the styles and properties.
   * @member TComponents.Text#onRender
   * @method
   * @throws {Error} Throws error code ErrorCode.FailedToRunOnRender when runtime error happens.
   * @returns {void}
   */
  onRender() {
    try {
      this.removeAllEventListeners();

      if (this._props.labelPos === 'left' || this._props.labelPos === 'right') {
        this.container.classList.add('justify-stretch');
      }

      this._text.text = this._props.text;
      this._text.textType = this._props.textType;
      this._text.borderRadius = this._props.borderRadius;
      this._text.color = this._props.color;
      this._text.backgroundColor = this._props.backgroundColor;
      this._text.font = this._props.font;

      const textContainer = this.find('.tc-text');
      if (Component_A._isHTMLElement(textContainer)) this._text.attachToElement(textContainer);
      this._addTips();
      Component_A.resolveBindingExpression(this._props.text, this);
    } catch (e) {
      // Runtime errors: write specific content to the log, throw error code
      Logger.e(
        logModule,
        ErrorCode.FailedToRunOnRender,
        `Error happens on onRender of Text component ${this.compId}.`,
        e,
      );
      throw new Error(ErrorCode.FailedToRunOnRender, {cause: e});
    }
  }

  /**
   * @description Returns the markup for the text component.
   * @member TComponents.Text#markup
   * @method
   * @returns {string} HTML markup for the text component.
   */
  markup() {
    return /*html*/ `<div class="tc-text"></div>`;
  }

  /**
   * @returns {string}
   */
  get text() {
    return this._props.text;
  }

  /**
   * @description Sets the text content.
   * @member {string} TComponents.Text#text
   * @instance
   * @param {string} value - The text content.
   * @example
   * const text = new TComponents.Text(document.body, {
   *   position: 'absolute',
   *   zIndex: 1000,
   *   text: 'Hello',
   *   color: '#000',
   * });
   *
   * text.render();
   *
   * // Set text value.
   * text.text = 'new Text';
   */
  set text(value) {
    this.commitProps({text: value});
    this._text.text = value;
  }

  /**
   * @returns {string}
   */
  get textType() {
    return this._props.textType;
  }

  /**
   * @description Sets the text type and adjusts corresponding styles.
   * The supported text types are:
   * - `Header1`
   * - `Header2`
   * - `Header3`
   * - `Body`
   * - `Description`
   * - `Error`
   * - `Warning`
   * - `Success`
   * - `Information`
   * @member {string} TComponents.Text#textType
   * @instance
   * @param {string} t - The text type.
   * @example
   * const text = new TComponents.Text(document.body, {
   *   position: 'absolute',
   *   zIndex: 1000,
   *   text: 'Hello',
   *   color: '#000',
   * });
   *
   * text.render();
   *
   * // Set text tyoe.
   * text.textType = 'Header1';
   */
  set textType(t) {
    let textType = this._props.textType;
    let color = this._props.color;
    let font = Object.assign({}, this._props.font);

    if (t == 'Header1') {
      textType = 'h1';
      color = '#000';
      font.fontSize = 32;
      font.fontWeight = 'bold';
    } else if (t == 'Header2') {
      textType = 'h2';
      color = '#000';
      font.fontSize = 24;
      font.fontWeight = 'bold';
    } else if (t == 'Header3') {
      textType = 'h3';
      color = '#000';
      font.fontSize = 19;
      font.fontWeight = 'bold';
    } else if (t == 'Body') {
      textType = 'body';
      color = '#000';
      font.fontSize = 16;
    } else if (t == 'Description') {
      textType = 'description';
      color = '#888';
      font.fontSize = 16;
    } else if (t == 'Error') {
      textType = 'error';
      color = 'red';
    } else if (t == 'Warning') {
      textType = 'warning';
      color = 'orange';
    } else if (t == 'Success') {
      textType = 'success';
      color = 'green';
    } else if (t == 'Information') {
      textType = 'information';
      color = 'blue';
    } else {
      Logger.w(logModule, ErrorCode.FailedToSetTextType, `Invalid textType for component ${this.compId}.`, t);
      return;
    }

    this.setProps({textType: textType, color: color, font: font});
  }
}

/**
 * @description Add css properties to the component
 * @member TComponents.Text.loadCssClassFromString
 * @method
 * @static
 * @param {string} css - The css string to be loaded into style tag
 * @returns {void}
 * @example
 * TComponents.Text.loadCssClassFromString(`
 *   .tc-text {
 *     height: inherit;
 *    }`
 * );
 */
Text.loadCssClassFromString(/*css*/ `
.tc-text {
  height: inherit;
  width: 100%;
  min-width: 0px;
  min-height: 0px;
  padding: 0px;
  margin: 0px;
}

.tc-text .fp-components-text,
.tc-text .fp-components-text-disabled {
  height: 100%;
  width: 100%;
  min-width: 0px;
  min-height: 0px;
  padding: 0px;
  margin: 0px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
}

.tc-text .fp-components-text-disabled{
  cursor:not-allowed !important;
}
.tc-text .fp-components-text:hover,
.tc-text .fp-components-text-disabled:hover{
  opacity:0.7;
}


.fp-components-text-h1 {
  font-size: 2em;
  font-weight: bold;
  color: #000;
}

.fp-components-text-h2 {
  font-size: 1.5em;
  font-weight: bold;
  color: #000;
}

.fp-components-text-h3 {
  font-size: 1.17em;
  font-weight: bold;
  color: #000;
}

.fp-components-text-description {
  font-size: 1em;
  color: #888;
}

.fp-components-text-error {
  color: red;
}

.fp-components-text-warning {
  color: orange;
}

.fp-components-text-success {
  color: green;
}

.fp-components-text-information {
  color: blue;
}
`);