"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Annotation
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextBlock = exports.Paragraph = exports.LineBreakRun = exports.FractionRun = exports.TextRun = exports.Run = exports.TextBlockComponent = void 0;
/** Abstract representation of any of the building blocks that make up a [[TextBlock]] document - namely [[Run]]s, [[Paragraph]]s, and [[TextBlock]] itself.
 * Each component can specify a [[TextStyle]] that formats its contents and optional [[styleOverrides]] to customize that formatting.
 * @beta
 */
class TextBlockComponent {
    /** @internal */
    constructor(props) {
        this._styleName = props.styleName;
        this._styleOverrides = { ...props.styleOverrides };
    }
    /** The name of the [[TextStyle]] that provides the base formatting for the contents of this component.
     * @note Assigning to this property is equivalent to calling [[applyStyle]] with default [[ApplyTextStyleOptions]], which propagates the style change to all of
     * the components sub-components and clears any [[styleOverrides]].
     */
    get styleName() {
        return this._styleName;
    }
    set styleName(styleName) {
        this.applyStyle(styleName);
    }
    /** Deviations in individual properties of the [[TextStyle]] specified by [[styleName]].
     * For example, if the style uses the "Arial" font, you can override that by settings `styleOverrides.fontName` to "Comic Sans".
     * @see [[clearStyleOverrides]] to reset this to an empty object.
     */
    get styleOverrides() {
        return this._styleOverrides;
    }
    set styleOverrides(overrides) {
        this._styleOverrides = { ...overrides };
    }
    /** Reset any [[styleOverrides]] applied to this component's [[TextStyle]]. */
    clearStyleOverrides() {
        this.styleOverrides = {};
    }
    /** Apply the [[TextStyle]] specified by `styleName` to this component, optionally preserving [[styleOverrides]] and/or preventing propagation to sub-components. */
    applyStyle(styleName, options) {
        this._styleName = styleName;
        if (!(options?.preserveOverrides)) {
            this.clearStyleOverrides();
        }
    }
    /** Returns true if [[styleOverrides]] specifies any deviations from this component's base [[TextStyle]]. */
    get overridesStyle() {
        return Object.keys(this.styleOverrides).length > 0;
    }
    /** Convert this component to its JSON representation. */
    toJSON() {
        return {
            styleName: this.styleName,
            styleOverrides: { ...this.styleOverrides },
        };
    }
    /** Returns true if `this` is equivalent to `other`. */
    equals(other) {
        const myKeys = Object.keys(this.styleOverrides);
        const yrKeys = Object.keys(other._styleOverrides);
        if (this.styleName !== other.styleName || myKeys.length !== yrKeys.length) {
            return false;
        }
        for (const name of myKeys) {
            const key = name;
            if (this.styleOverrides[key] !== other.styleOverrides[key]) {
                return false;
            }
        }
        return true;
    }
}
exports.TextBlockComponent = TextBlockComponent;
/** A sequence of characters within a [[Paragraph]] that share a single style. Runs are the leaf nodes of a [[TextBlock]] document. When laid out for display, a single run may span
 * multiple lines, but it will never contain different styling.
 * Use the `type` field to discriminate between the different kinds of runs.
 * @beta
 */
var Run;
(function (Run) {
    /** Create a run from its JSON representation.
     * @see [[TextRun.create]], [[FractionRun.create]], and [[LineBreakRun.create]] to create a run directly.
     */
    function fromJSON(props) {
        switch (props.type) {
            case "text": return TextRun.create(props);
            case "fraction": return FractionRun.create(props);
            case "linebreak": return LineBreakRun.create(props);
        }
    }
    Run.fromJSON = fromJSON;
})(Run || (exports.Run = Run = {}));
/** The most common type of [[Run]], containing a sequence of characters to be displayed using a single style.
 * @beta
 */
class TextRun extends TextBlockComponent {
    constructor(props) {
        super(props);
        /** Discriminator field for the [[Run]] union. */
        this.type = "text";
        this.content = props.content ?? "";
        this.baselineShift = props.baselineShift ?? "none";
    }
    clone() {
        return new TextRun(this.toJSON());
    }
    toJSON() {
        return {
            ...super.toJSON(),
            type: "text",
            content: this.content,
            baselineShift: this.baselineShift,
        };
    }
    static create(props) {
        return new TextRun(props);
    }
    /** Simply returns [[content]]. */
    stringify() {
        return this.content;
    }
    equals(other) {
        return other instanceof TextRun && this.content === other.content && this.baselineShift === other.baselineShift && super.equals(other);
    }
}
exports.TextRun = TextRun;
/** A [[Run]] containing a numeric ratio to be displayed as a numerator and denominator separated by a horizontal or diagonal bar.
 * @note The [[numerator]] and [[denominator]] are stored as strings. They are not technically required to contain a numeric representation.
 * @beta
 */
class FractionRun extends TextBlockComponent {
    constructor(props) {
        super(props);
        /** Discriminator field for the [[Run]] union. */
        this.type = "fraction";
        this.numerator = props.numerator ?? "";
        this.denominator = props.denominator ?? "";
    }
    toJSON() {
        return {
            ...super.toJSON(),
            type: "fraction",
            numerator: this.numerator,
            denominator: this.denominator,
        };
    }
    clone() {
        return new FractionRun(this.toJSON());
    }
    static create(props) {
        return new FractionRun(props);
    }
    /** Formats the fraction as a string with the [[numerator]] and [[denominator]] separated by [[TextBlockStringifyOptions.fractionSeparator]]. */
    stringify(options) {
        const sep = options?.fractionSeparator ?? "/";
        return `${this.numerator}${sep}${this.denominator}`;
    }
    equals(other) {
        return other instanceof FractionRun && this.numerator === other.numerator && this.denominator === other.denominator && super.equals(other);
    }
}
exports.FractionRun = FractionRun;
/** A [[Run]] that represents the end of a line of text within a [[Paragraph]]. It contains no content of its own - it simply causes subsequent content to display on a new line.
 * @beta
 */
class LineBreakRun extends TextBlockComponent {
    constructor(props) {
        super(props);
        /** Discriminator field for the [[Run]] union. */
        this.type = "linebreak";
    }
    toJSON() {
        return {
            ...super.toJSON(),
            type: "linebreak",
        };
    }
    static create(props) {
        return new LineBreakRun(props);
    }
    clone() {
        return new LineBreakRun(this.toJSON());
    }
    /** Simply returns [[TextBlockStringifyOptions.lineBreak]]. */
    stringify(options) {
        return options?.lineBreak ?? " ";
    }
    equals(other) {
        return other instanceof LineBreakRun && super.equals(other);
    }
}
exports.LineBreakRun = LineBreakRun;
/** A collection of [[Run]]s within a [[TextBlock]]. Each paragraph within a text block is laid out on a separate line.
 * @beta
 */
class Paragraph extends TextBlockComponent {
    constructor(props) {
        super(props);
        this.runs = props.runs?.map((run) => Run.fromJSON(run)) ?? [];
    }
    toJSON() {
        return {
            ...super.toJSON(),
            runs: this.runs.map((run) => run.toJSON()),
        };
    }
    /** Create a paragraph from its JSON representation. */
    static create(props) {
        return new Paragraph(props);
    }
    clone() {
        return new Paragraph(this.toJSON());
    }
    /** Apply the specified style to this [[Paragraph]], and - unless [[ApplyTextStyleOptions.preventPropagation]] is `true` - to all of its [[runs]]. */
    applyStyle(styleName, options) {
        super.applyStyle(styleName, options);
        if (!(options?.preventPropagation)) {
            for (const run of this.runs) {
                run.applyStyle(styleName, options);
            }
        }
    }
    /** Compute a string representation of this paragraph by concatenating the string representations of all of its [[runs]]. */
    stringify(options) {
        return this.runs.map((x) => x.stringify(options)).join("");
    }
    equals(other) {
        if (!(other instanceof Paragraph)) {
            return false;
        }
        if (this.runs.length !== other.runs.length || !super.equals(other)) {
            return false;
        }
        return this.runs.every((run, index) => run.equals(other.runs[index]));
    }
}
exports.Paragraph = Paragraph;
/** Represents a formatted text document consisting of a series of [[Paragraph]]s, each laid out on a separate line and containing their own content in the form of [[Run]]s.
 * You can change the content of the document by directly modifying the contents of its [[paragraphs]], or via [[appendParagraph]] and [[appendRun]].
 * No word-wrapping is applied to the document unless a [[width]] greater than zero is specified.
 * @see [[TextAnnotation]] to position a text block as an annotation in 2d or 3d space.
 * @beta
 */
class TextBlock extends TextBlockComponent {
    constructor(props) {
        super(props);
        this.width = props.width ?? 0;
        this.justification = props.justification ?? "left";
        this.paragraphs = props.paragraphs?.map((x) => Paragraph.create(x)) ?? [];
    }
    toJSON() {
        return {
            ...super.toJSON(),
            width: this.width,
            justification: this.justification,
            paragraphs: this.paragraphs.map((x) => x.toJSON()),
        };
    }
    /** Create a text block from its JSON representation. */
    static create(props) {
        return new TextBlock(props);
    }
    /** Create an empty text block containing no [[paragraphs]] and an empty [[styleName]]. */
    static createEmpty() {
        return TextBlock.create({ styleName: "" });
    }
    /** Returns true if every paragraph in this text block is empty. */
    get isEmpty() {
        return this.paragraphs.every((p) => p.runs.length === 0);
    }
    clone() {
        return new TextBlock(this.toJSON());
    }
    /** Apply the specified style to this block and - unless [[ApplyTextStyleOptions.preventPropagation]] is `true` - to all of its [[paragraphs]]. */
    applyStyle(styleName, options) {
        super.applyStyle(styleName, options);
        if (!(options?.preventPropagation)) {
            for (const paragraph of this.paragraphs) {
                paragraph.applyStyle(styleName, options);
            }
        }
    }
    /** Compute a string representation of the document's contents by concatenating the string representations of each of its [[paragraphs]], separated by [[TextBlockStringifyOptions.paragraphBreak]]. */
    stringify(options) {
        return this.paragraphs.map((x) => x.stringify(options)).join(options?.paragraphBreak ?? " ");
    }
    /** Add and return a new paragraph.
     * If [[paragraphs]] is not empty, the style and overrides of the last [[Paragraph]] in the block will be applied to the new paragraph; otherwise,
     * the paragraph will inherit this block's style with no overrides.
     */
    appendParagraph() {
        const seed = this.paragraphs[0];
        const paragraph = Paragraph.create({
            styleName: seed?.styleName ?? this.styleName,
            styleOverrides: seed?.styleOverrides ?? undefined,
        });
        this.paragraphs.push(paragraph);
        return paragraph;
    }
    /** Append a run to the last [[Paragraph]] in this block.
     * If the block contains no [[paragraphs]], a new one will first be created using [[appendParagraph]].
     */
    appendRun(run) {
        const paragraph = this.paragraphs[this.paragraphs.length - 1] ?? this.appendParagraph();
        paragraph.runs.push(run);
    }
    equals(other) {
        if (!(other instanceof TextBlock)) {
            return false;
        }
        if (this.width !== other.width || this.justification !== other.justification || this.paragraphs.length !== other.paragraphs.length) {
            return false;
        }
        return this.paragraphs.every((paragraph, index) => paragraph.equals(other.paragraphs[index]));
    }
}
exports.TextBlock = TextBlock;
//# sourceMappingURL=TextBlock.js.map