uicore-ts 1.1.211 → 1.1.212

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.
@@ -7,6 +7,8 @@ export interface TextMeasurementStyle {
7
7
  paddingRight: number;
8
8
  paddingTop: number;
9
9
  paddingBottom: number;
10
+ letterSpacing: number;
11
+ textTransform: string;
10
12
  }
11
13
  export declare class UITextMeasurement {
12
14
  private static canvas;
@@ -52,11 +54,12 @@ export declare class UITextMeasurement {
52
54
  /**
53
55
  * Parse line height from computed style
54
56
  */
57
+ private static applyTextTransform;
55
58
  private static parseLineHeight;
56
59
  /**
57
60
  * Measure text width using Canvas API
58
61
  */
59
- static measureTextWidth(text: string, font: string): number;
62
+ static measureTextWidth(text: string, font: string, letterSpacing?: number): number;
60
63
  /**
61
64
  * Split text into lines based on width constraint
62
65
  */
@@ -76,7 +79,11 @@ export declare class UITextMeasurement {
76
79
  */
77
80
  private static calculatePlainTextSize;
78
81
  /**
79
- * Clear all caches (call when fonts change or for cleanup)
82
+ * Clear all caches (call when fonts change or for cleanup).
83
+ * Also resets the canvas context so the next measureText call forces the
84
+ * browser to re-resolve the font string against the now-loaded FontFace set.
85
+ * Without this, ctx.font may silently fall back to the system font even
86
+ * though getComputedStyle already reports the correct custom font.
80
87
  */
81
88
  static clearCaches(): void;
82
89
  /**
@@ -102,6 +102,8 @@ class UITextMeasurement {
102
102
  measureEl.style.lineHeight = styles.lineHeight + "px";
103
103
  measureEl.style.whiteSpace = styles.whiteSpace;
104
104
  measureEl.style.padding = `${styles.paddingTop}px ${styles.paddingRight}px ${styles.paddingBottom}px ${styles.paddingLeft}px`;
105
+ measureEl.style.letterSpacing = styles.letterSpacing ? styles.letterSpacing + "px" : "";
106
+ measureEl.style.textTransform = styles.textTransform || "";
105
107
  if (constrainingWidth) {
106
108
  measureEl.style.width = constrainingWidth + "px";
107
109
  measureEl.style.maxWidth = constrainingWidth + "px";
@@ -152,11 +154,25 @@ class UITextMeasurement {
152
154
  paddingLeft: parseFloat(computed.paddingLeft) || 0,
153
155
  paddingRight: parseFloat(computed.paddingRight) || 0,
154
156
  paddingTop: parseFloat(computed.paddingTop) || 0,
155
- paddingBottom: parseFloat(computed.paddingBottom) || 0
157
+ paddingBottom: parseFloat(computed.paddingBottom) || 0,
158
+ letterSpacing: parseFloat(computed.letterSpacing) || 0,
159
+ textTransform: computed.textTransform || "none"
156
160
  };
157
161
  this.globalStyleCache.set(cacheKey, styles);
158
162
  return styles;
159
163
  }
164
+ static applyTextTransform(text, transform) {
165
+ switch (transform) {
166
+ case "uppercase":
167
+ return text.toUpperCase();
168
+ case "lowercase":
169
+ return text.toLowerCase();
170
+ case "capitalize":
171
+ return text.replace(/\b\w/g, (c) => c.toUpperCase());
172
+ default:
173
+ return text;
174
+ }
175
+ }
160
176
  static parseLineHeight(lineHeight, fontSize) {
161
177
  if (lineHeight === "normal") {
162
178
  return fontSize * 1.2;
@@ -170,10 +186,11 @@ class UITextMeasurement {
170
186
  }
171
187
  return fontSize * 1.2;
172
188
  }
173
- static measureTextWidth(text, font) {
189
+ static measureTextWidth(text, font, letterSpacing = 0) {
174
190
  const ctx = this.getContext();
175
191
  ctx.font = font;
176
- return ctx.measureText(text).width;
192
+ const baseWidth = ctx.measureText(text).width;
193
+ return baseWidth + letterSpacing * text.length;
177
194
  }
178
195
  static wrapText(text, maxWidth, font, whiteSpace) {
179
196
  if (whiteSpace === "nowrap" || whiteSpace === "pre") {
@@ -272,13 +289,14 @@ class UITextMeasurement {
272
289
  const availableWidth = constrainingWidth ? constrainingWidth - styles.paddingLeft - styles.paddingRight : Infinity;
273
290
  let width;
274
291
  let height;
292
+ const transformedText = this.applyTextTransform(text, styles.textTransform);
275
293
  if (styles.whiteSpace === "nowrap" || styles.whiteSpace === "pre" || !constrainingWidth) {
276
- width = this.measureTextWidth(text, styles.font) + styles.paddingLeft + styles.paddingRight;
294
+ width = this.measureTextWidth(transformedText, styles.font, styles.letterSpacing) + styles.paddingLeft + styles.paddingRight;
277
295
  height = styles.lineHeight + styles.paddingTop + styles.paddingBottom;
278
296
  } else {
279
- const lines = this.wrapText(text, availableWidth, styles.font, styles.whiteSpace);
297
+ const lines = this.wrapText(transformedText, availableWidth, styles.font, styles.whiteSpace);
280
298
  width = Math.max(
281
- ...lines.map((line) => this.measureTextWidth(line, styles.font))
299
+ ...lines.map((line) => this.measureTextWidth(line, styles.font, styles.letterSpacing))
282
300
  ) + styles.paddingLeft + styles.paddingRight;
283
301
  height = lines.length * styles.lineHeight + styles.paddingTop + styles.paddingBottom;
284
302
  }
@@ -287,6 +305,8 @@ class UITextMeasurement {
287
305
  static clearCaches() {
288
306
  this.globalStyleCache.clear();
289
307
  this.elementToCacheKey = /* @__PURE__ */ new WeakMap();
308
+ this.context = null;
309
+ this.canvas = null;
290
310
  }
291
311
  static invalidateElement(element) {
292
312
  const cacheKey = this.elementToCacheKey.get(element);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../scripts/UITextMeasurement.ts"],
4
- "sourcesContent": ["// UITextMeasurement.ts - Efficient text measurement without DOM reflows\n\nexport interface TextMeasurementStyle {\n font: string;\n fontSize: number;\n lineHeight: number;\n whiteSpace: string;\n paddingLeft: number;\n paddingRight: number;\n paddingTop: number;\n paddingBottom: number;\n}\n\nexport class UITextMeasurement {\n private static canvas: HTMLCanvasElement | null = null;\n private static context: CanvasRenderingContext2D | null = null;\n \n // Global cache for style objects using semantic cache key\n private static globalStyleCache = new Map<string, TextMeasurementStyle>();\n \n // Per-element cache to map element -> cache key\n private static elementToCacheKey = new WeakMap<HTMLElement, string>();\n \n // Temporary element for complex HTML measurements (reused to avoid allocations)\n private static measurementElement: HTMLDivElement | null = null;\n \n /**\n * Generate a cache key based only on styles that affect text measurement\n * Ignores position, color, transform, etc.\n */\n private static generateStyleCacheKey(computed: CSSStyleDeclaration): string {\n // Only include properties that affect text layout\n const relevantProps = [\n computed.fontFamily,\n computed.fontSize,\n computed.fontWeight,\n computed.fontStyle,\n computed.fontVariant,\n computed.lineHeight,\n computed.letterSpacing,\n computed.wordSpacing,\n computed.textTransform,\n computed.whiteSpace,\n computed.wordBreak,\n computed.wordWrap,\n computed.paddingLeft,\n computed.paddingRight,\n computed.paddingTop,\n computed.paddingBottom,\n computed.borderLeftWidth,\n computed.borderRightWidth,\n computed.borderTopWidth,\n computed.borderBottomWidth,\n computed.boxSizing\n ];\n \n // Create a hash-like key from relevant properties\n return relevantProps.join('|');\n }\n \n /**\n * Extract cache key from element's classList\n * Elements with same classes likely have same text measurement styles\n */\n private static getSemanticCacheKey(element: HTMLElement): string {\n // Check if we already computed a cache key for this element\n const existingKey = this.elementToCacheKey.get(element);\n if (existingKey) {\n return existingKey;\n }\n \n // Try to use class-based caching first (fastest)\n const classList = Array.from(element.classList).sort().join(' ');\n const tagName = element.tagName.toLowerCase();\n \n // Semantic key based on tag + classes\n const semanticKey = `${tagName}::${classList}`;\n \n // Check if we have styles for this semantic key\n if (this.globalStyleCache.has(semanticKey)) {\n this.elementToCacheKey.set(element, semanticKey);\n return semanticKey;\n }\n \n // If not cached, compute and use style-based key\n const computed = window.getComputedStyle(element);\n const styleCacheKey = this.generateStyleCacheKey(computed);\n \n // Use the style-based key\n this.elementToCacheKey.set(element, styleCacheKey);\n \n return styleCacheKey;\n }\n \n /**\n * Get or create the canvas context for text measurement\n */\n private static getContext(): CanvasRenderingContext2D {\n if (!this.context) {\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d')!;\n }\n return this.context;\n }\n \n /**\n * Detect if content is plain text or complex HTML\n */\n private static isPlainText(content: string): boolean {\n // Check for HTML tags (excluding simple formatting like <b>, <i>, <span>)\n const hasComplexHTML = /<(?!\\/?(b|i|em|strong|span|br)\\b)[^>]+>/i.test(content);\n return !hasComplexHTML;\n }\n \n /**\n * Check if content has only simple inline formatting\n */\n private static hasSimpleFormatting(content: string): boolean {\n // Only <b>, <i>, <strong>, <em>, <span> with inline styles\n const simpleTagPattern = /^[^<]*(?:<\\/?(?:b|i|em|strong|span)(?:\\s+style=\"[^\"]*\")?>[^<]*)*$/i;\n return simpleTagPattern.test(content);\n }\n \n /**\n * Get or create measurement element for complex HTML\n */\n private static getMeasurementElement(): HTMLDivElement {\n if (!this.measurementElement) {\n this.measurementElement = document.createElement('div');\n this.measurementElement.style.cssText = `\n position: absolute;\n visibility: hidden;\n pointer-events: none;\n top: -9999px;\n left: -9999px;\n width: auto;\n height: auto;\n `;\n }\n return this.measurementElement;\n }\n \n /**\n * Fast measurement using DOM (but optimized to minimize reflows)\n */\n private static measureWithDOM(\n element: HTMLElement,\n content: string,\n constrainingWidth?: number,\n constrainingHeight?: number,\n providedStyles?: TextMeasurementStyle\n ): { width: number; height: number } {\n const measureEl = this.getMeasurementElement();\n const styles = this.getElementStyles(element, providedStyles);\n \n // Copy relevant styles\n measureEl.style.font = styles.font;\n measureEl.style.lineHeight = styles.lineHeight + 'px';\n measureEl.style.whiteSpace = styles.whiteSpace;\n measureEl.style.padding = `${styles.paddingTop}px ${styles.paddingRight}px ${styles.paddingBottom}px ${styles.paddingLeft}px`;\n \n // Set constraints\n if (constrainingWidth) {\n measureEl.style.width = constrainingWidth + 'px';\n measureEl.style.maxWidth = constrainingWidth + 'px';\n } else {\n measureEl.style.width = 'auto';\n measureEl.style.maxWidth = 'none';\n }\n \n if (constrainingHeight) {\n measureEl.style.height = constrainingHeight + 'px';\n measureEl.style.maxHeight = constrainingHeight + 'px';\n } else {\n measureEl.style.height = 'auto';\n measureEl.style.maxHeight = 'none';\n }\n \n // Set content\n measureEl.innerHTML = content;\n \n // Add to DOM only if not already there\n if (!measureEl.parentElement) {\n document.body.appendChild(measureEl);\n }\n \n // Single reflow for both measurements\n const rect = measureEl.getBoundingClientRect();\n const result = {\n width: rect.width || measureEl.scrollWidth,\n height: rect.height || measureEl.scrollHeight\n };\n \n return result;\n }\n \n /**\n * Get or extract styles from element (with smart global caching)\n * Returns cached styles if available, otherwise computes once and caches globally\n */\n private static getElementStyles(element: HTMLElement, providedStyles?: TextMeasurementStyle): TextMeasurementStyle {\n // Use provided styles if available (avoids getComputedStyle entirely)\n if (providedStyles) {\n return providedStyles;\n }\n \n // Get semantic cache key\n const cacheKey = this.getSemanticCacheKey(element);\n \n // Check global cache\n const cached = this.globalStyleCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n \n // Compute once and cache globally (this is the only getComputedStyle call)\n const computed = window.getComputedStyle(element);\n const fontSize = parseFloat(computed.fontSize);\n \n const styles: TextMeasurementStyle = {\n font: [\n computed.fontStyle,\n computed.fontVariant,\n computed.fontWeight,\n computed.fontSize,\n computed.fontFamily\n ].join(' '),\n fontSize: fontSize,\n lineHeight: this.parseLineHeight(computed.lineHeight, fontSize),\n whiteSpace: computed.whiteSpace,\n paddingLeft: parseFloat(computed.paddingLeft) || 0,\n paddingRight: parseFloat(computed.paddingRight) || 0,\n paddingTop: parseFloat(computed.paddingTop) || 0,\n paddingBottom: parseFloat(computed.paddingBottom) || 0\n };\n \n this.globalStyleCache.set(cacheKey, styles);\n return styles;\n }\n \n /**\n * Parse line height from computed style\n */\n private static parseLineHeight(lineHeight: string, fontSize: number): number {\n if (lineHeight === 'normal') {\n return fontSize * 1.2;\n }\n \n if (lineHeight.endsWith('px')) {\n return parseFloat(lineHeight);\n }\n \n const numericLineHeight = parseFloat(lineHeight);\n if (!isNaN(numericLineHeight)) {\n return fontSize * numericLineHeight;\n }\n \n return fontSize * 1.2;\n }\n \n /**\n * Measure text width using Canvas API\n */\n static measureTextWidth(text: string, font: string): number {\n const ctx = this.getContext();\n ctx.font = font;\n return ctx.measureText(text).width;\n }\n \n /**\n * Split text into lines based on width constraint\n */\n private static wrapText(\n text: string,\n maxWidth: number,\n font: string,\n whiteSpace: string\n ): string[] {\n // No wrapping needed\n if (whiteSpace === 'nowrap' || whiteSpace === 'pre') {\n return [text];\n }\n \n const ctx = this.getContext();\n ctx.font = font;\n \n const lines: string[] = [];\n const paragraphs = text.split('\\n');\n \n for (const paragraph of paragraphs) {\n if (whiteSpace === 'pre-wrap') {\n // Preserve whitespace but wrap at maxWidth\n lines.push(...this.wrapPreservingWhitespace(paragraph, maxWidth, ctx));\n } else {\n // Normal wrapping (collapse whitespace)\n lines.push(...this.wrapNormal(paragraph, maxWidth, ctx));\n }\n }\n \n return lines;\n }\n \n private static wrapNormal(\n text: string,\n maxWidth: number,\n ctx: CanvasRenderingContext2D\n ): string[] {\n const words = text.split(/\\s+/).filter(w => w.length > 0);\n const lines: string[] = [];\n let currentLine = '';\n \n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word;\n const metrics = ctx.measureText(testLine);\n \n if (metrics.width > maxWidth && currentLine) {\n lines.push(currentLine);\n currentLine = word;\n } else {\n currentLine = testLine;\n }\n }\n \n if (currentLine) {\n lines.push(currentLine);\n }\n \n return lines.length > 0 ? lines : [''];\n }\n \n private static wrapPreservingWhitespace(\n text: string,\n maxWidth: number,\n ctx: CanvasRenderingContext2D\n ): string[] {\n const lines: string[] = [];\n let currentLine = '';\n \n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n const testLine = currentLine + char;\n const metrics = ctx.measureText(testLine);\n \n if (metrics.width > maxWidth && currentLine) {\n lines.push(currentLine);\n currentLine = char;\n } else {\n currentLine = testLine;\n }\n }\n \n if (currentLine) {\n lines.push(currentLine);\n }\n \n return lines.length > 0 ? lines : [''];\n }\n \n /**\n * Calculate intrinsic content size for text - SMART METHOD\n * Automatically chooses the most efficient measurement technique\n */\n static calculateTextSize(\n element: HTMLElement,\n content: string,\n constrainingWidth?: number,\n constrainingHeight?: number,\n providedStyles?: TextMeasurementStyle\n ): { width: number; height: number } {\n // Empty content\n if (!content || content.length === 0) {\n const styles = this.getElementStyles(element, providedStyles);\n return {\n width: styles.paddingLeft + styles.paddingRight,\n height: styles.paddingTop + styles.paddingBottom\n };\n }\n \n // Check complexity of content\n const isPlain = this.isPlainText(content);\n const hasSimple = this.hasSimpleFormatting(content);\n \n // Strategy 1: Pure canvas for plain text (fastest)\n if (isPlain) {\n return this.calculatePlainTextSize(\n element,\n content,\n constrainingWidth,\n constrainingHeight,\n providedStyles\n );\n }\n \n // Strategy 2: Optimized DOM for simple formatting (fast)\n if (hasSimple) {\n // For simple formatting, we can still use canvas but need to strip tags\n const plainText = content.replace(/<[^>]+>/g, '');\n return this.calculatePlainTextSize(\n element,\n plainText,\n constrainingWidth,\n constrainingHeight,\n providedStyles\n );\n }\n \n // Strategy 3: DOM measurement for complex HTML (slower but accurate)\n return this.measureWithDOM(\n element,\n content,\n constrainingWidth,\n constrainingHeight,\n providedStyles\n );\n }\n \n /**\n * Calculate size for plain text using canvas (no HTML)\n */\n private static calculatePlainTextSize(\n element: HTMLElement,\n text: string,\n constrainingWidth?: number,\n constrainingHeight?: number,\n providedStyles?: TextMeasurementStyle\n ): { width: number; height: number } {\n const styles = this.getElementStyles(element, providedStyles);\n \n // Adjust constraining width for padding\n const availableWidth = constrainingWidth\n ? constrainingWidth - styles.paddingLeft - styles.paddingRight\n : Infinity;\n \n // Calculate dimensions\n let width: number;\n let height: number;\n \n if (styles.whiteSpace === 'nowrap' || styles.whiteSpace === 'pre' || !constrainingWidth) {\n // Single line or no width constraint\n width = this.measureTextWidth(text, styles.font) + styles.paddingLeft + styles.paddingRight;\n height = styles.lineHeight + styles.paddingTop + styles.paddingBottom;\n } else {\n // Multi-line text\n const lines = this.wrapText(text, availableWidth, styles.font, styles.whiteSpace);\n \n // Find the widest line\n width = Math.max(\n ...lines.map(line => this.measureTextWidth(line, styles.font))\n ) + styles.paddingLeft + styles.paddingRight;\n \n height = (lines.length * styles.lineHeight) + styles.paddingTop + styles.paddingBottom;\n }\n \n return { width, height };\n }\n \n /**\n * Clear all caches (call when fonts change or for cleanup)\n */\n static clearCaches(): void {\n this.globalStyleCache.clear();\n this.elementToCacheKey = new WeakMap();\n }\n \n /**\n * Invalidate cached styles for a specific element\n */\n static invalidateElement(element: HTMLElement): void {\n const cacheKey = this.elementToCacheKey.get(element);\n if (cacheKey) {\n this.globalStyleCache.delete(cacheKey);\n this.elementToCacheKey.delete(element);\n }\n }\n \n /**\n * Invalidate cache for elements with specific class\n * Useful when you change a CSS class definition\n */\n static invalidateClass(className: string): void {\n // Clear all cache keys that contain this class\n for (const [key] of this.globalStyleCache.entries()) {\n if (key.includes(className)) {\n this.globalStyleCache.delete(key);\n }\n }\n }\n \n /**\n * Pre-warm the cache by measuring a representative element\n * Useful at app startup to avoid first-paint delays\n */\n static prewarmCache(elements: HTMLElement[]): void {\n elements.forEach(el => {\n this.getElementStyles(el);\n });\n }\n \n /**\n * Clean up measurement element (call on app cleanup)\n */\n static cleanup(): void {\n if (this.measurementElement && this.measurementElement.parentElement) {\n document.body.removeChild(this.measurementElement);\n }\n this.measurementElement = null;\n this.canvas = null;\n this.context = null;\n this.clearCaches();\n }\n}\n\n// Extension methods to add to UITextView\nexport interface UITextViewMeasurementMethods {\n intrinsicContentSizeEfficient(\n constrainingWidth?: number,\n constrainingHeight?: number\n ): { width: number; height: number };\n}\n\n// ==================== INTEGRATION CODE ====================\n// Add these methods to UITextView class:\n\n/*\n // In UITextView class:\n \n // Add this property to track content complexity and cached styles\n private _useFastMeasurement: boolean | undefined;\n private _cachedMeasurementStyles: TextMeasurementStyle | undefined;\n \n // Call this when styles change (fontSize, padding, etc.)\n private _invalidateMeasurementStyles(): void {\n this._cachedMeasurementStyles = undefined;\n UITextMeasurement.invalidateElement(this.viewHTMLElement);\n this._intrinsicSizesCache = {};\n }\n \n // Extract styles ONCE and cache them (avoids getComputedStyle)\n private _getMeasurementStyles(): TextMeasurementStyle {\n if (this._cachedMeasurementStyles) {\n return this._cachedMeasurementStyles;\n }\n \n // Only call getComputedStyle once and cache the result\n const computed = window.getComputedStyle(this.viewHTMLElement);\n const fontSize = parseFloat(computed.fontSize);\n \n this._cachedMeasurementStyles = {\n font: [\n computed.fontStyle,\n computed.fontVariant,\n computed.fontWeight,\n computed.fontSize,\n computed.fontFamily\n ].join(' '),\n fontSize: fontSize,\n lineHeight: this._parseLineHeight(computed.lineHeight, fontSize),\n whiteSpace: computed.whiteSpace,\n paddingLeft: parseFloat(computed.paddingLeft) || 0,\n paddingRight: parseFloat(computed.paddingRight) || 0,\n paddingTop: parseFloat(computed.paddingTop) || 0,\n paddingBottom: parseFloat(computed.paddingBottom) || 0\n };\n \n return this._cachedMeasurementStyles;\n }\n \n private _parseLineHeight(lineHeight: string, fontSize: number): number {\n if (lineHeight === 'normal') {\n return fontSize * 1.2;\n }\n if (lineHeight.endsWith('px')) {\n return parseFloat(lineHeight);\n }\n const numericLineHeight = parseFloat(lineHeight);\n if (!isNaN(numericLineHeight)) {\n return fontSize * numericLineHeight;\n }\n return fontSize * 1.2;\n }\n \n // Override the intrinsic size method\n override intrinsicContentSizeWithConstraints(\n constrainingHeight: number = 0,\n constrainingWidth: number = 0\n ): UIRectangle {\n const cacheKey = \"h_\" + constrainingHeight + \"__w_\" + constrainingWidth;\n const cachedResult = this._intrinsicSizesCache[cacheKey];\n if (cachedResult) {\n return cachedResult;\n }\n \n // Determine measurement strategy\n const shouldUseFastPath = this._useFastMeasurement ?? this._shouldUseFastMeasurement();\n \n let result: UIRectangle;\n \n if (shouldUseFastPath) {\n // Fast path: canvas-based measurement with pre-extracted styles\n const styles = this._getMeasurementStyles();\n const size = UITextMeasurement.calculateTextSize(\n this.viewHTMLElement,\n this.text || this.innerHTML,\n constrainingWidth || undefined,\n constrainingHeight || undefined,\n styles // Pass pre-computed styles to avoid getComputedStyle!\n );\n result = new UIRectangle(0, 0, size.height, size.width);\n } else {\n // Fallback: original DOM-based measurement for complex content\n result = super.intrinsicContentSizeWithConstraints(constrainingHeight, constrainingWidth);\n }\n \n this._intrinsicSizesCache[cacheKey] = result.copy();\n return result;\n }\n \n // Helper to determine if we can use fast measurement\n private _shouldUseFastMeasurement(): boolean {\n const content = this.text || this.innerHTML;\n \n // If using dynamic innerHTML with parameters, use DOM measurement\n if (this._innerHTMLKey || this._localizedTextObject) {\n return false;\n }\n \n // Check for notification badges\n if (this.notificationAmount > 0) {\n return false; // Has span with colored text\n }\n \n // Check content complexity\n const hasComplexHTML = /<(?!\\/?(b|i|em|strong|span|br)\\b)[^>]+>/i.test(content);\n \n return !hasComplexHTML;\n }\n \n // Optional: Allow manual override for specific instances\n setUseFastMeasurement(useFast: boolean): void {\n this._useFastMeasurement = useFast;\n this._intrinsicSizesCache = {};\n }\n \n // Optional: Force re-evaluation of measurement strategy\n invalidateMeasurementStrategy(): void {\n this._useFastMeasurement = undefined;\n this._invalidateMeasurementStyles();\n }\n \n // Update fontSize setter to invalidate cached styles\n override set fontSize(fontSize: number) {\n this.style.fontSize = \"\" + fontSize + \"pt\";\n this._intrinsicHeightCache = new UIObject() as any;\n this._intrinsicWidthCache = new UIObject() as any;\n this._invalidateMeasurementStyles(); // Invalidate when font changes\n }\n \n // Update the text setter to invalidate measurement strategy\n override set text(text: string) {\n this._text = text;\n \n var notificationText = \"\";\n \n if (this.notificationAmount) {\n notificationText = \"<span style=\\\"color: \" + UITextView.notificationTextColor.stringValue + \";\\\">\" +\n (\" (\" + this.notificationAmount + \")\").bold() + \"</span>\";\n }\n \n if (this.viewHTMLElement.innerHTML != this.textPrefix + text + this.textSuffix + notificationText) {\n this.viewHTMLElement.innerHTML = this.textPrefix + FIRST(text, \"\") + this.textSuffix + notificationText;\n }\n \n if (this.changesOften) {\n this._intrinsicHeightCache = new UIObject() as any;\n this._intrinsicWidthCache = new UIObject() as any;\n }\n \n // Invalidate measurement strategy when text changes significantly\n this._useFastMeasurement = undefined;\n this._intrinsicSizesCache = {};\n \n this.setNeedsLayout();\n }\n */\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// UITextMeasurement.ts - Efficient text measurement without DOM reflows\n\nexport interface TextMeasurementStyle {\n font: string;\n fontSize: number;\n lineHeight: number;\n whiteSpace: string;\n paddingLeft: number;\n paddingRight: number;\n paddingTop: number;\n paddingBottom: number;\n letterSpacing: number;\n textTransform: string;\n}\n\nexport class UITextMeasurement {\n private static canvas: HTMLCanvasElement | null = null;\n private static context: CanvasRenderingContext2D | null = null;\n \n // Global cache for style objects using semantic cache key\n private static globalStyleCache = new Map<string, TextMeasurementStyle>();\n \n // Per-element cache to map element -> cache key\n private static elementToCacheKey = new WeakMap<HTMLElement, string>();\n \n // Temporary element for complex HTML measurements (reused to avoid allocations)\n private static measurementElement: HTMLDivElement | null = null;\n \n /**\n * Generate a cache key based only on styles that affect text measurement\n * Ignores position, color, transform, etc.\n */\n private static generateStyleCacheKey(computed: CSSStyleDeclaration): string {\n // Only include properties that affect text layout\n const relevantProps = [\n computed.fontFamily,\n computed.fontSize,\n computed.fontWeight,\n computed.fontStyle,\n computed.fontVariant,\n computed.lineHeight,\n computed.letterSpacing,\n computed.wordSpacing,\n computed.textTransform,\n computed.whiteSpace,\n computed.wordBreak,\n computed.wordWrap,\n computed.paddingLeft,\n computed.paddingRight,\n computed.paddingTop,\n computed.paddingBottom,\n computed.borderLeftWidth,\n computed.borderRightWidth,\n computed.borderTopWidth,\n computed.borderBottomWidth,\n computed.boxSizing\n ];\n \n // Create a hash-like key from relevant properties\n return relevantProps.join('|');\n }\n \n /**\n * Extract cache key from element's classList\n * Elements with same classes likely have same text measurement styles\n */\n private static getSemanticCacheKey(element: HTMLElement): string {\n // Check if we already computed a cache key for this element\n const existingKey = this.elementToCacheKey.get(element);\n if (existingKey) {\n return existingKey;\n }\n \n // Try to use class-based caching first (fastest)\n const classList = Array.from(element.classList).sort().join(' ');\n const tagName = element.tagName.toLowerCase();\n \n // Semantic key based on tag + classes\n const semanticKey = `${tagName}::${classList}`;\n \n // Check if we have styles for this semantic key\n if (this.globalStyleCache.has(semanticKey)) {\n this.elementToCacheKey.set(element, semanticKey);\n return semanticKey;\n }\n \n // If not cached, compute and use style-based key\n const computed = window.getComputedStyle(element);\n const styleCacheKey = this.generateStyleCacheKey(computed);\n \n // Use the style-based key\n this.elementToCacheKey.set(element, styleCacheKey);\n \n return styleCacheKey;\n }\n \n /**\n * Get or create the canvas context for text measurement\n */\n private static getContext(): CanvasRenderingContext2D {\n if (!this.context) {\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d')!;\n }\n return this.context;\n }\n \n /**\n * Detect if content is plain text or complex HTML\n */\n private static isPlainText(content: string): boolean {\n // Check for HTML tags (excluding simple formatting like <b>, <i>, <span>)\n const hasComplexHTML = /<(?!\\/?(b|i|em|strong|span|br)\\b)[^>]+>/i.test(content);\n return !hasComplexHTML;\n }\n \n /**\n * Check if content has only simple inline formatting\n */\n private static hasSimpleFormatting(content: string): boolean {\n // Only <b>, <i>, <strong>, <em>, <span> with inline styles\n const simpleTagPattern = /^[^<]*(?:<\\/?(?:b|i|em|strong|span)(?:\\s+style=\"[^\"]*\")?>[^<]*)*$/i;\n return simpleTagPattern.test(content);\n }\n \n /**\n * Get or create measurement element for complex HTML\n */\n private static getMeasurementElement(): HTMLDivElement {\n if (!this.measurementElement) {\n this.measurementElement = document.createElement('div');\n this.measurementElement.style.cssText = `\n position: absolute;\n visibility: hidden;\n pointer-events: none;\n top: -9999px;\n left: -9999px;\n width: auto;\n height: auto;\n `;\n }\n return this.measurementElement;\n }\n \n /**\n * Fast measurement using DOM (but optimized to minimize reflows)\n */\n private static measureWithDOM(\n element: HTMLElement,\n content: string,\n constrainingWidth?: number,\n constrainingHeight?: number,\n providedStyles?: TextMeasurementStyle\n ): { width: number; height: number } {\n const measureEl = this.getMeasurementElement();\n const styles = this.getElementStyles(element, providedStyles);\n \n // Copy relevant styles\n measureEl.style.font = styles.font;\n measureEl.style.lineHeight = styles.lineHeight + 'px';\n measureEl.style.whiteSpace = styles.whiteSpace;\n measureEl.style.padding = `${styles.paddingTop}px ${styles.paddingRight}px ${styles.paddingBottom}px ${styles.paddingLeft}px`;\n measureEl.style.letterSpacing = styles.letterSpacing ? styles.letterSpacing + 'px' : '';\n measureEl.style.textTransform = styles.textTransform || '';\n \n // Set constraints\n if (constrainingWidth) {\n measureEl.style.width = constrainingWidth + 'px';\n measureEl.style.maxWidth = constrainingWidth + 'px';\n } else {\n measureEl.style.width = 'auto';\n measureEl.style.maxWidth = 'none';\n }\n \n if (constrainingHeight) {\n measureEl.style.height = constrainingHeight + 'px';\n measureEl.style.maxHeight = constrainingHeight + 'px';\n } else {\n measureEl.style.height = 'auto';\n measureEl.style.maxHeight = 'none';\n }\n \n // Set content\n measureEl.innerHTML = content;\n \n // Add to DOM only if not already there\n if (!measureEl.parentElement) {\n document.body.appendChild(measureEl);\n }\n \n // Single reflow for both measurements\n const rect = measureEl.getBoundingClientRect();\n const result = {\n width: rect.width || measureEl.scrollWidth,\n height: rect.height || measureEl.scrollHeight\n };\n \n return result;\n }\n \n /**\n * Get or extract styles from element (with smart global caching)\n * Returns cached styles if available, otherwise computes once and caches globally\n */\n private static getElementStyles(element: HTMLElement, providedStyles?: TextMeasurementStyle): TextMeasurementStyle {\n // Use provided styles if available (avoids getComputedStyle entirely)\n if (providedStyles) {\n return providedStyles;\n }\n \n // Get semantic cache key\n const cacheKey = this.getSemanticCacheKey(element);\n \n // Check global cache\n const cached = this.globalStyleCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n \n // Compute once and cache globally (this is the only getComputedStyle call)\n const computed = window.getComputedStyle(element);\n const fontSize = parseFloat(computed.fontSize);\n \n const styles: TextMeasurementStyle = {\n font: [\n computed.fontStyle,\n computed.fontVariant,\n computed.fontWeight,\n computed.fontSize,\n computed.fontFamily\n ].join(' '),\n fontSize: fontSize,\n lineHeight: this.parseLineHeight(computed.lineHeight, fontSize),\n whiteSpace: computed.whiteSpace,\n paddingLeft: parseFloat(computed.paddingLeft) || 0,\n paddingRight: parseFloat(computed.paddingRight) || 0,\n paddingTop: parseFloat(computed.paddingTop) || 0,\n paddingBottom: parseFloat(computed.paddingBottom) || 0,\n letterSpacing: parseFloat(computed.letterSpacing) || 0,\n textTransform: computed.textTransform || 'none'\n };\n \n this.globalStyleCache.set(cacheKey, styles);\n return styles;\n }\n \n /**\n * Parse line height from computed style\n */\n private static applyTextTransform(text: string, transform: string): string {\n switch (transform) {\n case 'uppercase': return text.toUpperCase();\n case 'lowercase': return text.toLowerCase();\n case 'capitalize': return text.replace(/\\b\\w/g, c => c.toUpperCase());\n default: return text;\n }\n }\n \n private static parseLineHeight(lineHeight: string, fontSize: number): number {\n if (lineHeight === 'normal') {\n return fontSize * 1.2;\n }\n \n if (lineHeight.endsWith('px')) {\n return parseFloat(lineHeight);\n }\n \n const numericLineHeight = parseFloat(lineHeight);\n if (!isNaN(numericLineHeight)) {\n return fontSize * numericLineHeight;\n }\n \n return fontSize * 1.2;\n }\n \n \n /**\n * Measure text width using Canvas API\n */\n static measureTextWidth(text: string, font: string, letterSpacing: number = 0): number {\n const ctx = this.getContext();\n ctx.font = font;\n const baseWidth = ctx.measureText(text).width;\n // Canvas measureText does not apply letter-spacing; add it manually.\n // letter-spacing applies between characters, so multiply by text length\n // (not text.length - 1) to match browser rendering which also adds it\n // after the last character in most implementations.\n return baseWidth + letterSpacing * text.length;\n }\n \n /**\n * Split text into lines based on width constraint\n */\n private static wrapText(\n text: string,\n maxWidth: number,\n font: string,\n whiteSpace: string\n ): string[] {\n // No wrapping needed\n if (whiteSpace === 'nowrap' || whiteSpace === 'pre') {\n return [text];\n }\n \n const ctx = this.getContext();\n ctx.font = font;\n \n const lines: string[] = [];\n const paragraphs = text.split('\\n');\n \n for (const paragraph of paragraphs) {\n if (whiteSpace === 'pre-wrap') {\n // Preserve whitespace but wrap at maxWidth\n lines.push(...this.wrapPreservingWhitespace(paragraph, maxWidth, ctx));\n } else {\n // Normal wrapping (collapse whitespace)\n lines.push(...this.wrapNormal(paragraph, maxWidth, ctx));\n }\n }\n \n return lines;\n }\n \n private static wrapNormal(\n text: string,\n maxWidth: number,\n ctx: CanvasRenderingContext2D\n ): string[] {\n const words = text.split(/\\s+/).filter(w => w.length > 0);\n const lines: string[] = [];\n let currentLine = '';\n \n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word;\n const metrics = ctx.measureText(testLine);\n \n if (metrics.width > maxWidth && currentLine) {\n lines.push(currentLine);\n currentLine = word;\n } else {\n currentLine = testLine;\n }\n }\n \n if (currentLine) {\n lines.push(currentLine);\n }\n \n return lines.length > 0 ? lines : [''];\n }\n \n private static wrapPreservingWhitespace(\n text: string,\n maxWidth: number,\n ctx: CanvasRenderingContext2D\n ): string[] {\n const lines: string[] = [];\n let currentLine = '';\n \n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n const testLine = currentLine + char;\n const metrics = ctx.measureText(testLine);\n \n if (metrics.width > maxWidth && currentLine) {\n lines.push(currentLine);\n currentLine = char;\n } else {\n currentLine = testLine;\n }\n }\n \n if (currentLine) {\n lines.push(currentLine);\n }\n \n return lines.length > 0 ? lines : [''];\n }\n \n /**\n * Calculate intrinsic content size for text - SMART METHOD\n * Automatically chooses the most efficient measurement technique\n */\n static calculateTextSize(\n element: HTMLElement,\n content: string,\n constrainingWidth?: number,\n constrainingHeight?: number,\n providedStyles?: TextMeasurementStyle\n ): { width: number; height: number } {\n // Empty content\n if (!content || content.length === 0) {\n const styles = this.getElementStyles(element, providedStyles);\n return {\n width: styles.paddingLeft + styles.paddingRight,\n height: styles.paddingTop + styles.paddingBottom\n };\n }\n \n // Check complexity of content\n const isPlain = this.isPlainText(content);\n const hasSimple = this.hasSimpleFormatting(content);\n \n // Strategy 1: Pure canvas for plain text (fastest)\n if (isPlain) {\n return this.calculatePlainTextSize(\n element,\n content,\n constrainingWidth,\n constrainingHeight,\n providedStyles\n );\n }\n \n // Strategy 2: Optimized DOM for simple formatting (fast)\n if (hasSimple) {\n // For simple formatting, we can still use canvas but need to strip tags\n const plainText = content.replace(/<[^>]+>/g, '');\n return this.calculatePlainTextSize(\n element,\n plainText,\n constrainingWidth,\n constrainingHeight,\n providedStyles\n );\n }\n \n // Strategy 3: DOM measurement for complex HTML (slower but accurate)\n return this.measureWithDOM(\n element,\n content,\n constrainingWidth,\n constrainingHeight,\n providedStyles\n );\n }\n \n /**\n * Calculate size for plain text using canvas (no HTML)\n */\n private static calculatePlainTextSize(\n element: HTMLElement,\n text: string,\n constrainingWidth?: number,\n constrainingHeight?: number,\n providedStyles?: TextMeasurementStyle\n ): { width: number; height: number } {\n const styles = this.getElementStyles(element, providedStyles);\n \n // Adjust constraining width for padding\n const availableWidth = constrainingWidth\n ? constrainingWidth - styles.paddingLeft - styles.paddingRight\n : Infinity;\n \n // Calculate dimensions\n let width: number;\n let height: number;\n \n // Apply text-transform before measuring, matching what the browser renders\n const transformedText = this.applyTextTransform(text, styles.textTransform);\n \n if (styles.whiteSpace === 'nowrap' || styles.whiteSpace === 'pre' || !constrainingWidth) {\n // Single line or no width constraint\n width = this.measureTextWidth(transformedText, styles.font, styles.letterSpacing) + styles.paddingLeft + styles.paddingRight;\n height = styles.lineHeight + styles.paddingTop + styles.paddingBottom;\n } else {\n // Multi-line text\n const lines = this.wrapText(transformedText, availableWidth, styles.font, styles.whiteSpace);\n \n // Find the widest line\n width = Math.max(\n ...lines.map(line => this.measureTextWidth(line, styles.font, styles.letterSpacing))\n ) + styles.paddingLeft + styles.paddingRight;\n \n height = (lines.length * styles.lineHeight) + styles.paddingTop + styles.paddingBottom;\n }\n \n return { width, height };\n }\n \n /**\n * Clear all caches (call when fonts change or for cleanup).\n * Also resets the canvas context so the next measureText call forces the\n * browser to re-resolve the font string against the now-loaded FontFace set.\n * Without this, ctx.font may silently fall back to the system font even\n * though getComputedStyle already reports the correct custom font.\n */\n static clearCaches(): void {\n this.globalStyleCache.clear();\n this.elementToCacheKey = new WeakMap();\n this.context = null;\n this.canvas = null;\n }\n \n /**\n * Invalidate cached styles for a specific element\n */\n static invalidateElement(element: HTMLElement): void {\n const cacheKey = this.elementToCacheKey.get(element);\n if (cacheKey) {\n this.globalStyleCache.delete(cacheKey);\n this.elementToCacheKey.delete(element);\n }\n }\n \n /**\n * Invalidate cache for elements with specific class\n * Useful when you change a CSS class definition\n */\n static invalidateClass(className: string): void {\n // Clear all cache keys that contain this class\n for (const [key] of this.globalStyleCache.entries()) {\n if (key.includes(className)) {\n this.globalStyleCache.delete(key);\n }\n }\n }\n \n /**\n * Pre-warm the cache by measuring a representative element\n * Useful at app startup to avoid first-paint delays\n */\n static prewarmCache(elements: HTMLElement[]): void {\n elements.forEach(el => {\n this.getElementStyles(el);\n });\n }\n \n /**\n * Clean up measurement element (call on app cleanup)\n */\n static cleanup(): void {\n if (this.measurementElement && this.measurementElement.parentElement) {\n document.body.removeChild(this.measurementElement);\n }\n this.measurementElement = null;\n this.canvas = null;\n this.context = null;\n this.clearCaches();\n }\n}\n\n// Extension methods to add to UITextView\nexport interface UITextViewMeasurementMethods {\n intrinsicContentSizeEfficient(\n constrainingWidth?: number,\n constrainingHeight?: number\n ): { width: number; height: number };\n}\n\n// ==================== INTEGRATION CODE ====================\n// Add these methods to UITextView class:\n\n/*\n // In UITextView class:\n \n // Add this property to track content complexity and cached styles\n private _useFastMeasurement: boolean | undefined;\n private _cachedMeasurementStyles: TextMeasurementStyle | undefined;\n \n // Call this when styles change (fontSize, padding, etc.)\n private _invalidateMeasurementStyles(): void {\n this._cachedMeasurementStyles = undefined;\n UITextMeasurement.invalidateElement(this.viewHTMLElement);\n this._intrinsicSizesCache = {};\n }\n \n // Extract styles ONCE and cache them (avoids getComputedStyle)\n private _getMeasurementStyles(): TextMeasurementStyle {\n if (this._cachedMeasurementStyles) {\n return this._cachedMeasurementStyles;\n }\n \n // Only call getComputedStyle once and cache the result\n const computed = window.getComputedStyle(this.viewHTMLElement);\n const fontSize = parseFloat(computed.fontSize);\n \n this._cachedMeasurementStyles = {\n font: [\n computed.fontStyle,\n computed.fontVariant,\n computed.fontWeight,\n computed.fontSize,\n computed.fontFamily\n ].join(' '),\n fontSize: fontSize,\n lineHeight: this._parseLineHeight(computed.lineHeight, fontSize),\n whiteSpace: computed.whiteSpace,\n paddingLeft: parseFloat(computed.paddingLeft) || 0,\n paddingRight: parseFloat(computed.paddingRight) || 0,\n paddingTop: parseFloat(computed.paddingTop) || 0,\n paddingBottom: parseFloat(computed.paddingBottom) || 0\n };\n \n return this._cachedMeasurementStyles;\n }\n \n private _parseLineHeight(lineHeight: string, fontSize: number): number {\n if (lineHeight === 'normal') {\n return fontSize * 1.2;\n }\n if (lineHeight.endsWith('px')) {\n return parseFloat(lineHeight);\n }\n const numericLineHeight = parseFloat(lineHeight);\n if (!isNaN(numericLineHeight)) {\n return fontSize * numericLineHeight;\n }\n return fontSize * 1.2;\n }\n \n // Override the intrinsic size method\n override intrinsicContentSizeWithConstraints(\n constrainingHeight: number = 0,\n constrainingWidth: number = 0\n ): UIRectangle {\n const cacheKey = \"h_\" + constrainingHeight + \"__w_\" + constrainingWidth;\n const cachedResult = this._intrinsicSizesCache[cacheKey];\n if (cachedResult) {\n return cachedResult;\n }\n \n // Determine measurement strategy\n const shouldUseFastPath = this._useFastMeasurement ?? this._shouldUseFastMeasurement();\n \n let result: UIRectangle;\n \n if (shouldUseFastPath) {\n // Fast path: canvas-based measurement with pre-extracted styles\n const styles = this._getMeasurementStyles();\n const size = UITextMeasurement.calculateTextSize(\n this.viewHTMLElement,\n this.text || this.innerHTML,\n constrainingWidth || undefined,\n constrainingHeight || undefined,\n styles // Pass pre-computed styles to avoid getComputedStyle!\n );\n result = new UIRectangle(0, 0, size.height, size.width);\n } else {\n // Fallback: original DOM-based measurement for complex content\n result = super.intrinsicContentSizeWithConstraints(constrainingHeight, constrainingWidth);\n }\n \n this._intrinsicSizesCache[cacheKey] = result.copy();\n return result;\n }\n \n // Helper to determine if we can use fast measurement\n private _shouldUseFastMeasurement(): boolean {\n const content = this.text || this.innerHTML;\n \n // If using dynamic innerHTML with parameters, use DOM measurement\n if (this._innerHTMLKey || this._localizedTextObject) {\n return false;\n }\n \n // Check for notification badges\n if (this.notificationAmount > 0) {\n return false; // Has span with colored text\n }\n \n // Check content complexity\n const hasComplexHTML = /<(?!\\/?(b|i|em|strong|span|br)\\b)[^>]+>/i.test(content);\n \n return !hasComplexHTML;\n }\n \n // Optional: Allow manual override for specific instances\n setUseFastMeasurement(useFast: boolean): void {\n this._useFastMeasurement = useFast;\n this._intrinsicSizesCache = {};\n }\n \n // Optional: Force re-evaluation of measurement strategy\n invalidateMeasurementStrategy(): void {\n this._useFastMeasurement = undefined;\n this._invalidateMeasurementStyles();\n }\n \n // Update fontSize setter to invalidate cached styles\n override set fontSize(fontSize: number) {\n this.style.fontSize = \"\" + fontSize + \"pt\";\n this._intrinsicHeightCache = new UIObject() as any;\n this._intrinsicWidthCache = new UIObject() as any;\n this._invalidateMeasurementStyles(); // Invalidate when font changes\n }\n \n // Update the text setter to invalidate measurement strategy\n override set text(text: string) {\n this._text = text;\n \n var notificationText = \"\";\n \n if (this.notificationAmount) {\n notificationText = \"<span style=\\\"color: \" + UITextView.notificationTextColor.stringValue + \";\\\">\" +\n (\" (\" + this.notificationAmount + \")\").bold() + \"</span>\";\n }\n \n if (this.viewHTMLElement.innerHTML != this.textPrefix + text + this.textSuffix + notificationText) {\n this.viewHTMLElement.innerHTML = this.textPrefix + FIRST(text, \"\") + this.textSuffix + notificationText;\n }\n \n if (this.changesOften) {\n this._intrinsicHeightCache = new UIObject() as any;\n this._intrinsicWidthCache = new UIObject() as any;\n }\n \n // Invalidate measurement strategy when text changes significantly\n this._useFastMeasurement = undefined;\n this._intrinsicSizesCache = {};\n \n this.setNeedsLayout();\n }\n */\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -57,6 +57,7 @@ export declare class UITextView extends UIView {
57
57
  set styleClasses(styleClasses: string[]);
58
58
  didReceiveBroadcastEvent(event: UIViewBroadcastEvent): void;
59
59
  willMoveToSuperview(superview: UIView): void;
60
+ documentFontsDidLoad(): void;
60
61
  layoutSubviews(): void;
61
62
  private _invalidateMeasurementStyles;
62
63
  private _getMeasurementStyles;
@@ -100,6 +100,15 @@ const _UITextView = class extends import_UIView.UIView {
100
100
  willMoveToSuperview(superview) {
101
101
  super.willMoveToSuperview(superview);
102
102
  }
103
+ documentFontsDidLoad() {
104
+ super.documentFontsDidLoad();
105
+ this._invalidateFontCache();
106
+ this.invalidateMeasurementStrategy();
107
+ this._intrinsicHeightCache = new import_UIObject.UIObject();
108
+ this._intrinsicWidthCache = new import_UIObject.UIObject();
109
+ _UITextView._intrinsicHeightCache = new import_UIObject.UIObject();
110
+ _UITextView._intrinsicWidthCache = new import_UIObject.UIObject();
111
+ }
103
112
  layoutSubviews() {
104
113
  super.layoutSubviews();
105
114
  if (this._automaticFontSizeSelection) {
@@ -133,6 +142,7 @@ const _UITextView = class extends import_UIView.UIView {
133
142
  const computed = window.getComputedStyle(this.textElementView.viewHTMLElement);
134
143
  const fontSizeStr = computed.fontSize;
135
144
  const fontSize = parseFloat(fontSizeStr);
145
+ console.log(computed.letterSpacing);
136
146
  if (!fontSize || isNaN(fontSize)) {
137
147
  return null;
138
148
  }
@@ -155,7 +165,9 @@ const _UITextView = class extends import_UIView.UIView {
155
165
  paddingLeft: parseFloat(computed.paddingLeft) || 0,
156
166
  paddingRight: parseFloat(computed.paddingRight) || 0,
157
167
  paddingTop: parseFloat(computed.paddingTop) || 0,
158
- paddingBottom: parseFloat(computed.paddingBottom) || 0
168
+ paddingBottom: parseFloat(computed.paddingBottom) || 0,
169
+ letterSpacing: parseFloat(computed.letterSpacing) || 0,
170
+ textTransform: computed.textTransform || "none"
159
171
  };
160
172
  return this._cachedMeasurementStyles;
161
173
  }
@@ -181,7 +193,14 @@ const _UITextView = class extends import_UIView.UIView {
181
193
  return false;
182
194
  }
183
195
  const hasComplexHTML = /<(?!\/?(b|i|em|strong|span|br)\b)[^>]+>/i.test(content);
184
- return !hasComplexHTML;
196
+ if (hasComplexHTML) {
197
+ return false;
198
+ }
199
+ const styles = this._getMeasurementStyles();
200
+ if (styles && !document.fonts.check(styles.font)) {
201
+ return false;
202
+ }
203
+ return true;
185
204
  }
186
205
  setUseFastMeasurement(useFast) {
187
206
  this._useFastMeasurement = useFast;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../scripts/UITextView.ts"],
4
- "sourcesContent": ["import { UIColor } from \"./UIColor\"\nimport { UILocalizedTextObject } from \"./UIInterfaces\"\nimport { EXTEND, FIRST, IS_LIKE_NULL, nil, NO, UIObject, ValueOf, YES } from \"./UIObject\"\nimport { UIRectangle } from \"./UIRectangle\"\nimport { TextMeasurementStyle, UITextMeasurement } from \"./UITextMeasurement\"\nimport { UIView, UIViewBroadcastEvent } from \"./UIView\"\n\n\nexport class UITextView extends UIView {\n \n //#region Static Properties\n \n static defaultTextColor = UIColor.blackColor\n static notificationTextColor = UIColor.redColor\n \n // Global caches for all UILabels\n static _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n static _intrinsicWidthCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n \n static _ptToPx: number\n static _pxToPt: number\n \n static type = {\n \"paragraph\": \"p\",\n \"header1\": \"h1\",\n \"header2\": \"h2\",\n \"header3\": \"h3\",\n \"header4\": \"h4\",\n \"header5\": \"h5\",\n \"header6\": \"h6\",\n \"textArea\": \"textarea\",\n \"textField\": \"input\",\n \"span\": \"span\",\n \"label\": \"label\"\n } as const\n \n static textAlignment = {\n \"left\": \"left\",\n \"center\": \"center\",\n \"right\": \"right\",\n \"justify\": \"justify\"\n } as const\n \n //#endregion\n \n //#region Constructor\n \n \n constructor(\n elementID?: string,\n textViewType: string | ValueOf<typeof UITextView.type> = UITextView.type.paragraph,\n viewHTMLElement = null\n ) {\n \n // Create inner text element as a UIView\n const innerElementID = elementID ? `${elementID}_textElement` : undefined\n const _textElementView = new UIView(innerElementID, null, textViewType)\n \n // Create outer container (wrapper) - this is the main viewHTMLElement\n super(elementID, viewHTMLElement, \"span\", { _textElementView })\n \n // Configure outer container for vertical centering using direct property access\n \n this.configureWithObject({\n // @ts-ignore\n viewHTMLElement: {\n style: {\n display: \"flex\",\n alignItems: \"center\", // Vertical centering\n overflow: \"hidden\"\n }\n }\n })\n \n this.text = \"\"\n \n this._textElementView = _textElementView\n \n // Configure inner text element for ellipsis and positioning\n this._textElementView.configureWithObject({\n style: {\n position: \"relative\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n width: \"100%\",\n margin: \"0\",\n padding: \"0\"\n },\n // Forward control events from text element to the container\n sendControlEventForKey: EXTEND(this.sendControlEventForKey.bind(this))\n })\n \n // Add text element as a subview\n this.addSubview(this._textElementView)\n \n \n this.isSingleLine = YES\n \n this.textColor = this.textColor\n \n this.userInteractionEnabled = YES\n \n if (textViewType == UITextView.type.textArea) {\n this.pausesPointerEvents = YES\n this.addTargetForControlEvent(\n UIView.controlEvent.PointerUpInside,\n (sender, event) => sender.focus()\n )\n }\n }\n \n //#endregion\n \n //#region Text Element View Property\n \n private _textElementView: UIView\n \n /**\n * The inner text element that holds the actual text content\n */\n get textElementView(): UIView {\n return this._textElementView\n }\n \n /**\n * Override style to apply to the text element instead of the container\n */\n // override get style() {\n // return this._textElementView.style\n // }\n //\n // /**\n // * Override computedStyle to get computed styles from the text element\n // */\n // override get computedStyle() {\n // return this._textElementView.computedStyle\n // }\n \n /**\n * Access the outer container's style (for positioning, layout, etc.)\n */\n get containerStyle() {\n return this.viewHTMLElement.style\n }\n \n /**\n * Override styleClasses to apply to the text element\n */\n override get styleClasses() {\n return this._textElementView.styleClasses\n }\n \n override set styleClasses(styleClasses: string[]) {\n this._textElementView.styleClasses = styleClasses\n }\n \n //#endregion\n \n //#region Lifecycle Methods\n \n override didReceiveBroadcastEvent(event: UIViewBroadcastEvent) {\n super.didReceiveBroadcastEvent(event)\n }\n \n override willMoveToSuperview(superview: UIView) {\n super.willMoveToSuperview(superview)\n }\n \n override layoutSubviews() {\n super.layoutSubviews()\n \n if (this._automaticFontSizeSelection) {\n this.fontSize = UITextView.automaticallyCalculatedFontSize(\n new UIRectangle(\n 0,\n 0,\n this.textElementView.viewHTMLElement.offsetHeight,\n this.textElementView.viewHTMLElement.offsetWidth\n ),\n this.intrinsicContentSize(),\n this.fontSize,\n this._minFontSize,\n this._maxFontSize\n )\n }\n }\n \n //#endregion\n \n //#region Measurement & Sizing - Private Methods\n \n private _invalidateMeasurementStyles(): void {\n this._cachedMeasurementStyles = undefined\n UITextMeasurement.invalidateElement(this.textElementView.viewHTMLElement)\n this._intrinsicSizesCache = {}\n }\n \n private _getMeasurementStyles(): TextMeasurementStyle | null {\n if (this._cachedMeasurementStyles) {\n return this._cachedMeasurementStyles\n }\n \n // Ensure element is in document\n if (!this.textElementView.viewHTMLElement.isConnected) {\n return null\n }\n \n // Force a layout flush ONCE to ensure computed styles are available\n // This is only paid once per style change, then we use cached values\n this.textElementView.viewHTMLElement.offsetHeight\n \n const computed = window.getComputedStyle(this.textElementView.viewHTMLElement)\n const fontSizeStr = computed.fontSize\n const fontSize = parseFloat(fontSizeStr)\n \n if (!fontSize || isNaN(fontSize)) {\n return null\n }\n \n const lineHeight = this._parseLineHeight(computed.lineHeight, fontSize)\n \n if (isNaN(lineHeight)) {\n return null\n }\n \n const font = [\n computed.fontStyle || \"normal\",\n computed.fontVariant || \"normal\",\n computed.fontWeight || \"normal\",\n fontSize + \"px\",\n computed.fontFamily || \"sans-serif\"\n ].join(\" \")\n \n this._cachedMeasurementStyles = {\n font: font,\n fontSize: fontSize,\n lineHeight: lineHeight,\n whiteSpace: computed.whiteSpace || \"normal\",\n paddingLeft: parseFloat(computed.paddingLeft) || 0,\n paddingRight: parseFloat(computed.paddingRight) || 0,\n paddingTop: parseFloat(computed.paddingTop) || 0,\n paddingBottom: parseFloat(computed.paddingBottom) || 0\n }\n \n return this._cachedMeasurementStyles\n }\n \n private _parseLineHeight(lineHeight: string, fontSize: number): number {\n if (lineHeight === \"normal\") {\n return fontSize * 1.2\n }\n if (lineHeight.endsWith(\"px\")) {\n return parseFloat(lineHeight)\n }\n const numericLineHeight = parseFloat(lineHeight)\n if (!isNaN(numericLineHeight)) {\n return fontSize * numericLineHeight\n }\n return fontSize * 1.2\n }\n \n private _shouldUseFastMeasurement(): boolean {\n const content = this.text || this.textElementView.innerHTML\n \n if (this._innerHTMLKey || this._localizedTextObject) {\n return false\n }\n \n if (this.notificationAmount > 0) {\n return false\n }\n \n const hasComplexHTML = /<(?!\\/?(b|i|em|strong|span|br)\\b)[^>]+>/i.test(content)\n \n return !hasComplexHTML\n }\n \n //#endregion\n \n //#region Measurement & Sizing - Public Methods\n \n setUseFastMeasurement(useFast: boolean): void {\n this._useFastMeasurement = useFast\n this._intrinsicSizesCache = {}\n }\n \n invalidateMeasurementStrategy(): void {\n this._useFastMeasurement = undefined\n this._invalidateMeasurementStyles()\n }\n \n //#endregion\n \n //#region Getters & Setters - Text Alignment\n \n get textAlignment() {\n return this._textElementView.style.textAlign as ValueOf<typeof UITextView.textAlignment>\n }\n \n set textAlignment(textAlignment: ValueOf<typeof UITextView.textAlignment>) {\n this._textAlignment = textAlignment\n this._textElementView.style.textAlign = textAlignment\n }\n \n //#endregion\n \n //#region Getters & Setters - Text Color\n \n get textColor() {\n return this._textColor\n }\n \n set textColor(color: UIColor) {\n this._textColor = color || UITextView.defaultTextColor\n this._textElementView.style.color = this._textColor.stringValue\n }\n \n //#endregion\n \n //#region Getters & Setters - Single Line\n \n get isSingleLine() {\n return this._isSingleLine\n }\n \n set isSingleLine(isSingleLine: boolean) {\n this._isSingleLine = isSingleLine\n \n this._intrinsicHeightCache = new UIObject() as any\n this._intrinsicWidthCache = new UIObject() as any\n \n if (isSingleLine) {\n // Single line: use nowrap with ellipsis\n this._textElementView.style.whiteSpace = \"nowrap\"\n this._textElementView.style.textOverflow = \"ellipsis\"\n this._textElementView.style.display = \"\"\n this._textElementView.style.webkitLineClamp = \"\"\n this._textElementView.style.webkitBoxOrient = \"\"\n return\n }\n \n // Multiline: allow wrapping, but still show ellipsis if content overflows the container\n // This uses the -webkit-line-clamp approach which works for multiline ellipsis\n this._textElementView.style.whiteSpace = \"normal\"\n this._textElementView.style.textOverflow = \"ellipsis\"\n this._textElementView.style.display = \"-webkit-box\"\n this._textElementView.style.webkitBoxOrient = \"vertical\"\n // Don't set line-clamp to a specific number - let it fill available space\n // The overflow: hidden from the constructor will clip content that exceeds the height\n this.invalidateMeasurementStrategy()\n }\n \n //#endregion\n \n //#region Getters & Setters - Notification Amount\n \n get notificationAmount() {\n return this._notificationAmount\n }\n \n set notificationAmount(notificationAmount: number) {\n if (this._notificationAmount == notificationAmount) {\n return\n }\n \n this._notificationAmount = notificationAmount\n this.text = this.text\n this.setNeedsLayoutUpToRootView()\n this.notificationAmountDidChange(notificationAmount)\n }\n \n notificationAmountDidChange(notificationAmount: number) {\n }\n \n //#endregion\n \n //#region Getters & Setters - Text Content\n \n get text() {\n return (this._text || this.textElementView.viewHTMLElement.innerHTML)\n }\n \n set text(text) {\n this._text = text\n var notificationText = \"\"\n if (this.notificationAmount) {\n notificationText = \"<span style=\\\"color: \" + UITextView.notificationTextColor.stringValue + \";\\\">\" +\n (\" (\" + this.notificationAmount + \")\").bold() + \"</span>\"\n }\n \n if (this.textElementView.viewHTMLElement.innerHTML != this.textPrefix + text + this.textSuffix + notificationText) {\n this.textElementView.viewHTMLElement.innerHTML = this.textPrefix + FIRST(\n text, \"\") + this.textSuffix + notificationText\n }\n \n if (this.changesOften) {\n this._intrinsicHeightCache = new UIObject() as any\n this._intrinsicWidthCache = new UIObject() as any\n }\n \n this._useFastMeasurement = undefined\n this._intrinsicSizesCache = {}\n this.invalidateMeasurementStrategy()\n this._invalidateMeasurementStyles()\n this.clearIntrinsicSizeCache()\n \n this.setNeedsLayout()\n }\n \n override set innerHTML(innerHTML: string) {\n this.text = innerHTML\n this.invalidateMeasurementStrategy()\n }\n \n override get innerHTML() {\n return this.viewHTMLElement.innerHTML\n }\n \n setText(key: string, defaultString: string, parameters?: { [x: string]: string | UILocalizedTextObject }) {\n this.textElementView.setInnerHTML(key, defaultString, parameters)\n this.invalidateMeasurementStrategy()\n }\n \n //#endregion\n \n //#region Getters & Setters - Font Size\n \n get fontSize() {\n const style = this._textElementView.style.fontSize || window.getComputedStyle(this._textElementView.viewHTMLElement, null).fontSize\n const result = (parseFloat(style) * UITextView._pxToPt)\n return result\n }\n \n set fontSize(fontSize: number) {\n if (fontSize != this.fontSize) {\n this._textElementView.style.fontSize = \"\" + fontSize + \"pt\"\n \n this._intrinsicHeightCache = new UIObject() as any\n this._intrinsicWidthCache = new UIObject() as any\n \n this._invalidateFontCache()\n this._invalidateMeasurementStyles()\n this.clearIntrinsicSizeCache()\n }\n }\n \n useAutomaticFontSize(minFontSize: number = nil, maxFontSize: number = nil) {\n this._automaticFontSizeSelection = YES\n this._minFontSize = minFontSize\n this._maxFontSize = maxFontSize\n this.setNeedsLayout()\n }\n \n //#endregion\n \n //#region Font Caching - Private Methods\n \n /**\n * Get a stable cache key for the font without triggering reflow.\n * Only computes font on first access or when font properties change.\n */\n private _getFontCacheKey(): string {\n // Check if font-related properties have changed\n const currentTriggers = {\n fontSize: this._textElementView.style.fontSize || \"\",\n fontFamily: this._textElementView.style.fontFamily || \"\",\n fontWeight: this._textElementView.style.fontWeight || \"\",\n fontStyle: this._textElementView.style.fontStyle || \"\",\n styleClasses: this.styleClasses.join(\",\")\n }\n \n const hasChanged =\n currentTriggers.fontSize !== this._fontInvalidationTriggers.fontSize ||\n currentTriggers.fontFamily !== this._fontInvalidationTriggers.fontFamily ||\n currentTriggers.fontWeight !== this._fontInvalidationTriggers.fontWeight ||\n currentTriggers.fontStyle !== this._fontInvalidationTriggers.fontStyle ||\n currentTriggers.styleClasses !== this._fontInvalidationTriggers.styleClasses\n \n if (!this._cachedFontKey || hasChanged) {\n // Only access computedStyle when we know something changed\n const computed = this._textElementView.computedStyle\n this._cachedFontKey = [\n computed.fontStyle,\n computed.fontVariant,\n computed.fontWeight,\n computed.fontSize,\n computed.fontFamily\n ].join(\"_\").replace(/[.\\s]/g, \"_\")\n \n this._fontInvalidationTriggers = currentTriggers\n }\n \n return this._cachedFontKey\n }\n \n /**\n * Invalidate font cache when font properties change\n */\n private _invalidateFontCache(): void {\n this._cachedFontKey = undefined\n }\n \n //#endregion\n \n //#region Static Methods\n \n static _determinePXAndPTRatios() {\n if (UITextView._ptToPx) {\n return\n }\n \n const o = document.createElement(\"div\")\n o.style.width = \"1000pt\"\n document.body.appendChild(o)\n UITextView._ptToPx = o.clientWidth / 1000\n document.body.removeChild(o)\n UITextView._pxToPt = 1 / UITextView._ptToPx\n }\n \n static automaticallyCalculatedFontSize(\n bounds: UIRectangle,\n currentSize: UIRectangle,\n currentFontSize: number,\n minFontSize?: number,\n maxFontSize?: number\n ) {\n minFontSize = FIRST(minFontSize, 1)\n maxFontSize = FIRST(maxFontSize, 100000000000)\n \n const heightMultiplier = bounds.height / (currentSize.height + 1)\n const widthMultiplier = bounds.width / (currentSize.width + 1)\n \n var multiplier = heightMultiplier\n if (heightMultiplier > widthMultiplier) {\n multiplier = widthMultiplier\n }\n \n const maxFittingFontSize = currentFontSize * multiplier\n \n if (maxFittingFontSize > maxFontSize) {\n return maxFontSize\n }\n \n if (minFontSize > maxFittingFontSize) {\n return minFontSize\n }\n \n return maxFittingFontSize\n }\n \n //#endregion\n \n //#region Instance Properties - Text Content\n \n _text?: string\n textPrefix = \"\"\n textSuffix = \"\"\n _notificationAmount = 0\n \n //#endregion\n \n //#region Instance Properties - Styling\n \n _textColor: UIColor = UITextView.defaultTextColor\n _textAlignment?: ValueOf<typeof UITextView.textAlignment>\n _isSingleLine = YES\n \n //#endregion\n \n //#region Instance Properties - Font & Sizing\n \n _minFontSize?: number\n _maxFontSize?: number\n _automaticFontSizeSelection = NO\n \n // Cache for the computed font string\n private _cachedFontKey?: string\n private _fontInvalidationTriggers = {\n fontSize: \"\",\n fontFamily: \"\",\n fontWeight: \"\",\n fontStyle: \"\",\n styleClasses: \"\"\n }\n \n //#endregion\n \n //#region Instance Properties - Caching & Performance\n \n changesOften = NO\n \n // Local cache for this instance if the label changes often\n _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n _intrinsicWidthCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n \n private _useFastMeasurement: boolean | undefined\n private _cachedMeasurementStyles: TextMeasurementStyle | undefined | null\n \n override usesVirtualLayoutingForIntrinsicSizing = NO\n \n //#endregion\n \n // Override addStyleClass to invalidate font cache\n override addStyleClass(styleClass: string) {\n super.addStyleClass(styleClass)\n this._invalidateFontCache()\n }\n \n // Override removeStyleClass to invalidate font cache\n override removeStyleClass(styleClass: string) {\n super.removeStyleClass(styleClass)\n this._invalidateFontCache()\n }\n \n // Override focus to focus the text element\n override focus() {\n this._textElementView.focus()\n }\n \n // Override blur to blur the text element\n override blur() {\n this._textElementView.blur()\n }\n \n override intrinsicContentHeight(constrainingWidth = 0) {\n \n const keyPath = ((this.textElementView.viewHTMLElement.innerHTML || this.text) + \"_csf_\" + this._getFontCacheKey()) + \".\" +\n (\"\" + constrainingWidth).replace(new RegExp(\"\\\\.\", \"g\"), \"_\")\n \n let cacheObject = UITextView._intrinsicHeightCache\n \n if (this.changesOften) {\n cacheObject = this._intrinsicHeightCache\n }\n \n var result = cacheObject.valueForKeyPath(keyPath)\n \n if (IS_LIKE_NULL(result)) {\n // Determine if we should use fast measurement\n const shouldUseFastPath = this._useFastMeasurement ?? this._shouldUseFastMeasurement()\n \n if (shouldUseFastPath) {\n // Fast path: use UITextMeasurement with pre-extracted styles\n const styles = this._getMeasurementStyles()\n \n // If styles are invalid (element not properly initialized), fall back to DOM\n if (styles) {\n const size = UITextMeasurement.calculateTextSize(\n this.textElementView.viewHTMLElement,\n this.text || this.textElementView.innerHTML,\n constrainingWidth || undefined,\n undefined,\n styles\n )\n result = size.height\n }\n else {\n // Styles not ready, use DOM measurement\n result = super.intrinsicContentHeight(constrainingWidth)\n }\n }\n else {\n // Fallback: DOM-based measurement for complex content\n result = super.intrinsicContentHeight(constrainingWidth)\n }\n \n cacheObject.setValueForKeyPath(keyPath, result)\n }\n \n if (isNaN(result) || (!result && !this.text)) {\n result = super.intrinsicContentHeight(constrainingWidth)\n cacheObject.setValueForKeyPath(keyPath, result)\n }\n \n return result\n }\n \n override intrinsicContentWidth(constrainingHeight = 0) {\n \n const keyPath = ((this.textElementView.viewHTMLElement.innerHTML || this.text) + \"_csf_\" + this._getFontCacheKey()) + \".\" +\n (\"\" + constrainingHeight).replace(new RegExp(\"\\\\.\", \"g\"), \"_\")\n \n let cacheObject = UITextView._intrinsicWidthCache\n \n if (this.changesOften) {\n cacheObject = this._intrinsicWidthCache\n }\n \n var result = cacheObject.valueForKeyPath(keyPath)\n \n if (IS_LIKE_NULL(result)) {\n // Determine if we should use fast measurement\n const shouldUseFastPath = this._useFastMeasurement ?? this._shouldUseFastMeasurement()\n \n if (shouldUseFastPath) {\n // Fast path: use UITextMeasurement with pre-extracted styles\n const styles = this._getMeasurementStyles()\n \n // If styles are invalid (element not properly initialized), fall back to DOM\n if (styles) {\n const size = UITextMeasurement.calculateTextSize(\n this.textElementView.viewHTMLElement,\n this.text || this.textElementView.innerHTML,\n undefined,\n constrainingHeight || undefined,\n styles\n )\n result = size.width\n }\n else {\n // Styles not ready, use DOM measurement\n result = super.intrinsicContentWidth(constrainingHeight)\n }\n }\n else {\n // Fallback: DOM-based measurement for complex content\n result = super.intrinsicContentWidth(constrainingHeight)\n }\n \n cacheObject.setValueForKeyPath(keyPath, result)\n }\n \n return result\n }\n \n \n override intrinsicContentSizeWithConstraints(constrainingHeight: number = 0, constrainingWidth: number = 0) {\n \n const cacheKey = this._getIntrinsicSizeCacheKey(constrainingHeight, constrainingWidth)\n const cachedResult = this._getCachedIntrinsicSize(cacheKey)\n if (cachedResult) {\n return cachedResult\n }\n \n // UITextView needs to measure the text element, not the outer container\n const result = new UIRectangle(0, 0, 0, 0)\n if (this.rootView.forceIntrinsicSizeZero) {\n return result\n }\n \n let temporarilyInViewTree = NO\n let nodeAboveThisView: Node | null = null\n if (!this.isMemberOfViewTree) {\n document.body.appendChild(this.viewHTMLElement)\n temporarilyInViewTree = YES\n nodeAboveThisView = this.viewHTMLElement.nextSibling\n }\n \n // Save and clear styles on the TEXT ELEMENT (not the container)\n const height = this._textElementView.style.height\n const width = this._textElementView.style.width\n \n this._textElementView.style.height = \"\" + constrainingHeight\n this._textElementView.style.width = \"\" + constrainingWidth\n \n const left = this._textElementView.style.left\n const right = this._textElementView.style.right\n const bottom = this._textElementView.style.bottom\n const top = this._textElementView.style.top\n \n this._textElementView.style.left = \"\"\n this._textElementView.style.right = \"\"\n this._textElementView.style.bottom = \"\"\n this._textElementView.style.top = \"\"\n \n // Measure height with the text element\n const resultHeight = this._textElementView.viewHTMLElement.scrollHeight\n \n // Measure width by temporarily setting nowrap\n const whiteSpace = this._textElementView.style.whiteSpace\n this._textElementView.style.whiteSpace = \"nowrap\"\n \n const resultWidth = this._textElementView.viewHTMLElement.scrollWidth\n \n this._textElementView.style.whiteSpace = whiteSpace\n \n // Restore styles on the TEXT ELEMENT\n this._textElementView.style.height = height\n this._textElementView.style.width = width\n \n this._textElementView.style.left = left\n this._textElementView.style.right = right\n this._textElementView.style.bottom = bottom\n this._textElementView.style.top = top\n \n if (temporarilyInViewTree) {\n document.body.removeChild(this.viewHTMLElement)\n if (this.superview) {\n if (nodeAboveThisView) {\n this.superview.viewHTMLElement.insertBefore(this.viewHTMLElement, nodeAboveThisView)\n }\n else {\n this.superview.viewHTMLElement.appendChild(this.viewHTMLElement)\n }\n }\n }\n \n result.height = resultHeight\n result.width = resultWidth\n \n this._setCachedIntrinsicSize(cacheKey, result)\n \n return result\n }\n \n \n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAwB;AAExB,sBAA6E;AAC7E,yBAA4B;AAC5B,+BAAwD;AACxD,oBAA6C;AAGtC,MAAM,cAAN,cAAyB,qBAAO;AAAA,EAwCnC,YACI,WACA,eAAyD,YAAW,KAAK,WACzE,kBAAkB,MACpB;AAGE,UAAM,iBAAiB,YAAY,GAAG,0BAA0B;AAChE,UAAM,mBAAmB,IAAI,qBAAO,gBAAgB,MAAM,YAAY;AAGtE,UAAM,WAAW,iBAAiB,QAAQ,EAAE,iBAAiB,CAAC;AAgflE,sBAAa;AACb,sBAAa;AACb,+BAAsB;AAMtB,sBAAsB,YAAW;AAEjC,yBAAgB;AAQhB,uCAA8B;AAI9B,SAAQ,4BAA4B;AAAA,MAChC,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,cAAc;AAAA,IAClB;AAMA,wBAAe;AAGf,iCAA+E,IAAI,yBAAS;AAC5F,gCAA8E,IAAI,yBAAS;AAK3F,SAAS,yCAAyC;AAvhB9C,SAAK,oBAAoB;AAAA,MAErB,iBAAiB;AAAA,QACb,OAAO;AAAA,UACH,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,UAAU;AAAA,QACd;AAAA,MACJ;AAAA,IACJ,CAAC;AAED,SAAK,OAAO;AAEZ,SAAK,mBAAmB;AAGxB,SAAK,iBAAiB,oBAAoB;AAAA,MACtC,OAAO;AAAA,QACH,UAAU;AAAA,QACV,UAAU;AAAA,QACV,cAAc;AAAA,QACd,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,MAEA,4BAAwB,wBAAO,KAAK,uBAAuB,KAAK,IAAI,CAAC;AAAA,IACzE,CAAC;AAGD,SAAK,WAAW,KAAK,gBAAgB;AAGrC,SAAK,eAAe;AAEpB,SAAK,YAAY,KAAK;AAEtB,SAAK,yBAAyB;AAE9B,QAAI,gBAAgB,YAAW,KAAK,UAAU;AAC1C,WAAK,sBAAsB;AAC3B,WAAK;AAAA,QACD,qBAAO,aAAa;AAAA,QACpB,CAAC,QAAQ,UAAU,OAAO,MAAM;AAAA,MACpC;AAAA,IACJ;AAAA,EACJ;AAAA,EAWA,IAAI,kBAA0B;AAC1B,WAAO,KAAK;AAAA,EAChB;AAAA,EAmBA,IAAI,iBAAiB;AACjB,WAAO,KAAK,gBAAgB;AAAA,EAChC;AAAA,EAKA,IAAa,eAAe;AACxB,WAAO,KAAK,iBAAiB;AAAA,EACjC;AAAA,EAEA,IAAa,aAAa,cAAwB;AAC9C,SAAK,iBAAiB,eAAe;AAAA,EACzC;AAAA,EAMS,yBAAyB,OAA6B;AAC3D,UAAM,yBAAyB,KAAK;AAAA,EACxC;AAAA,EAES,oBAAoB,WAAmB;AAC5C,UAAM,oBAAoB,SAAS;AAAA,EACvC;AAAA,EAES,iBAAiB;AACtB,UAAM,eAAe;AAErB,QAAI,KAAK,6BAA6B;AAClC,WAAK,WAAW,YAAW;AAAA,QACvB,IAAI;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK,gBAAgB,gBAAgB;AAAA,UACrC,KAAK,gBAAgB,gBAAgB;AAAA,QACzC;AAAA,QACA,KAAK,qBAAqB;AAAA,QAC1B,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACT;AAAA,IACJ;AAAA,EACJ;AAAA,EAMQ,+BAAqC;AACzC,SAAK,2BAA2B;AAChC,+CAAkB,kBAAkB,KAAK,gBAAgB,eAAe;AACxE,SAAK,uBAAuB,CAAC;AAAA,EACjC;AAAA,EAEQ,wBAAqD;AACzD,QAAI,KAAK,0BAA0B;AAC/B,aAAO,KAAK;AAAA,IAChB;AAGA,QAAI,CAAC,KAAK,gBAAgB,gBAAgB,aAAa;AACnD,aAAO;AAAA,IACX;AAIA,SAAK,gBAAgB,gBAAgB;AAErC,UAAM,WAAW,OAAO,iBAAiB,KAAK,gBAAgB,eAAe;AAC7E,UAAM,cAAc,SAAS;AAC7B,UAAM,WAAW,WAAW,WAAW;AAEvC,QAAI,CAAC,YAAY,MAAM,QAAQ,GAAG;AAC9B,aAAO;AAAA,IACX;AAEA,UAAM,aAAa,KAAK,iBAAiB,SAAS,YAAY,QAAQ;AAEtE,QAAI,MAAM,UAAU,GAAG;AACnB,aAAO;AAAA,IACX;AAEA,UAAM,OAAO;AAAA,MACT,SAAS,aAAa;AAAA,MACtB,SAAS,eAAe;AAAA,MACxB,SAAS,cAAc;AAAA,MACvB,WAAW;AAAA,MACX,SAAS,cAAc;AAAA,IAC3B,EAAE,KAAK,GAAG;AAEV,SAAK,2BAA2B;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,SAAS,cAAc;AAAA,MACnC,aAAa,WAAW,SAAS,WAAW,KAAK;AAAA,MACjD,cAAc,WAAW,SAAS,YAAY,KAAK;AAAA,MACnD,YAAY,WAAW,SAAS,UAAU,KAAK;AAAA,MAC/C,eAAe,WAAW,SAAS,aAAa,KAAK;AAAA,IACzD;AAEA,WAAO,KAAK;AAAA,EAChB;AAAA,EAEQ,iBAAiB,YAAoB,UAA0B;AACnE,QAAI,eAAe,UAAU;AACzB,aAAO,WAAW;AAAA,IACtB;AACA,QAAI,WAAW,SAAS,IAAI,GAAG;AAC3B,aAAO,WAAW,UAAU;AAAA,IAChC;AACA,UAAM,oBAAoB,WAAW,UAAU;AAC/C,QAAI,CAAC,MAAM,iBAAiB,GAAG;AAC3B,aAAO,WAAW;AAAA,IACtB;AACA,WAAO,WAAW;AAAA,EACtB;AAAA,EAEQ,4BAAqC;AACzC,UAAM,UAAU,KAAK,QAAQ,KAAK,gBAAgB;AAElD,QAAI,KAAK,iBAAiB,KAAK,sBAAsB;AACjD,aAAO;AAAA,IACX;AAEA,QAAI,KAAK,qBAAqB,GAAG;AAC7B,aAAO;AAAA,IACX;AAEA,UAAM,iBAAiB,2CAA2C,KAAK,OAAO;AAE9E,WAAO,CAAC;AAAA,EACZ;AAAA,EAMA,sBAAsB,SAAwB;AAC1C,SAAK,sBAAsB;AAC3B,SAAK,uBAAuB,CAAC;AAAA,EACjC;AAAA,EAEA,gCAAsC;AAClC,SAAK,sBAAsB;AAC3B,SAAK,6BAA6B;AAAA,EACtC;AAAA,EAMA,IAAI,gBAAgB;AAChB,WAAO,KAAK,iBAAiB,MAAM;AAAA,EACvC;AAAA,EAEA,IAAI,cAAc,eAAyD;AACvE,SAAK,iBAAiB;AACtB,SAAK,iBAAiB,MAAM,YAAY;AAAA,EAC5C;AAAA,EAMA,IAAI,YAAY;AACZ,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,UAAU,OAAgB;AAC1B,SAAK,aAAa,SAAS,YAAW;AACtC,SAAK,iBAAiB,MAAM,QAAQ,KAAK,WAAW;AAAA,EACxD;AAAA,EAMA,IAAI,eAAe;AACf,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,aAAa,cAAuB;AACpC,SAAK,gBAAgB;AAErB,SAAK,wBAAwB,IAAI,yBAAS;AAC1C,SAAK,uBAAuB,IAAI,yBAAS;AAEzC,QAAI,cAAc;AAEd,WAAK,iBAAiB,MAAM,aAAa;AACzC,WAAK,iBAAiB,MAAM,eAAe;AAC3C,WAAK,iBAAiB,MAAM,UAAU;AACtC,WAAK,iBAAiB,MAAM,kBAAkB;AAC9C,WAAK,iBAAiB,MAAM,kBAAkB;AAC9C;AAAA,IACJ;AAIA,SAAK,iBAAiB,MAAM,aAAa;AACzC,SAAK,iBAAiB,MAAM,eAAe;AAC3C,SAAK,iBAAiB,MAAM,UAAU;AACtC,SAAK,iBAAiB,MAAM,kBAAkB;AAG9C,SAAK,8BAA8B;AAAA,EACvC;AAAA,EAMA,IAAI,qBAAqB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,IAAI,mBAAmB,oBAA4B;AAC/C,QAAI,KAAK,uBAAuB,oBAAoB;AAChD;AAAA,IACJ;AAEA,SAAK,sBAAsB;AAC3B,SAAK,OAAO,KAAK;AACjB,SAAK,2BAA2B;AAChC,SAAK,4BAA4B,kBAAkB;AAAA,EACvD;AAAA,EAEA,4BAA4B,oBAA4B;AAAA,EACxD;AAAA,EAMA,IAAI,OAAO;AACP,WAAQ,KAAK,SAAS,KAAK,gBAAgB,gBAAgB;AAAA,EAC/D;AAAA,EAEA,IAAI,KAAK,MAAM;AACX,SAAK,QAAQ;AACb,QAAI,mBAAmB;AACvB,QAAI,KAAK,oBAAoB;AACzB,yBAAmB,yBAA0B,YAAW,sBAAsB,cAAc,SACvF,OAAO,KAAK,qBAAqB,KAAK,KAAK,IAAI;AAAA,IACxD;AAEA,QAAI,KAAK,gBAAgB,gBAAgB,aAAa,KAAK,aAAa,OAAO,KAAK,aAAa,kBAAkB;AAC/G,WAAK,gBAAgB,gBAAgB,YAAY,KAAK,iBAAa;AAAA,QAC/D;AAAA,QAAM;AAAA,MAAE,IAAI,KAAK,aAAa;AAAA,IACtC;AAEA,QAAI,KAAK,cAAc;AACnB,WAAK,wBAAwB,IAAI,yBAAS;AAC1C,WAAK,uBAAuB,IAAI,yBAAS;AAAA,IAC7C;AAEA,SAAK,sBAAsB;AAC3B,SAAK,uBAAuB,CAAC;AAC7B,SAAK,8BAA8B;AACnC,SAAK,6BAA6B;AAClC,SAAK,wBAAwB;AAE7B,SAAK,eAAe;AAAA,EACxB;AAAA,EAEA,IAAa,UAAU,WAAmB;AACtC,SAAK,OAAO;AACZ,SAAK,8BAA8B;AAAA,EACvC;AAAA,EAEA,IAAa,YAAY;AACrB,WAAO,KAAK,gBAAgB;AAAA,EAChC;AAAA,EAEA,QAAQ,KAAa,eAAuB,YAA8D;AACtG,SAAK,gBAAgB,aAAa,KAAK,eAAe,UAAU;AAChE,SAAK,8BAA8B;AAAA,EACvC;AAAA,EAMA,IAAI,WAAW;AACX,UAAM,QAAQ,KAAK,iBAAiB,MAAM,YAAY,OAAO,iBAAiB,KAAK,iBAAiB,iBAAiB,IAAI,EAAE;AAC3H,UAAM,SAAU,WAAW,KAAK,IAAI,YAAW;AAC/C,WAAO;AAAA,EACX;AAAA,EAEA,IAAI,SAAS,UAAkB;AAC3B,QAAI,YAAY,KAAK,UAAU;AAC3B,WAAK,iBAAiB,MAAM,WAAW,KAAK,WAAW;AAEvD,WAAK,wBAAwB,IAAI,yBAAS;AAC1C,WAAK,uBAAuB,IAAI,yBAAS;AAEzC,WAAK,qBAAqB;AAC1B,WAAK,6BAA6B;AAClC,WAAK,wBAAwB;AAAA,IACjC;AAAA,EACJ;AAAA,EAEA,qBAAqB,cAAsB,qBAAK,cAAsB,qBAAK;AACvE,SAAK,8BAA8B;AACnC,SAAK,eAAe;AACpB,SAAK,eAAe;AACpB,SAAK,eAAe;AAAA,EACxB;AAAA,EAUQ,mBAA2B;AAE/B,UAAM,kBAAkB;AAAA,MACpB,UAAU,KAAK,iBAAiB,MAAM,YAAY;AAAA,MAClD,YAAY,KAAK,iBAAiB,MAAM,cAAc;AAAA,MACtD,YAAY,KAAK,iBAAiB,MAAM,cAAc;AAAA,MACtD,WAAW,KAAK,iBAAiB,MAAM,aAAa;AAAA,MACpD,cAAc,KAAK,aAAa,KAAK,GAAG;AAAA,IAC5C;AAEA,UAAM,aACF,gBAAgB,aAAa,KAAK,0BAA0B,YAC5D,gBAAgB,eAAe,KAAK,0BAA0B,cAC9D,gBAAgB,eAAe,KAAK,0BAA0B,cAC9D,gBAAgB,cAAc,KAAK,0BAA0B,aAC7D,gBAAgB,iBAAiB,KAAK,0BAA0B;AAEpE,QAAI,CAAC,KAAK,kBAAkB,YAAY;AAEpC,YAAM,WAAW,KAAK,iBAAiB;AACvC,WAAK,iBAAiB;AAAA,QAClB,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,MACb,EAAE,KAAK,GAAG,EAAE,QAAQ,UAAU,GAAG;AAEjC,WAAK,4BAA4B;AAAA,IACrC;AAEA,WAAO,KAAK;AAAA,EAChB;AAAA,EAKQ,uBAA6B;AACjC,SAAK,iBAAiB;AAAA,EAC1B;AAAA,EAMA,OAAO,0BAA0B;AAC7B,QAAI,YAAW,SAAS;AACpB;AAAA,IACJ;AAEA,UAAM,IAAI,SAAS,cAAc,KAAK;AACtC,MAAE,MAAM,QAAQ;AAChB,aAAS,KAAK,YAAY,CAAC;AAC3B,gBAAW,UAAU,EAAE,cAAc;AACrC,aAAS,KAAK,YAAY,CAAC;AAC3B,gBAAW,UAAU,IAAI,YAAW;AAAA,EACxC;AAAA,EAEA,OAAO,gCACH,QACA,aACA,iBACA,aACA,aACF;AACE,sBAAc,uBAAM,aAAa,CAAC;AAClC,sBAAc,uBAAM,aAAa,IAAY;AAE7C,UAAM,mBAAmB,OAAO,UAAU,YAAY,SAAS;AAC/D,UAAM,kBAAkB,OAAO,SAAS,YAAY,QAAQ;AAE5D,QAAI,aAAa;AACjB,QAAI,mBAAmB,iBAAiB;AACpC,mBAAa;AAAA,IACjB;AAEA,UAAM,qBAAqB,kBAAkB;AAE7C,QAAI,qBAAqB,aAAa;AAClC,aAAO;AAAA,IACX;AAEA,QAAI,cAAc,oBAAoB;AAClC,aAAO;AAAA,IACX;AAEA,WAAO;AAAA,EACX;AAAA,EAuDS,cAAc,YAAoB;AACvC,UAAM,cAAc,UAAU;AAC9B,SAAK,qBAAqB;AAAA,EAC9B;AAAA,EAGS,iBAAiB,YAAoB;AAC1C,UAAM,iBAAiB,UAAU;AACjC,SAAK,qBAAqB;AAAA,EAC9B;AAAA,EAGS,QAAQ;AACb,SAAK,iBAAiB,MAAM;AAAA,EAChC;AAAA,EAGS,OAAO;AACZ,SAAK,iBAAiB,KAAK;AAAA,EAC/B;AAAA,EAES,uBAAuB,oBAAoB,GAAG;AAhnB3D;AAknBQ,UAAM,WAAY,KAAK,gBAAgB,gBAAgB,aAAa,KAAK,QAAQ,UAAU,KAAK,iBAAiB,IAAK,OACjH,KAAK,mBAAmB,QAAQ,IAAI,OAAO,OAAO,GAAG,GAAG,GAAG;AAEhE,QAAI,cAAc,YAAW;AAE7B,QAAI,KAAK,cAAc;AACnB,oBAAc,KAAK;AAAA,IACvB;AAEA,QAAI,SAAS,YAAY,gBAAgB,OAAO;AAEhD,YAAI,8BAAa,MAAM,GAAG;AAEtB,YAAM,qBAAoB,UAAK,wBAAL,YAA4B,KAAK,0BAA0B;AAErF,UAAI,mBAAmB;AAEnB,cAAM,SAAS,KAAK,sBAAsB;AAG1C,YAAI,QAAQ;AACR,gBAAM,OAAO,2CAAkB;AAAA,YAC3B,KAAK,gBAAgB;AAAA,YACrB,KAAK,QAAQ,KAAK,gBAAgB;AAAA,YAClC,qBAAqB;AAAA,YACrB;AAAA,YACA;AAAA,UACJ;AACA,mBAAS,KAAK;AAAA,QAClB,OACK;AAED,mBAAS,MAAM,uBAAuB,iBAAiB;AAAA,QAC3D;AAAA,MACJ,OACK;AAED,iBAAS,MAAM,uBAAuB,iBAAiB;AAAA,MAC3D;AAEA,kBAAY,mBAAmB,SAAS,MAAM;AAAA,IAClD;AAEA,QAAI,MAAM,MAAM,KAAM,CAAC,UAAU,CAAC,KAAK,MAAO;AAC1C,eAAS,MAAM,uBAAuB,iBAAiB;AACvD,kBAAY,mBAAmB,SAAS,MAAM;AAAA,IAClD;AAEA,WAAO;AAAA,EACX;AAAA,EAES,sBAAsB,qBAAqB,GAAG;AArqB3D;AAuqBQ,UAAM,WAAY,KAAK,gBAAgB,gBAAgB,aAAa,KAAK,QAAQ,UAAU,KAAK,iBAAiB,IAAK,OACjH,KAAK,oBAAoB,QAAQ,IAAI,OAAO,OAAO,GAAG,GAAG,GAAG;AAEjE,QAAI,cAAc,YAAW;AAE7B,QAAI,KAAK,cAAc;AACnB,oBAAc,KAAK;AAAA,IACvB;AAEA,QAAI,SAAS,YAAY,gBAAgB,OAAO;AAEhD,YAAI,8BAAa,MAAM,GAAG;AAEtB,YAAM,qBAAoB,UAAK,wBAAL,YAA4B,KAAK,0BAA0B;AAErF,UAAI,mBAAmB;AAEnB,cAAM,SAAS,KAAK,sBAAsB;AAG1C,YAAI,QAAQ;AACR,gBAAM,OAAO,2CAAkB;AAAA,YAC3B,KAAK,gBAAgB;AAAA,YACrB,KAAK,QAAQ,KAAK,gBAAgB;AAAA,YAClC;AAAA,YACA,sBAAsB;AAAA,YACtB;AAAA,UACJ;AACA,mBAAS,KAAK;AAAA,QAClB,OACK;AAED,mBAAS,MAAM,sBAAsB,kBAAkB;AAAA,QAC3D;AAAA,MACJ,OACK;AAED,iBAAS,MAAM,sBAAsB,kBAAkB;AAAA,MAC3D;AAEA,kBAAY,mBAAmB,SAAS,MAAM;AAAA,IAClD;AAEA,WAAO;AAAA,EACX;AAAA,EAGS,oCAAoC,qBAA6B,GAAG,oBAA4B,GAAG;AAExG,UAAM,WAAW,KAAK,0BAA0B,oBAAoB,iBAAiB;AACrF,UAAM,eAAe,KAAK,wBAAwB,QAAQ;AAC1D,QAAI,cAAc;AACd,aAAO;AAAA,IACX;AAGA,UAAM,SAAS,IAAI,+BAAY,GAAG,GAAG,GAAG,CAAC;AACzC,QAAI,KAAK,SAAS,wBAAwB;AACtC,aAAO;AAAA,IACX;AAEA,QAAI,wBAAwB;AAC5B,QAAI,oBAAiC;AACrC,QAAI,CAAC,KAAK,oBAAoB;AAC1B,eAAS,KAAK,YAAY,KAAK,eAAe;AAC9C,8BAAwB;AACxB,0BAAoB,KAAK,gBAAgB;AAAA,IAC7C;AAGA,UAAM,SAAS,KAAK,iBAAiB,MAAM;AAC3C,UAAM,QAAQ,KAAK,iBAAiB,MAAM;AAE1C,SAAK,iBAAiB,MAAM,SAAS,KAAK;AAC1C,SAAK,iBAAiB,MAAM,QAAQ,KAAK;AAEzC,UAAM,OAAO,KAAK,iBAAiB,MAAM;AACzC,UAAM,QAAQ,KAAK,iBAAiB,MAAM;AAC1C,UAAM,SAAS,KAAK,iBAAiB,MAAM;AAC3C,UAAM,MAAM,KAAK,iBAAiB,MAAM;AAExC,SAAK,iBAAiB,MAAM,OAAO;AACnC,SAAK,iBAAiB,MAAM,QAAQ;AACpC,SAAK,iBAAiB,MAAM,SAAS;AACrC,SAAK,iBAAiB,MAAM,MAAM;AAGlC,UAAM,eAAe,KAAK,iBAAiB,gBAAgB;AAG3D,UAAM,aAAa,KAAK,iBAAiB,MAAM;AAC/C,SAAK,iBAAiB,MAAM,aAAa;AAEzC,UAAM,cAAc,KAAK,iBAAiB,gBAAgB;AAE1D,SAAK,iBAAiB,MAAM,aAAa;AAGzC,SAAK,iBAAiB,MAAM,SAAS;AACrC,SAAK,iBAAiB,MAAM,QAAQ;AAEpC,SAAK,iBAAiB,MAAM,OAAO;AACnC,SAAK,iBAAiB,MAAM,QAAQ;AACpC,SAAK,iBAAiB,MAAM,SAAS;AACrC,SAAK,iBAAiB,MAAM,MAAM;AAElC,QAAI,uBAAuB;AACvB,eAAS,KAAK,YAAY,KAAK,eAAe;AAC9C,UAAI,KAAK,WAAW;AAChB,YAAI,mBAAmB;AACnB,eAAK,UAAU,gBAAgB,aAAa,KAAK,iBAAiB,iBAAiB;AAAA,QACvF,OACK;AACD,eAAK,UAAU,gBAAgB,YAAY,KAAK,eAAe;AAAA,QACnE;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO,SAAS;AAChB,WAAO,QAAQ;AAEf,SAAK,wBAAwB,UAAU,MAAM;AAE7C,WAAO;AAAA,EACX;AAGJ;AA9xBO,IAAM,aAAN;AAAM,WAIF,mBAAmB,uBAAQ;AAJzB,WAKF,wBAAwB,uBAAQ;AAL9B,WAQF,wBAA+E,IAAI,yBAAS;AAR1F,WASF,uBAA8E,IAAI,yBAAS;AATzF,WAcF,OAAO;AAAA,EACV,aAAa;AAAA,EACb,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AACb;AA1BS,WA4BF,gBAAgB;AAAA,EACnB,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AACf;",
4
+ "sourcesContent": ["import { UIColor } from \"./UIColor\"\nimport { UILocalizedTextObject } from \"./UIInterfaces\"\nimport { EXTEND, FIRST, IS_LIKE_NULL, nil, NO, UIObject, ValueOf, YES } from \"./UIObject\"\nimport { UIRectangle } from \"./UIRectangle\"\nimport { TextMeasurementStyle, UITextMeasurement } from \"./UITextMeasurement\"\nimport { UIView, UIViewBroadcastEvent } from \"./UIView\"\n\n\nexport class UITextView extends UIView {\n \n //#region Static Properties\n \n static defaultTextColor = UIColor.blackColor\n static notificationTextColor = UIColor.redColor\n \n // Global caches for all UILabels\n static _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n static _intrinsicWidthCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n \n static _ptToPx: number\n static _pxToPt: number\n \n static type = {\n \"paragraph\": \"p\",\n \"header1\": \"h1\",\n \"header2\": \"h2\",\n \"header3\": \"h3\",\n \"header4\": \"h4\",\n \"header5\": \"h5\",\n \"header6\": \"h6\",\n \"textArea\": \"textarea\",\n \"textField\": \"input\",\n \"span\": \"span\",\n \"label\": \"label\"\n } as const\n \n static textAlignment = {\n \"left\": \"left\",\n \"center\": \"center\",\n \"right\": \"right\",\n \"justify\": \"justify\"\n } as const\n \n //#endregion\n \n //#region Constructor\n \n \n constructor(\n elementID?: string,\n textViewType: string | ValueOf<typeof UITextView.type> = UITextView.type.paragraph,\n viewHTMLElement = null\n ) {\n \n // Create inner text element as a UIView\n const innerElementID = elementID ? `${elementID}_textElement` : undefined\n const _textElementView = new UIView(innerElementID, null, textViewType)\n \n // Create outer container (wrapper) - this is the main viewHTMLElement\n super(elementID, viewHTMLElement, \"span\", { _textElementView })\n \n // Configure outer container for vertical centering using direct property access\n \n this.configureWithObject({\n // @ts-ignore\n viewHTMLElement: {\n style: {\n display: \"flex\",\n alignItems: \"center\", // Vertical centering\n overflow: \"hidden\"\n }\n }\n })\n \n this.text = \"\"\n \n this._textElementView = _textElementView\n \n // Configure inner text element for ellipsis and positioning\n this._textElementView.configureWithObject({\n style: {\n position: \"relative\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n width: \"100%\",\n margin: \"0\",\n padding: \"0\"\n },\n // Forward control events from text element to the container\n sendControlEventForKey: EXTEND(this.sendControlEventForKey.bind(this))\n })\n \n // Add text element as a subview\n this.addSubview(this._textElementView)\n \n \n this.isSingleLine = YES\n \n this.textColor = this.textColor\n \n this.userInteractionEnabled = YES\n \n if (textViewType == UITextView.type.textArea) {\n this.pausesPointerEvents = YES\n this.addTargetForControlEvent(\n UIView.controlEvent.PointerUpInside,\n (sender, event) => sender.focus()\n )\n }\n }\n \n //#endregion\n \n //#region Text Element View Property\n \n private _textElementView: UIView\n \n /**\n * The inner text element that holds the actual text content\n */\n get textElementView(): UIView {\n return this._textElementView\n }\n \n /**\n * Override style to apply to the text element instead of the container\n */\n // override get style() {\n // return this._textElementView.style\n // }\n //\n // /**\n // * Override computedStyle to get computed styles from the text element\n // */\n // override get computedStyle() {\n // return this._textElementView.computedStyle\n // }\n \n /**\n * Access the outer container's style (for positioning, layout, etc.)\n */\n get containerStyle() {\n return this.viewHTMLElement.style\n }\n \n /**\n * Override styleClasses to apply to the text element\n */\n override get styleClasses() {\n return this._textElementView.styleClasses\n }\n \n override set styleClasses(styleClasses: string[]) {\n this._textElementView.styleClasses = styleClasses\n }\n \n //#endregion\n \n //#region Lifecycle Methods\n \n override didReceiveBroadcastEvent(event: UIViewBroadcastEvent) {\n super.didReceiveBroadcastEvent(event)\n }\n \n override willMoveToSuperview(superview: UIView) {\n super.willMoveToSuperview(superview)\n }\n \n override documentFontsDidLoad() {\n super.documentFontsDidLoad()\n this._invalidateFontCache()\n this.invalidateMeasurementStrategy()\n this._intrinsicHeightCache = new UIObject() as any\n this._intrinsicWidthCache = new UIObject() as any\n UITextView._intrinsicHeightCache = new UIObject() as any\n UITextView._intrinsicWidthCache = new UIObject() as any\n }\n \n override layoutSubviews() {\n super.layoutSubviews()\n \n if (this._automaticFontSizeSelection) {\n this.fontSize = UITextView.automaticallyCalculatedFontSize(\n new UIRectangle(\n 0,\n 0,\n this.textElementView.viewHTMLElement.offsetHeight,\n this.textElementView.viewHTMLElement.offsetWidth\n ),\n this.intrinsicContentSize(),\n this.fontSize,\n this._minFontSize,\n this._maxFontSize\n )\n }\n }\n \n //#endregion\n \n //#region Measurement & Sizing - Private Methods\n \n private _invalidateMeasurementStyles(): void {\n this._cachedMeasurementStyles = undefined\n UITextMeasurement.invalidateElement(this.textElementView.viewHTMLElement)\n this._intrinsicSizesCache = {}\n }\n \n private _getMeasurementStyles(): TextMeasurementStyle | null {\n if (this._cachedMeasurementStyles) {\n return this._cachedMeasurementStyles\n }\n \n // Ensure element is in document\n if (!this.textElementView.viewHTMLElement.isConnected) {\n return null\n }\n \n // Force a layout flush ONCE to ensure computed styles are available\n // This is only paid once per style change, then we use cached values\n this.textElementView.viewHTMLElement.offsetHeight\n \n const computed = window.getComputedStyle(this.textElementView.viewHTMLElement)\n const fontSizeStr = computed.fontSize\n const fontSize = parseFloat(fontSizeStr)\n \n console.log(computed.letterSpacing)\n \n if (!fontSize || isNaN(fontSize)) {\n return null\n }\n \n const lineHeight = this._parseLineHeight(computed.lineHeight, fontSize)\n \n if (isNaN(lineHeight)) {\n return null\n }\n \n const font = [\n computed.fontStyle || \"normal\",\n computed.fontVariant || \"normal\",\n computed.fontWeight || \"normal\",\n fontSize + \"px\",\n computed.fontFamily || \"sans-serif\"\n ].join(\" \")\n \n this._cachedMeasurementStyles = {\n font: font,\n fontSize: fontSize,\n lineHeight: lineHeight,\n whiteSpace: computed.whiteSpace || \"normal\",\n paddingLeft: parseFloat(computed.paddingLeft) || 0,\n paddingRight: parseFloat(computed.paddingRight) || 0,\n paddingTop: parseFloat(computed.paddingTop) || 0,\n paddingBottom: parseFloat(computed.paddingBottom) || 0,\n letterSpacing: parseFloat(computed.letterSpacing) || 0,\n textTransform: computed.textTransform || \"none\"\n }\n \n return this._cachedMeasurementStyles\n }\n \n private _parseLineHeight(lineHeight: string, fontSize: number): number {\n if (lineHeight === \"normal\") {\n return fontSize * 1.2\n }\n if (lineHeight.endsWith(\"px\")) {\n return parseFloat(lineHeight)\n }\n const numericLineHeight = parseFloat(lineHeight)\n if (!isNaN(numericLineHeight)) {\n return fontSize * numericLineHeight\n }\n return fontSize * 1.2\n }\n \n private _shouldUseFastMeasurement(): boolean {\n const content = this.text || this.textElementView.innerHTML\n \n if (this._innerHTMLKey || this._localizedTextObject) {\n return false\n }\n \n if (this.notificationAmount > 0) {\n return false\n }\n \n const hasComplexHTML = /<(?!\\/?(b|i|em|strong|span|br)\\b)[^>]+>/i.test(content)\n \n if (hasComplexHTML) {\n return false\n }\n \n // Canvas measureText silently falls back to the system font when the\n // custom font hasn't been loaded into the canvas font system yet, even\n // if getComputedStyle already reports the correct font family. Guard\n // against this by checking the font is confirmed available before\n // trusting canvas-based measurement.\n const styles = this._getMeasurementStyles()\n if (styles && !document.fonts.check(styles.font)) {\n return false\n }\n \n return true\n }\n \n //#endregion\n \n //#region Measurement & Sizing - Public Methods\n \n setUseFastMeasurement(useFast: boolean): void {\n this._useFastMeasurement = useFast\n this._intrinsicSizesCache = {}\n }\n \n invalidateMeasurementStrategy(): void {\n this._useFastMeasurement = undefined\n this._invalidateMeasurementStyles()\n }\n \n //#endregion\n \n //#region Getters & Setters - Text Alignment\n \n get textAlignment() {\n return this._textElementView.style.textAlign as ValueOf<typeof UITextView.textAlignment>\n }\n \n set textAlignment(textAlignment: ValueOf<typeof UITextView.textAlignment>) {\n this._textAlignment = textAlignment\n this._textElementView.style.textAlign = textAlignment\n }\n \n //#endregion\n \n //#region Getters & Setters - Text Color\n \n get textColor() {\n return this._textColor\n }\n \n set textColor(color: UIColor) {\n this._textColor = color || UITextView.defaultTextColor\n this._textElementView.style.color = this._textColor.stringValue\n }\n \n //#endregion\n \n //#region Getters & Setters - Single Line\n \n get isSingleLine() {\n return this._isSingleLine\n }\n \n set isSingleLine(isSingleLine: boolean) {\n this._isSingleLine = isSingleLine\n \n this._intrinsicHeightCache = new UIObject() as any\n this._intrinsicWidthCache = new UIObject() as any\n \n if (isSingleLine) {\n // Single line: use nowrap with ellipsis\n this._textElementView.style.whiteSpace = \"nowrap\"\n this._textElementView.style.textOverflow = \"ellipsis\"\n this._textElementView.style.display = \"\"\n this._textElementView.style.webkitLineClamp = \"\"\n this._textElementView.style.webkitBoxOrient = \"\"\n return\n }\n \n // Multiline: allow wrapping, but still show ellipsis if content overflows the container\n // This uses the -webkit-line-clamp approach which works for multiline ellipsis\n this._textElementView.style.whiteSpace = \"normal\"\n this._textElementView.style.textOverflow = \"ellipsis\"\n this._textElementView.style.display = \"-webkit-box\"\n this._textElementView.style.webkitBoxOrient = \"vertical\"\n // Don't set line-clamp to a specific number - let it fill available space\n // The overflow: hidden from the constructor will clip content that exceeds the height\n this.invalidateMeasurementStrategy()\n }\n \n //#endregion\n \n //#region Getters & Setters - Notification Amount\n \n get notificationAmount() {\n return this._notificationAmount\n }\n \n set notificationAmount(notificationAmount: number) {\n if (this._notificationAmount == notificationAmount) {\n return\n }\n \n this._notificationAmount = notificationAmount\n this.text = this.text\n this.setNeedsLayoutUpToRootView()\n this.notificationAmountDidChange(notificationAmount)\n }\n \n notificationAmountDidChange(notificationAmount: number) {\n }\n \n //#endregion\n \n //#region Getters & Setters - Text Content\n \n get text() {\n return (this._text || this.textElementView.viewHTMLElement.innerHTML)\n }\n \n set text(text) {\n this._text = text\n var notificationText = \"\"\n if (this.notificationAmount) {\n notificationText = \"<span style=\\\"color: \" + UITextView.notificationTextColor.stringValue + \";\\\">\" +\n (\" (\" + this.notificationAmount + \")\").bold() + \"</span>\"\n }\n \n if (this.textElementView.viewHTMLElement.innerHTML != this.textPrefix + text + this.textSuffix + notificationText) {\n this.textElementView.viewHTMLElement.innerHTML = this.textPrefix + FIRST(\n text, \"\") + this.textSuffix + notificationText\n }\n \n if (this.changesOften) {\n this._intrinsicHeightCache = new UIObject() as any\n this._intrinsicWidthCache = new UIObject() as any\n }\n \n this._useFastMeasurement = undefined\n this._intrinsicSizesCache = {}\n this.invalidateMeasurementStrategy()\n this._invalidateMeasurementStyles()\n this.clearIntrinsicSizeCache()\n \n this.setNeedsLayout()\n }\n \n override set innerHTML(innerHTML: string) {\n this.text = innerHTML\n this.invalidateMeasurementStrategy()\n }\n \n override get innerHTML() {\n return this.viewHTMLElement.innerHTML\n }\n \n setText(key: string, defaultString: string, parameters?: { [x: string]: string | UILocalizedTextObject }) {\n this.textElementView.setInnerHTML(key, defaultString, parameters)\n this.invalidateMeasurementStrategy()\n }\n \n //#endregion\n \n //#region Getters & Setters - Font Size\n \n get fontSize() {\n const style = this._textElementView.style.fontSize || window.getComputedStyle(this._textElementView.viewHTMLElement, null).fontSize\n const result = (parseFloat(style) * UITextView._pxToPt)\n return result\n }\n \n set fontSize(fontSize: number) {\n if (fontSize != this.fontSize) {\n this._textElementView.style.fontSize = \"\" + fontSize + \"pt\"\n \n this._intrinsicHeightCache = new UIObject() as any\n this._intrinsicWidthCache = new UIObject() as any\n \n this._invalidateFontCache()\n this._invalidateMeasurementStyles()\n this.clearIntrinsicSizeCache()\n }\n }\n \n useAutomaticFontSize(minFontSize: number = nil, maxFontSize: number = nil) {\n this._automaticFontSizeSelection = YES\n this._minFontSize = minFontSize\n this._maxFontSize = maxFontSize\n this.setNeedsLayout()\n }\n \n //#endregion\n \n //#region Font Caching - Private Methods\n \n /**\n * Get a stable cache key for the font without triggering reflow.\n * Only computes font on first access or when font properties change.\n */\n private _getFontCacheKey(): string {\n // Check if font-related properties have changed\n const currentTriggers = {\n fontSize: this._textElementView.style.fontSize || \"\",\n fontFamily: this._textElementView.style.fontFamily || \"\",\n fontWeight: this._textElementView.style.fontWeight || \"\",\n fontStyle: this._textElementView.style.fontStyle || \"\",\n styleClasses: this.styleClasses.join(\",\")\n }\n \n const hasChanged =\n currentTriggers.fontSize !== this._fontInvalidationTriggers.fontSize ||\n currentTriggers.fontFamily !== this._fontInvalidationTriggers.fontFamily ||\n currentTriggers.fontWeight !== this._fontInvalidationTriggers.fontWeight ||\n currentTriggers.fontStyle !== this._fontInvalidationTriggers.fontStyle ||\n currentTriggers.styleClasses !== this._fontInvalidationTriggers.styleClasses\n \n if (!this._cachedFontKey || hasChanged) {\n // Only access computedStyle when we know something changed\n const computed = this._textElementView.computedStyle\n this._cachedFontKey = [\n computed.fontStyle,\n computed.fontVariant,\n computed.fontWeight,\n computed.fontSize,\n computed.fontFamily\n ].join(\"_\").replace(/[.\\s]/g, \"_\")\n \n this._fontInvalidationTriggers = currentTriggers\n }\n \n return this._cachedFontKey\n }\n \n /**\n * Invalidate font cache when font properties change\n */\n private _invalidateFontCache(): void {\n this._cachedFontKey = undefined\n }\n \n //#endregion\n \n //#region Static Methods\n \n static _determinePXAndPTRatios() {\n if (UITextView._ptToPx) {\n return\n }\n \n const o = document.createElement(\"div\")\n o.style.width = \"1000pt\"\n document.body.appendChild(o)\n UITextView._ptToPx = o.clientWidth / 1000\n document.body.removeChild(o)\n UITextView._pxToPt = 1 / UITextView._ptToPx\n }\n \n static automaticallyCalculatedFontSize(\n bounds: UIRectangle,\n currentSize: UIRectangle,\n currentFontSize: number,\n minFontSize?: number,\n maxFontSize?: number\n ) {\n minFontSize = FIRST(minFontSize, 1)\n maxFontSize = FIRST(maxFontSize, 100000000000)\n \n const heightMultiplier = bounds.height / (currentSize.height + 1)\n const widthMultiplier = bounds.width / (currentSize.width + 1)\n \n var multiplier = heightMultiplier\n if (heightMultiplier > widthMultiplier) {\n multiplier = widthMultiplier\n }\n \n const maxFittingFontSize = currentFontSize * multiplier\n \n if (maxFittingFontSize > maxFontSize) {\n return maxFontSize\n }\n \n if (minFontSize > maxFittingFontSize) {\n return minFontSize\n }\n \n return maxFittingFontSize\n }\n \n //#endregion\n \n //#region Instance Properties - Text Content\n \n _text?: string\n textPrefix = \"\"\n textSuffix = \"\"\n _notificationAmount = 0\n \n //#endregion\n \n //#region Instance Properties - Styling\n \n _textColor: UIColor = UITextView.defaultTextColor\n _textAlignment?: ValueOf<typeof UITextView.textAlignment>\n _isSingleLine = YES\n \n //#endregion\n \n //#region Instance Properties - Font & Sizing\n \n _minFontSize?: number\n _maxFontSize?: number\n _automaticFontSizeSelection = NO\n \n // Cache for the computed font string\n private _cachedFontKey?: string\n private _fontInvalidationTriggers = {\n fontSize: \"\",\n fontFamily: \"\",\n fontWeight: \"\",\n fontStyle: \"\",\n styleClasses: \"\"\n }\n \n //#endregion\n \n //#region Instance Properties - Caching & Performance\n \n changesOften = NO\n \n // Local cache for this instance if the label changes often\n _intrinsicHeightCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n _intrinsicWidthCache: { [x: string]: { [x: string]: number; }; } & UIObject = new UIObject() as any\n \n private _useFastMeasurement: boolean | undefined\n private _cachedMeasurementStyles: TextMeasurementStyle | undefined | null\n \n override usesVirtualLayoutingForIntrinsicSizing = NO\n \n //#endregion\n \n // Override addStyleClass to invalidate font cache\n override addStyleClass(styleClass: string) {\n super.addStyleClass(styleClass)\n this._invalidateFontCache()\n }\n \n // Override removeStyleClass to invalidate font cache\n override removeStyleClass(styleClass: string) {\n super.removeStyleClass(styleClass)\n this._invalidateFontCache()\n }\n \n // Override focus to focus the text element\n override focus() {\n this._textElementView.focus()\n }\n \n // Override blur to blur the text element\n override blur() {\n this._textElementView.blur()\n }\n \n override intrinsicContentHeight(constrainingWidth = 0) {\n \n const keyPath = ((this.textElementView.viewHTMLElement.innerHTML || this.text) + \"_csf_\" + this._getFontCacheKey()) + \".\" +\n (\"\" + constrainingWidth).replace(new RegExp(\"\\\\.\", \"g\"), \"_\")\n \n let cacheObject = UITextView._intrinsicHeightCache\n \n if (this.changesOften) {\n cacheObject = this._intrinsicHeightCache\n }\n \n var result = cacheObject.valueForKeyPath(keyPath)\n \n if (IS_LIKE_NULL(result)) {\n // Determine if we should use fast measurement\n const shouldUseFastPath = this._useFastMeasurement ?? this._shouldUseFastMeasurement()\n \n if (shouldUseFastPath) {\n // Fast path: use UITextMeasurement with pre-extracted styles\n const styles = this._getMeasurementStyles()\n \n // If styles are invalid (element not properly initialized), fall back to DOM\n if (styles) {\n const size = UITextMeasurement.calculateTextSize(\n this.textElementView.viewHTMLElement,\n this.text || this.textElementView.innerHTML,\n constrainingWidth || undefined,\n undefined,\n styles\n )\n result = size.height\n }\n else {\n // Styles not ready, use DOM measurement\n result = super.intrinsicContentHeight(constrainingWidth)\n }\n }\n else {\n // Fallback: DOM-based measurement for complex content\n result = super.intrinsicContentHeight(constrainingWidth)\n }\n \n cacheObject.setValueForKeyPath(keyPath, result)\n }\n \n if (isNaN(result) || (!result && !this.text)) {\n result = super.intrinsicContentHeight(constrainingWidth)\n cacheObject.setValueForKeyPath(keyPath, result)\n }\n \n return result\n }\n \n override intrinsicContentWidth(constrainingHeight = 0) {\n \n const keyPath = ((this.textElementView.viewHTMLElement.innerHTML || this.text) + \"_csf_\" + this._getFontCacheKey()) + \".\" +\n (\"\" + constrainingHeight).replace(new RegExp(\"\\\\.\", \"g\"), \"_\")\n \n let cacheObject = UITextView._intrinsicWidthCache\n \n if (this.changesOften) {\n cacheObject = this._intrinsicWidthCache\n }\n \n var result = cacheObject.valueForKeyPath(keyPath)\n \n if (IS_LIKE_NULL(result)) {\n // Determine if we should use fast measurement\n const shouldUseFastPath = this._useFastMeasurement ?? this._shouldUseFastMeasurement()\n \n if (shouldUseFastPath) {\n // Fast path: use UITextMeasurement with pre-extracted styles\n const styles = this._getMeasurementStyles()\n \n // If styles are invalid (element not properly initialized), fall back to DOM\n if (styles) {\n const size = UITextMeasurement.calculateTextSize(\n this.textElementView.viewHTMLElement,\n this.text || this.textElementView.innerHTML,\n undefined,\n constrainingHeight || undefined,\n styles\n )\n result = size.width\n }\n else {\n // Styles not ready, use DOM measurement\n result = super.intrinsicContentWidth(constrainingHeight)\n }\n }\n else {\n // Fallback: DOM-based measurement for complex content\n result = super.intrinsicContentWidth(constrainingHeight)\n }\n \n cacheObject.setValueForKeyPath(keyPath, result)\n }\n \n return result\n }\n \n \n override intrinsicContentSizeWithConstraints(constrainingHeight: number = 0, constrainingWidth: number = 0) {\n \n const cacheKey = this._getIntrinsicSizeCacheKey(constrainingHeight, constrainingWidth)\n const cachedResult = this._getCachedIntrinsicSize(cacheKey)\n if (cachedResult) {\n return cachedResult\n }\n \n // UITextView needs to measure the text element, not the outer container\n const result = new UIRectangle(0, 0, 0, 0)\n if (this.rootView.forceIntrinsicSizeZero) {\n return result\n }\n \n let temporarilyInViewTree = NO\n let nodeAboveThisView: Node | null = null\n if (!this.isMemberOfViewTree) {\n document.body.appendChild(this.viewHTMLElement)\n temporarilyInViewTree = YES\n nodeAboveThisView = this.viewHTMLElement.nextSibling\n }\n \n // Save and clear styles on the TEXT ELEMENT (not the container)\n const height = this._textElementView.style.height\n const width = this._textElementView.style.width\n \n this._textElementView.style.height = \"\" + constrainingHeight\n this._textElementView.style.width = \"\" + constrainingWidth\n \n const left = this._textElementView.style.left\n const right = this._textElementView.style.right\n const bottom = this._textElementView.style.bottom\n const top = this._textElementView.style.top\n \n this._textElementView.style.left = \"\"\n this._textElementView.style.right = \"\"\n this._textElementView.style.bottom = \"\"\n this._textElementView.style.top = \"\"\n \n // Measure height with the text element\n const resultHeight = this._textElementView.viewHTMLElement.scrollHeight\n \n // Measure width by temporarily setting nowrap\n const whiteSpace = this._textElementView.style.whiteSpace\n this._textElementView.style.whiteSpace = \"nowrap\"\n \n const resultWidth = this._textElementView.viewHTMLElement.scrollWidth\n \n this._textElementView.style.whiteSpace = whiteSpace\n \n // Restore styles on the TEXT ELEMENT\n this._textElementView.style.height = height\n this._textElementView.style.width = width\n \n this._textElementView.style.left = left\n this._textElementView.style.right = right\n this._textElementView.style.bottom = bottom\n this._textElementView.style.top = top\n \n if (temporarilyInViewTree) {\n document.body.removeChild(this.viewHTMLElement)\n if (this.superview) {\n if (nodeAboveThisView) {\n this.superview.viewHTMLElement.insertBefore(this.viewHTMLElement, nodeAboveThisView)\n }\n else {\n this.superview.viewHTMLElement.appendChild(this.viewHTMLElement)\n }\n }\n }\n \n result.height = resultHeight\n result.width = resultWidth\n \n this._setCachedIntrinsicSize(cacheKey, result)\n \n return result\n }\n \n \n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -141,6 +141,7 @@ export declare class UIView extends UIObject {
141
141
  static _pageScale: number;
142
142
  _scale: number;
143
143
  isInternalScaling: boolean;
144
+ static _fontReadyHook: boolean;
144
145
  static resizeObserver: ResizeObserver;
145
146
  private _isMovable;
146
147
  makeNotMovable?: () => void;
@@ -381,6 +382,7 @@ export declare class UIView extends UIObject {
381
382
  _annotatePathOnViewHTMLElementsInSubtree(): void;
382
383
  _annotatePathOnViewHTMLElement(): void;
383
384
  wasAddedToViewTree(): void;
385
+ documentFontsDidLoad(): void;
384
386
  wasRemovedFromViewTree(): void;
385
387
  get isMemberOfViewTree(): boolean;
386
388
  get withAllSuperviews(): UIView[];
@@ -57,6 +57,7 @@ var import_UICoreExtensions = require("./UICoreExtensions");
57
57
  var import_UIObject = require("./UIObject");
58
58
  var import_UIPoint = require("./UIPoint");
59
59
  var import_UIRectangle = require("./UIRectangle");
60
+ var import_UITextMeasurement = require("./UITextMeasurement");
60
61
  var import_UIViewController = require("./UIViewController");
61
62
  if (!window.AutoLayout) {
62
63
  window.AutoLayout = import_UIObject.nil;
@@ -1382,6 +1383,8 @@ const _UIView = class extends import_UIObject.UIObject {
1382
1383
  this._annotatePathOnViewHTMLElement();
1383
1384
  }
1384
1385
  }
1386
+ documentFontsDidLoad() {
1387
+ }
1385
1388
  wasRemovedFromViewTree() {
1386
1389
  _UIView.resizeObserver.unobserve(this.viewHTMLElement);
1387
1390
  for (const [key, color] of import_UIColor.UIColor._registrationMap) {
@@ -2627,6 +2630,26 @@ let UIView = _UIView;
2627
2630
  UIView._UIViewIndex = -1;
2628
2631
  UIView._viewsToLayout = [];
2629
2632
  UIView._pageScale = 1;
2633
+ UIView._fontReadyHook = (() => {
2634
+ document.fonts.ready.then(() => {
2635
+ requestAnimationFrame(() => {
2636
+ var _a, _b;
2637
+ import_UITextMeasurement.UITextMeasurement.clearCaches();
2638
+ const rootView = (_b = (_a = import_UICore.UICore.main) == null ? void 0 : _a.rootViewController) == null ? void 0 : _b.view;
2639
+ if (!rootView) {
2640
+ return;
2641
+ }
2642
+ rootView.forEachViewInSubtree((view) => {
2643
+ view.clearIntrinsicSizeCache();
2644
+ view._frameCache = void 0;
2645
+ view.documentFontsDidLoad();
2646
+ view.setNeedsLayout();
2647
+ });
2648
+ _UIView.layoutViewsIfNeeded();
2649
+ });
2650
+ });
2651
+ return import_UIObject.YES;
2652
+ })();
2630
2653
  UIView.resizeObserver = new ResizeObserver((entries, observer) => {
2631
2654
  var _a;
2632
2655
  for (let i = 0; i < entries.length; i++) {