UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

359 lines • 14.4 kB
"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 { _styleName; _styleOverrides; /** @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 { /** Discriminator field for the [[Run]] union. */ type = "text"; /** The sequence of characters to be displayed by the run. */ content; /** Whether to display [[content]] as a subscript, superscript, or normally. */ baselineShift; constructor(props) { super(props); 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 { /** Discriminator field for the [[Run]] union. */ type = "fraction"; /** The fraction's numerator. */ numerator; /** The fraction's denominator. */ denominator; constructor(props) { super(props); 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 { /** Discriminator field for the [[Run]] union. */ type = "linebreak"; constructor(props) { super(props); } 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 { /** The runs within the paragraph. You can modify the contents of this array to change the content of the paragraph. */ runs; 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 { /** The width of the document in meters. Lines that would exceed this width are instead wrapped around to the next line if possible. * A value less than or equal to zero indicates no wrapping is to be applied. * Default: 0 */ width; /** The alignment of the document's content. */ justification; /** The margins of the document. */ margins; /** The ordered list of paragraphs within the document. */ paragraphs; constructor(props) { super(props); this.width = props.width ?? 0; this.justification = props.justification ?? "left"; // Assign default margins if not provided this.margins = { left: props.margins?.left ?? 0, right: props.margins?.right ?? 0, top: props.margins?.top ?? 0, bottom: props.margins?.bottom ?? 0, }; this.paragraphs = props.paragraphs?.map((x) => Paragraph.create(x)) ?? []; } toJSON() { return { ...super.toJSON(), width: this.width, justification: this.justification, margins: this.margins, 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; } const marginsAreEqual = Object.entries(this.margins).every(([key, value]) => value === other.margins[key]); if (!marginsAreEqual) return false; return this.paragraphs.every((paragraph, index) => paragraph.equals(other.paragraphs[index])); } } exports.TextBlock = TextBlock; //# sourceMappingURL=TextBlock.js.map