@itwin/core-common 5.2.0-dev.31 → 5.2.0-dev.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/annotation/TextBlock.d.ts +98 -37
- package/lib/cjs/annotation/TextBlock.d.ts.map +1 -1
- package/lib/cjs/annotation/TextBlock.js +211 -77
- package/lib/cjs/annotation/TextBlock.js.map +1 -1
- package/lib/cjs/annotation/TextBlockLayoutResult.d.ts +3 -5
- package/lib/cjs/annotation/TextBlockLayoutResult.d.ts.map +1 -1
- package/lib/cjs/annotation/TextBlockLayoutResult.js.map +1 -1
- package/lib/cjs/annotation/TextStyle.d.ts +63 -1
- package/lib/cjs/annotation/TextStyle.d.ts.map +1 -1
- package/lib/cjs/annotation/TextStyle.js +44 -4
- package/lib/cjs/annotation/TextStyle.js.map +1 -1
- package/lib/esm/annotation/TextBlock.d.ts +98 -37
- package/lib/esm/annotation/TextBlock.d.ts.map +1 -1
- package/lib/esm/annotation/TextBlock.js +207 -76
- package/lib/esm/annotation/TextBlock.js.map +1 -1
- package/lib/esm/annotation/TextBlockLayoutResult.d.ts +3 -5
- package/lib/esm/annotation/TextBlockLayoutResult.d.ts.map +1 -1
- package/lib/esm/annotation/TextBlockLayoutResult.js.map +1 -1
- package/lib/esm/annotation/TextStyle.d.ts +63 -1
- package/lib/esm/annotation/TextStyle.d.ts.map +1 -1
- package/lib/esm/annotation/TextStyle.js +43 -3
- package/lib/esm/annotation/TextStyle.js.map +1 -1
- package/package.json +6 -6
|
@@ -5,9 +5,19 @@
|
|
|
5
5
|
/** @packageDocumentation
|
|
6
6
|
* @module Annotation
|
|
7
7
|
*/
|
|
8
|
-
import { TextStyleSettings } from "./TextStyle";
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import { ListMarkerEnumerator, TextStyleSettings } from "./TextStyle";
|
|
9
|
+
function clearStyleOverrides(component, options) {
|
|
10
|
+
component.styleOverrides = {};
|
|
11
|
+
if (!options?.preserveChildrenOverrides) {
|
|
12
|
+
for (const child of component.children) {
|
|
13
|
+
child.clearStyleOverrides(options);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Abstract representation of any of the building blocks that make up a [[TextBlock]] document - namely [[Run]]s and [[StructuralTextBlockComponent]]s.
|
|
19
|
+
* The [[TextBlock]] can specify an [AnnotationTextStyle]($backend) that formats its contents.
|
|
20
|
+
* Each component can specify an optional [[styleOverrides]] to customize that formatting.
|
|
11
21
|
* @beta
|
|
12
22
|
*/
|
|
13
23
|
export class TextBlockComponent {
|
|
@@ -72,15 +82,15 @@ export class TextBlockComponent {
|
|
|
72
82
|
export var Run;
|
|
73
83
|
(function (Run) {
|
|
74
84
|
/** Create a run from its JSON representation.
|
|
75
|
-
* @see [[TextRun.create]], [[FractionRun.create]], and [[LineBreakRun.create]] to create a run directly.
|
|
85
|
+
* @see [[TextRun.create]], [[FractionRun.create]], [[FieldRun.create]], [[TabRun.create]], and [[LineBreakRun.create]] to create a run directly.
|
|
76
86
|
*/
|
|
77
87
|
function fromJSON(props) {
|
|
78
88
|
switch (props.type) {
|
|
79
|
-
case "
|
|
89
|
+
case "field": return FieldRun.create(props);
|
|
80
90
|
case "fraction": return FractionRun.create(props);
|
|
81
|
-
case "tab": return TabRun.create(props);
|
|
82
91
|
case "linebreak": return LineBreakRun.create(props);
|
|
83
|
-
case "
|
|
92
|
+
case "tab": return TabRun.create(props);
|
|
93
|
+
case "text": return TextRun.create(props);
|
|
84
94
|
}
|
|
85
95
|
}
|
|
86
96
|
Run.fromJSON = fromJSON;
|
|
@@ -112,7 +122,10 @@ export class TextRun extends TextBlockComponent {
|
|
|
112
122
|
};
|
|
113
123
|
}
|
|
114
124
|
static create(props) {
|
|
115
|
-
return new TextRun(props);
|
|
125
|
+
return new TextRun({ ...props, type: "text" });
|
|
126
|
+
}
|
|
127
|
+
get isEmpty() {
|
|
128
|
+
return this.stringify().length === 0;
|
|
116
129
|
}
|
|
117
130
|
/** Simply returns [[content]]. */
|
|
118
131
|
stringify() {
|
|
@@ -150,7 +163,10 @@ export class FractionRun extends TextBlockComponent {
|
|
|
150
163
|
return new FractionRun(this.toJSON());
|
|
151
164
|
}
|
|
152
165
|
static create(props) {
|
|
153
|
-
return new FractionRun(props);
|
|
166
|
+
return new FractionRun({ ...props, type: "fraction" });
|
|
167
|
+
}
|
|
168
|
+
get isEmpty() {
|
|
169
|
+
return this.stringify().length === 0;
|
|
154
170
|
}
|
|
155
171
|
/** Formats the fraction as a string with the [[numerator]] and [[denominator]] separated by [[TextBlockStringifyOptions.fractionSeparator]]. */
|
|
156
172
|
stringify(options) {
|
|
@@ -177,11 +193,14 @@ export class LineBreakRun extends TextBlockComponent {
|
|
|
177
193
|
};
|
|
178
194
|
}
|
|
179
195
|
static create(props) {
|
|
180
|
-
return new LineBreakRun(props);
|
|
196
|
+
return new LineBreakRun({ ...props, type: "linebreak" });
|
|
181
197
|
}
|
|
182
198
|
clone() {
|
|
183
199
|
return new LineBreakRun(this.toJSON());
|
|
184
200
|
}
|
|
201
|
+
get isEmpty() {
|
|
202
|
+
return this.stringify().length === 0;
|
|
203
|
+
}
|
|
185
204
|
/** Simply returns [[TextBlockStringifyOptions.lineBreak]]. */
|
|
186
205
|
stringify(options) {
|
|
187
206
|
return options?.lineBreak ?? " ";
|
|
@@ -209,6 +228,9 @@ export class TabRun extends TextBlockComponent {
|
|
|
209
228
|
static create(props) {
|
|
210
229
|
return new TabRun(props);
|
|
211
230
|
}
|
|
231
|
+
get isEmpty() {
|
|
232
|
+
return this.stringify().length === 0;
|
|
233
|
+
}
|
|
212
234
|
/**
|
|
213
235
|
* Converts a [[TabRun]] to its string representation.
|
|
214
236
|
* If the `tabsAsSpaces` option is provided, returns a string of spaces of the specified length.
|
|
@@ -270,6 +292,7 @@ export class FieldRun extends TextBlockComponent {
|
|
|
270
292
|
propertyHost: { ...props.propertyHost },
|
|
271
293
|
propertyPath: structuredClone(props.propertyPath),
|
|
272
294
|
formatOptions: structuredClone(props.formatOptions),
|
|
295
|
+
type: "field",
|
|
273
296
|
});
|
|
274
297
|
}
|
|
275
298
|
/** Convert the FieldRun to its JSON representation. */
|
|
@@ -292,6 +315,9 @@ export class FieldRun extends TextBlockComponent {
|
|
|
292
315
|
clone() {
|
|
293
316
|
return new FieldRun(this.toJSON());
|
|
294
317
|
}
|
|
318
|
+
get isEmpty() {
|
|
319
|
+
return this.stringify().length === 0;
|
|
320
|
+
}
|
|
295
321
|
/** Convert this FieldRun to a simple string representation. */
|
|
296
322
|
stringify() {
|
|
297
323
|
return this.cachedContent;
|
|
@@ -330,63 +356,97 @@ export class FieldRun extends TextBlockComponent {
|
|
|
330
356
|
return true;
|
|
331
357
|
}
|
|
332
358
|
}
|
|
333
|
-
/** A collection of [[Run]]s
|
|
359
|
+
/** A collection of [[Run]]s and [[List]]s. Paragraphs can be appended to [[List]]s and [[TextBlock]]s.
|
|
360
|
+
* Each paragraph is laid out on a separate line. If included in a [[List]], the paragraph will be treated as a list item.
|
|
334
361
|
* @beta
|
|
335
362
|
*/
|
|
336
363
|
export class Paragraph extends TextBlockComponent {
|
|
337
|
-
|
|
338
|
-
|
|
364
|
+
type = "paragraph";
|
|
365
|
+
children;
|
|
339
366
|
constructor(props) {
|
|
340
367
|
super(props);
|
|
341
|
-
this.
|
|
368
|
+
this.children = props?.children?.map((child) => child.type === "list" ? List.create(child) : Run.fromJSON(child)) ?? [];
|
|
369
|
+
}
|
|
370
|
+
/** Create a paragraph from its JSON representation. */
|
|
371
|
+
static create(props) {
|
|
372
|
+
return new Paragraph(props);
|
|
373
|
+
}
|
|
374
|
+
clearStyleOverrides(options) {
|
|
375
|
+
clearStyleOverrides(this, options);
|
|
376
|
+
}
|
|
377
|
+
get isEmpty() {
|
|
378
|
+
return this.children.length === 0;
|
|
379
|
+
}
|
|
380
|
+
clone() {
|
|
381
|
+
return new Paragraph(this.toJSON());
|
|
342
382
|
}
|
|
343
383
|
toJSON() {
|
|
344
384
|
return {
|
|
345
385
|
...super.toJSON(),
|
|
346
|
-
|
|
386
|
+
children: this.children.map((child) => child.toJSON()),
|
|
347
387
|
};
|
|
348
388
|
}
|
|
349
|
-
/**
|
|
389
|
+
/** Compute a string representation of this paragraph by concatenating the string representations of all of its children. */
|
|
390
|
+
stringify(options, context) {
|
|
391
|
+
return this.children.map((x, index) => (index > 0 && x.type === "list") ? `${options?.paragraphBreak ?? " "}${x.stringify(options, context)}` : x.stringify(options, context)).join("") ?? "";
|
|
392
|
+
}
|
|
393
|
+
equals(other) {
|
|
394
|
+
return (other instanceof Paragraph) && super.equals(other);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/** A collection of list items ([[Paragraph]]s). Lists can be appended to [[Paragraph]]s.
|
|
398
|
+
* Lists will be laid out on a new line. Each item in a list is laid out on a separate line.
|
|
399
|
+
* @beta
|
|
400
|
+
*/
|
|
401
|
+
export class List extends TextBlockComponent {
|
|
402
|
+
type = "list";
|
|
403
|
+
children;
|
|
404
|
+
constructor(props) {
|
|
405
|
+
super(props);
|
|
406
|
+
this.children = props?.children?.map((child) => Paragraph.create(child)) ?? [];
|
|
407
|
+
}
|
|
408
|
+
/** Create a list from its JSON representation. */
|
|
350
409
|
static create(props) {
|
|
351
|
-
return new
|
|
410
|
+
return new List({ ...props, type: "list" });
|
|
411
|
+
}
|
|
412
|
+
clearStyleOverrides(options) {
|
|
413
|
+
clearStyleOverrides(this, options);
|
|
414
|
+
}
|
|
415
|
+
get isEmpty() {
|
|
416
|
+
return this.children.length === 0;
|
|
352
417
|
}
|
|
353
418
|
clone() {
|
|
354
|
-
return new
|
|
419
|
+
return new List(this.toJSON());
|
|
355
420
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (options?.preserveChildrenOverrides)
|
|
363
|
-
return;
|
|
364
|
-
for (const run of this.runs) {
|
|
365
|
-
run.clearStyleOverrides();
|
|
366
|
-
}
|
|
421
|
+
toJSON() {
|
|
422
|
+
return {
|
|
423
|
+
...super.toJSON(),
|
|
424
|
+
type: "list",
|
|
425
|
+
children: this.children.map((run) => run.toJSON()),
|
|
426
|
+
};
|
|
367
427
|
}
|
|
368
|
-
/** Compute a string representation of this
|
|
369
|
-
stringify(options) {
|
|
370
|
-
|
|
428
|
+
/** Compute a string representation of this list by concatenating the string representations of all of its [[children]]. */
|
|
429
|
+
stringify(options, context) {
|
|
430
|
+
const children = this.children.map((x, index) => {
|
|
431
|
+
const depth = context?.depth ?? 0;
|
|
432
|
+
const marker = getMarkerText(this.styleOverrides.listMarker ?? TextStyleSettings.defaultProps.listMarker, index + 1);
|
|
433
|
+
const tab = (options?.tabsAsSpaces ? " ".repeat(options.tabsAsSpaces) : "\t").repeat(depth);
|
|
434
|
+
return `${tab}${marker}${options?.listMarkerBreak ?? " "}${x.stringify(options, { depth: depth + 1 })}`;
|
|
435
|
+
});
|
|
436
|
+
return children.join(options?.paragraphBreak ?? " ") ?? "";
|
|
371
437
|
}
|
|
372
438
|
equals(other) {
|
|
373
|
-
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
if (this.runs.length !== other.runs.length || !super.equals(other)) {
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
return this.runs.every((run, index) => run.equals(other.runs[index]));
|
|
439
|
+
return (other instanceof List) && super.equals(other);
|
|
380
440
|
}
|
|
381
441
|
}
|
|
382
442
|
;
|
|
383
|
-
/** Represents a formatted text document consisting of a series of [[Paragraph]]s, each laid out on a separate line and containing their own content
|
|
384
|
-
* You can change the content of the document by directly modifying the contents of its [[paragraphs]], or via [[appendParagraph]] and [[appendRun]].
|
|
443
|
+
/** Represents a formatted text document consisting of a series of [[Paragraph]]s, each laid out on a separate line and containing their own content.
|
|
385
444
|
* No word-wrapping is applied to the document unless a [[width]] greater than zero is specified.
|
|
386
445
|
* @see [[TextAnnotation]] to position a text block as an annotation in 2d or 3d space.
|
|
387
446
|
* @beta
|
|
388
447
|
*/
|
|
389
448
|
export class TextBlock extends TextBlockComponent {
|
|
449
|
+
children;
|
|
390
450
|
/** The width of the document in meters. Lines that would exceed this width are instead wrapped around to the next line if possible.
|
|
391
451
|
* A value less than or equal to zero indicates no wrapping is to be applied.
|
|
392
452
|
* Default: 0
|
|
@@ -396,8 +456,6 @@ export class TextBlock extends TextBlockComponent {
|
|
|
396
456
|
justification;
|
|
397
457
|
/** The margins of the document. */
|
|
398
458
|
margins;
|
|
399
|
-
/** The ordered list of paragraphs within the document. */
|
|
400
|
-
paragraphs;
|
|
401
459
|
constructor(props) {
|
|
402
460
|
super(props);
|
|
403
461
|
this.width = props.width ?? 0;
|
|
@@ -409,7 +467,10 @@ export class TextBlock extends TextBlockComponent {
|
|
|
409
467
|
top: props.margins?.top ?? 0,
|
|
410
468
|
bottom: props.margins?.bottom ?? 0,
|
|
411
469
|
};
|
|
412
|
-
this.
|
|
470
|
+
this.children = props?.children?.map((para) => Paragraph.create(para)) ?? [];
|
|
471
|
+
}
|
|
472
|
+
clearStyleOverrides(options) {
|
|
473
|
+
clearStyleOverrides(this, options);
|
|
413
474
|
}
|
|
414
475
|
toJSON() {
|
|
415
476
|
return {
|
|
@@ -417,7 +478,7 @@ export class TextBlock extends TextBlockComponent {
|
|
|
417
478
|
width: this.width,
|
|
418
479
|
justification: this.justification,
|
|
419
480
|
margins: this.margins,
|
|
420
|
-
|
|
481
|
+
children: this.children.map((x) => x.toJSON()),
|
|
421
482
|
};
|
|
422
483
|
}
|
|
423
484
|
/** Create a text block from its JSON representation. */
|
|
@@ -426,61 +487,131 @@ export class TextBlock extends TextBlockComponent {
|
|
|
426
487
|
}
|
|
427
488
|
/** Returns true if every paragraph in this text block is empty. */
|
|
428
489
|
get isEmpty() {
|
|
429
|
-
return this.
|
|
490
|
+
return !this.children || this.children.every((child) => child.isEmpty);
|
|
430
491
|
}
|
|
431
492
|
clone() {
|
|
432
493
|
return new TextBlock(this.toJSON());
|
|
433
494
|
}
|
|
434
|
-
/**
|
|
435
|
-
* Clears any [[styleOverrides]] applied to this TextBlock.
|
|
436
|
-
* Will also clear [[styleOverrides]] from all child components unless [[ClearTextStyleOptions.preserveChildrenOverrides]] is `true`.
|
|
437
|
-
*/
|
|
438
|
-
clearStyleOverrides(options) {
|
|
439
|
-
super.clearStyleOverrides();
|
|
440
|
-
if (options?.preserveChildrenOverrides)
|
|
441
|
-
return;
|
|
442
|
-
for (const paragraph of this.paragraphs) {
|
|
443
|
-
paragraph.clearStyleOverrides();
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
/** Compute a string representation of the document's contents by concatenating the string representations of each of its [[paragraphs]], separated by [[TextBlockStringifyOptions.paragraphBreak]]. */
|
|
495
|
+
/** Compute a string representation of the document's contents by concatenating the string representations of each of its [[children]], separated by [[TextBlockStringifyOptions.paragraphBreak]]. */
|
|
447
496
|
stringify(options) {
|
|
448
|
-
return this.
|
|
497
|
+
return this.children.map((x) => x.stringify(options)).join(options?.paragraphBreak ?? " ") || "";
|
|
449
498
|
}
|
|
450
499
|
/** Add and return a new paragraph.
|
|
451
500
|
* By default, the paragraph will be created with no [[styleOverrides]], so that it inherits the style of this block.
|
|
452
|
-
* @param seedFromLast If true and [[
|
|
501
|
+
* @param seedFromLast If true and [[children]] is not empty, the new paragraph will inherit the style overrides of the last child in this block.
|
|
502
|
+
* @note Be sure you pass in [[ParagraphProps]] and not [[Paragraph]] or style overrides will be ignored.
|
|
453
503
|
*/
|
|
454
|
-
appendParagraph(seedFromLast = false) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
const paragraph = Paragraph.create({
|
|
461
|
-
styleOverrides
|
|
462
|
-
});
|
|
463
|
-
this.paragraphs.push(paragraph);
|
|
504
|
+
appendParagraph(props, seedFromLast = false) {
|
|
505
|
+
const seed = seedFromLast ? this.children[this.children.length - 1] : undefined;
|
|
506
|
+
const styleOverrides = { ...seed?.styleOverrides, ...props?.styleOverrides };
|
|
507
|
+
const paragraph = Paragraph.create({ ...props, styleOverrides });
|
|
508
|
+
this.children.push(paragraph);
|
|
464
509
|
return paragraph;
|
|
465
510
|
}
|
|
466
511
|
/** Append a run to the last [[Paragraph]] in this block.
|
|
467
|
-
* If the block contains no [[
|
|
512
|
+
* If the block contains no [[children]], a new [[Paragraph]] will first be created using [[appendParagraph]].
|
|
468
513
|
*/
|
|
469
514
|
appendRun(run) {
|
|
470
|
-
const paragraph = this.
|
|
471
|
-
paragraph.
|
|
515
|
+
const paragraph = this.children[this.children.length - 1] ?? this.appendParagraph();
|
|
516
|
+
paragraph.children.push(run);
|
|
472
517
|
}
|
|
473
518
|
equals(other) {
|
|
474
519
|
if (!(other instanceof TextBlock)) {
|
|
475
520
|
return false;
|
|
476
521
|
}
|
|
477
|
-
if (
|
|
522
|
+
if (!super.equals(other)) {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
if (this.width !== other.width || this.justification !== other.justification) {
|
|
478
526
|
return false;
|
|
479
527
|
}
|
|
480
528
|
const marginsAreEqual = Object.entries(this.margins).every(([key, value]) => value === other.margins[key]);
|
|
481
529
|
if (!marginsAreEqual)
|
|
482
530
|
return false;
|
|
483
|
-
|
|
531
|
+
if (this.children && other.children) {
|
|
532
|
+
if (this.children.length !== other.children.length) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
return this.children.every((child, index) => other.children && child.equals(other.children[index]));
|
|
536
|
+
}
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Recursively traverses a [[StructuralTextBlockComponent]] tree, yielding each child component along with its parent container.
|
|
542
|
+
* This generator enables depth-first iteration over all components in a text block structure, including paragraphs, lists, and runs.
|
|
543
|
+
*
|
|
544
|
+
* @param parent The root container whose children should be traversed.
|
|
545
|
+
* @returns An IterableIterator yielding objects with the child component and its parent container.
|
|
546
|
+
* @beta
|
|
547
|
+
*/
|
|
548
|
+
export function* traverseTextBlockComponent(parent) {
|
|
549
|
+
for (const child of parent.children) {
|
|
550
|
+
yield { parent, child };
|
|
551
|
+
if (child.type === "list" || child.type === "paragraph") {
|
|
552
|
+
yield* traverseTextBlockComponent(child);
|
|
553
|
+
}
|
|
484
554
|
}
|
|
485
555
|
}
|
|
556
|
+
/**
|
|
557
|
+
* Returns the formatted marker text for a list item based on the marker type and item number.
|
|
558
|
+
* Supports ordered and unordered list markers, including alphabetic, Roman numeral, and numeric formats.
|
|
559
|
+
* @param marker The type of list marker to use.
|
|
560
|
+
* @param num The item number in the list.
|
|
561
|
+
* @returns The formatted marker string for the list item.
|
|
562
|
+
* @beta
|
|
563
|
+
*/
|
|
564
|
+
export function getMarkerText(marker, num) {
|
|
565
|
+
let markerString = "";
|
|
566
|
+
switch (marker.enumerator) {
|
|
567
|
+
case undefined:
|
|
568
|
+
case ListMarkerEnumerator.Number:
|
|
569
|
+
markerString = `${num}`;
|
|
570
|
+
break;
|
|
571
|
+
case ListMarkerEnumerator.Letter:
|
|
572
|
+
markerString = integerToAlpha(num);
|
|
573
|
+
break;
|
|
574
|
+
case ListMarkerEnumerator.RomanNumeral:
|
|
575
|
+
markerString = integerToRoman(num);
|
|
576
|
+
break;
|
|
577
|
+
default:
|
|
578
|
+
markerString = marker.enumerator;
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
if (marker.case) {
|
|
582
|
+
markerString = marker.case === "upper" ? markerString.toUpperCase() : markerString.toLowerCase();
|
|
583
|
+
}
|
|
584
|
+
const terminator = marker.terminator === "period" ? "." : marker.terminator === "parenthesis" ? ")" : "";
|
|
585
|
+
return `${markerString}${terminator}`;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Converts an integer to its Roman numeral representation.
|
|
589
|
+
* Supports numbers from 1 and above.
|
|
590
|
+
* @param num The integer to convert.
|
|
591
|
+
* @returns The Roman numeral string.
|
|
592
|
+
*/
|
|
593
|
+
function integerToRoman(num) {
|
|
594
|
+
const values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
|
|
595
|
+
const symbols = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
|
|
596
|
+
let roman = '';
|
|
597
|
+
for (let i = 0; i < values.length; i++) {
|
|
598
|
+
while (num >= values[i]) {
|
|
599
|
+
roman += symbols[i];
|
|
600
|
+
num -= values[i];
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return roman;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Converts an integer to its alphabetic representation (A-Z, AA-ZZ, etc.).
|
|
607
|
+
* Used for ordered list markers with alphabetic styles.
|
|
608
|
+
* @param num The integer to convert (1-based).
|
|
609
|
+
* @returns The alphabetic string for the given number.
|
|
610
|
+
*/
|
|
611
|
+
function integerToAlpha(num) {
|
|
612
|
+
const letterOffset = (num - 1) % 26;
|
|
613
|
+
const letter = String.fromCharCode(65 + letterOffset);
|
|
614
|
+
const depth = Math.ceil(num / 26);
|
|
615
|
+
return letter.repeat(depth);
|
|
616
|
+
}
|
|
486
617
|
//# sourceMappingURL=TextBlock.js.map
|