uicore-ts 1.1.227 → 1.1.232
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiledScripts/UILayoutCycleTracer.d.ts +80 -0
- package/compiledScripts/UILayoutCycleTracer.js +193 -0
- package/compiledScripts/UILayoutCycleTracer.js.map +7 -0
- package/compiledScripts/UITableView.js +21 -7
- package/compiledScripts/UITableView.js.map +2 -2
- package/compiledScripts/UITextMeasurement.d.ts +28 -61
- package/compiledScripts/UITextMeasurement.js +189 -169
- package/compiledScripts/UITextMeasurement.js.map +3 -3
- package/compiledScripts/UITextView.js +13 -15
- package/compiledScripts/UITextView.js.map +2 -2
- package/compiledScripts/UITimer.d.ts +1 -1
- package/compiledScripts/UITimer.js +18 -13
- package/compiledScripts/UITimer.js.map +2 -2
- package/compiledScripts/UIView.js +7 -0
- package/compiledScripts/UIView.js.map +2 -2
- package/package.json +9 -1
- package/scripts/UILayoutCycleTracer.ts +246 -0
- package/scripts/UITableView.ts +43 -24
- package/scripts/UITextMeasurement.ts +378 -551
- package/scripts/UITextView.ts +15 -15
- package/scripts/UITimer.ts +21 -43
- package/scripts/UIView.ts +10 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UILayoutCycleTracer
|
|
3
|
+
*
|
|
4
|
+
* A development-only utility that detects and reports layout cycles in the
|
|
5
|
+
* UIView layout system.
|
|
6
|
+
*
|
|
7
|
+
* A layout cycle occurs when layouting a view causes another setNeedsLayout()
|
|
8
|
+
* call on the same view (or an ancestor) within the same layout pass, causing
|
|
9
|
+
* the loop in layoutViewsIfNeeded() to iterate multiple times.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* UILayoutCycleTracer.enable() — start tracing
|
|
13
|
+
* UILayoutCycleTracer.disable() — stop tracing
|
|
14
|
+
* UILayoutCycleTracer.isEnabled — check current state
|
|
15
|
+
*
|
|
16
|
+
* When a cycle is detected, a detailed report is printed to the console
|
|
17
|
+
* including:
|
|
18
|
+
* - Which view was re-queued
|
|
19
|
+
* - Its full superview chain
|
|
20
|
+
* - The call stack at the point of the re-queue (setNeedsLayout call)
|
|
21
|
+
* - How many times the view has been laid out in this pass
|
|
22
|
+
*
|
|
23
|
+
* Integration:
|
|
24
|
+
* Call UILayoutCycleTracer.willBeginLayoutPass() at the start of
|
|
25
|
+
* layoutViewsIfNeeded(), UILayoutCycleTracer.didLayoutView(view) after each
|
|
26
|
+
* view is laid out, and UILayoutCycleTracer.viewDidCallSetNeedsLayout(view)
|
|
27
|
+
* from setNeedsLayout() while a pass is active.
|
|
28
|
+
*/
|
|
29
|
+
export declare class UILayoutCycleTracer {
|
|
30
|
+
static _isEnabled: boolean;
|
|
31
|
+
static _isPassActive: boolean;
|
|
32
|
+
static _layoutCountsThisPass: Map<any, number>;
|
|
33
|
+
static _setNeedsLayoutCallsThisPass: Map<any, {
|
|
34
|
+
count: number;
|
|
35
|
+
stacks: string[];
|
|
36
|
+
}>;
|
|
37
|
+
static _currentIteration: number;
|
|
38
|
+
static _totalReportsThisPass: number;
|
|
39
|
+
static reportThreshold: number;
|
|
40
|
+
static maxReportsPerPass: number;
|
|
41
|
+
static _noiseFramePrefixes: string[];
|
|
42
|
+
static get isEnabled(): boolean;
|
|
43
|
+
static enable(): void;
|
|
44
|
+
static disable(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Called at the very start of each layoutViewsIfNeeded() invocation.
|
|
47
|
+
*/
|
|
48
|
+
static willBeginLayoutPass(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Called after each iteration increment in the layoutViewsIfNeeded() while loop.
|
|
51
|
+
*/
|
|
52
|
+
static willBeginIteration(iteration: number): void;
|
|
53
|
+
/**
|
|
54
|
+
* Called after a view's layoutIfNeeded() completes.
|
|
55
|
+
*/
|
|
56
|
+
static didLayoutView(view: any): void;
|
|
57
|
+
/**
|
|
58
|
+
* Called from setNeedsLayout() when a view enters the queue.
|
|
59
|
+
* If a layout pass is currently active, this is a potential cycle.
|
|
60
|
+
*/
|
|
61
|
+
static viewDidCallSetNeedsLayout(view: any): void;
|
|
62
|
+
/**
|
|
63
|
+
* Called at the end of a layout pass.
|
|
64
|
+
*/
|
|
65
|
+
static didFinishLayoutPass(iterationCount: number): void;
|
|
66
|
+
/**
|
|
67
|
+
* Strips the "Error" header line and any leading framework/tracer noise
|
|
68
|
+
* frames from a raw Error.stack string, so the first frame shown is always
|
|
69
|
+
* the application code that triggered the re-queue.
|
|
70
|
+
*/
|
|
71
|
+
static _cleanStack(rawStack: string): string;
|
|
72
|
+
static _viewIdentifier(view: any): string;
|
|
73
|
+
static _superviewChain(view: any): string;
|
|
74
|
+
static _reportCycle(view: any, layoutCountThisPass: number, cleanStack: string): void;
|
|
75
|
+
}
|
|
76
|
+
declare global {
|
|
77
|
+
interface Window {
|
|
78
|
+
UILayoutCycleTracer?: typeof UILayoutCycleTracer;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var UILayoutCycleTracer_exports = {};
|
|
20
|
+
__export(UILayoutCycleTracer_exports, {
|
|
21
|
+
UILayoutCycleTracer: () => UILayoutCycleTracer
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(UILayoutCycleTracer_exports);
|
|
24
|
+
const _UILayoutCycleTracer = class {
|
|
25
|
+
static get isEnabled() {
|
|
26
|
+
return _UILayoutCycleTracer._isEnabled;
|
|
27
|
+
}
|
|
28
|
+
static enable() {
|
|
29
|
+
;
|
|
30
|
+
Error.stackTraceLimit = 100;
|
|
31
|
+
_UILayoutCycleTracer._isEnabled = true;
|
|
32
|
+
console.log(
|
|
33
|
+
"%c[UILayoutCycleTracer] Layout cycle tracing ENABLED (Error.stackTraceLimit = 100)",
|
|
34
|
+
"color: #4CAF50; font-weight: bold"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
static disable() {
|
|
38
|
+
;
|
|
39
|
+
Error.stackTraceLimit = 10;
|
|
40
|
+
_UILayoutCycleTracer._isEnabled = false;
|
|
41
|
+
console.log(
|
|
42
|
+
"%c[UILayoutCycleTracer] Layout cycle tracing DISABLED",
|
|
43
|
+
"color: #9E9E9E; font-weight: bold"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
static willBeginLayoutPass() {
|
|
47
|
+
if (!_UILayoutCycleTracer._isEnabled) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
_UILayoutCycleTracer._isPassActive = true;
|
|
51
|
+
_UILayoutCycleTracer._layoutCountsThisPass = /* @__PURE__ */ new Map();
|
|
52
|
+
_UILayoutCycleTracer._setNeedsLayoutCallsThisPass = /* @__PURE__ */ new Map();
|
|
53
|
+
_UILayoutCycleTracer._currentIteration = 0;
|
|
54
|
+
_UILayoutCycleTracer._totalReportsThisPass = 0;
|
|
55
|
+
}
|
|
56
|
+
static willBeginIteration(iteration) {
|
|
57
|
+
if (!_UILayoutCycleTracer._isEnabled) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
_UILayoutCycleTracer._currentIteration = iteration;
|
|
61
|
+
if (iteration > 0) {
|
|
62
|
+
console.warn(
|
|
63
|
+
`%c[UILayoutCycleTracer] Layout pass iteration ${iteration + 1} \u2014 views were re-queued during the previous iteration`,
|
|
64
|
+
"color: #FF9800; font-weight: bold"
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
static didLayoutView(view) {
|
|
69
|
+
var _a;
|
|
70
|
+
if (!_UILayoutCycleTracer._isEnabled || !_UILayoutCycleTracer._isPassActive) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const previous = (_a = _UILayoutCycleTracer._layoutCountsThisPass.get(view)) != null ? _a : 0;
|
|
74
|
+
_UILayoutCycleTracer._layoutCountsThisPass.set(view, previous + 1);
|
|
75
|
+
}
|
|
76
|
+
static viewDidCallSetNeedsLayout(view) {
|
|
77
|
+
var _a, _b;
|
|
78
|
+
if (!_UILayoutCycleTracer._isEnabled || !_UILayoutCycleTracer._isPassActive) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const layoutCount = (_a = _UILayoutCycleTracer._layoutCountsThisPass.get(view)) != null ? _a : 0;
|
|
82
|
+
if (layoutCount < _UILayoutCycleTracer.reportThreshold) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (_UILayoutCycleTracer._totalReportsThisPass >= _UILayoutCycleTracer.maxReportsPerPass) {
|
|
86
|
+
if (_UILayoutCycleTracer._totalReportsThisPass === _UILayoutCycleTracer.maxReportsPerPass) {
|
|
87
|
+
console.warn(
|
|
88
|
+
`%c[UILayoutCycleTracer] Maximum reports per pass (${_UILayoutCycleTracer.maxReportsPerPass}) reached. Further reports suppressed.`,
|
|
89
|
+
"color: #F44336"
|
|
90
|
+
);
|
|
91
|
+
_UILayoutCycleTracer._totalReportsThisPass++;
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
_UILayoutCycleTracer._totalReportsThisPass++;
|
|
96
|
+
const rawStack = (_b = new Error().stack) != null ? _b : "(stack unavailable)";
|
|
97
|
+
const cleanStack = _UILayoutCycleTracer._cleanStack(rawStack);
|
|
98
|
+
const existing = _UILayoutCycleTracer._setNeedsLayoutCallsThisPass.get(view);
|
|
99
|
+
if (existing) {
|
|
100
|
+
existing.count++;
|
|
101
|
+
existing.stacks.push(cleanStack);
|
|
102
|
+
} else {
|
|
103
|
+
_UILayoutCycleTracer._setNeedsLayoutCallsThisPass.set(view, { count: 1, stacks: [cleanStack] });
|
|
104
|
+
}
|
|
105
|
+
_UILayoutCycleTracer._reportCycle(view, layoutCount, cleanStack);
|
|
106
|
+
}
|
|
107
|
+
static didFinishLayoutPass(iterationCount) {
|
|
108
|
+
if (!_UILayoutCycleTracer._isEnabled) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
_UILayoutCycleTracer._isPassActive = false;
|
|
112
|
+
if (iterationCount > 1) {
|
|
113
|
+
console.warn(
|
|
114
|
+
`%c[UILayoutCycleTracer] Layout pass completed in ${iterationCount} iteration(s). ${_UILayoutCycleTracer._totalReportsThisPass} cycle event(s) recorded.`,
|
|
115
|
+
"color: #FF9800"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
static _cleanStack(rawStack) {
|
|
120
|
+
const lines = rawStack.split("\n");
|
|
121
|
+
let firstAppFrameIndex = 1;
|
|
122
|
+
for (let i = 1; i < lines.length; i++) {
|
|
123
|
+
const trimmed = lines[i].trim();
|
|
124
|
+
const isNoise = _UILayoutCycleTracer._noiseFramePrefixes.some(
|
|
125
|
+
(prefix) => trimmed.includes(prefix)
|
|
126
|
+
);
|
|
127
|
+
if (!isNoise) {
|
|
128
|
+
firstAppFrameIndex = i;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return lines.slice(firstAppFrameIndex).join("\n");
|
|
133
|
+
}
|
|
134
|
+
static _viewIdentifier(view) {
|
|
135
|
+
var _a, _b, _c, _d;
|
|
136
|
+
const className = (_b = (_a = view == null ? void 0 : view.constructor) == null ? void 0 : _a.name) != null ? _b : "UnknownView";
|
|
137
|
+
const elementID = (_d = (_c = view == null ? void 0 : view.elementID) != null ? _c : view == null ? void 0 : view._UIViewIndex) != null ? _d : "?";
|
|
138
|
+
return `${className}#${elementID}`;
|
|
139
|
+
}
|
|
140
|
+
static _superviewChain(view) {
|
|
141
|
+
const parts = [];
|
|
142
|
+
let current = view;
|
|
143
|
+
let depth = 0;
|
|
144
|
+
while (current && depth < 20) {
|
|
145
|
+
parts.push(_UILayoutCycleTracer._viewIdentifier(current));
|
|
146
|
+
current = current.superview;
|
|
147
|
+
depth++;
|
|
148
|
+
}
|
|
149
|
+
return parts.join(" \u2192 ");
|
|
150
|
+
}
|
|
151
|
+
static _reportCycle(view, layoutCountThisPass, cleanStack) {
|
|
152
|
+
const identifier = _UILayoutCycleTracer._viewIdentifier(view);
|
|
153
|
+
const chain = _UILayoutCycleTracer._superviewChain(view);
|
|
154
|
+
console.groupCollapsed(
|
|
155
|
+
`%c[UILayoutCycleTracer] \u26A0\uFE0F Layout cycle: ${identifier} re-queued after being laid out ${layoutCountThisPass}x in iteration ${_UILayoutCycleTracer._currentIteration + 1}`,
|
|
156
|
+
"color: #F44336; font-weight: bold"
|
|
157
|
+
);
|
|
158
|
+
console.log("%cView:", "font-weight: bold", view);
|
|
159
|
+
console.log("%cSuperview chain:", "font-weight: bold", chain);
|
|
160
|
+
console.log("%cLayout count this pass:", "font-weight: bold", layoutCountThisPass);
|
|
161
|
+
console.log("%cIteration:", "font-weight: bold", _UILayoutCycleTracer._currentIteration + 1);
|
|
162
|
+
console.log("%cCall stack (noise frames stripped):", "font-weight: bold");
|
|
163
|
+
cleanStack.split("\n").forEach((frame) => console.log(" " + frame.trim()));
|
|
164
|
+
console.groupEnd();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
let UILayoutCycleTracer = _UILayoutCycleTracer;
|
|
168
|
+
UILayoutCycleTracer._isEnabled = false;
|
|
169
|
+
UILayoutCycleTracer._isPassActive = false;
|
|
170
|
+
UILayoutCycleTracer._layoutCountsThisPass = /* @__PURE__ */ new Map();
|
|
171
|
+
UILayoutCycleTracer._setNeedsLayoutCallsThisPass = /* @__PURE__ */ new Map();
|
|
172
|
+
UILayoutCycleTracer._currentIteration = 0;
|
|
173
|
+
UILayoutCycleTracer._totalReportsThisPass = 0;
|
|
174
|
+
UILayoutCycleTracer.reportThreshold = 1;
|
|
175
|
+
UILayoutCycleTracer.maxReportsPerPass = 10;
|
|
176
|
+
UILayoutCycleTracer._noiseFramePrefixes = [
|
|
177
|
+
"UILayoutCycleTracer",
|
|
178
|
+
"UIView.setNeedsLayout",
|
|
179
|
+
"setNeedsLayout",
|
|
180
|
+
"UIView.didLayoutSubviews",
|
|
181
|
+
"didLayoutSubviews",
|
|
182
|
+
"UIView.layoutSubviews",
|
|
183
|
+
"UIView.layoutIfNeeded",
|
|
184
|
+
"layoutIfNeeded",
|
|
185
|
+
"UIView.layoutViewsIfNeeded",
|
|
186
|
+
"layoutViewsIfNeeded"
|
|
187
|
+
];
|
|
188
|
+
window.UILayoutCycleTracer = UILayoutCycleTracer;
|
|
189
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
190
|
+
0 && (module.exports = {
|
|
191
|
+
UILayoutCycleTracer
|
|
192
|
+
});
|
|
193
|
+
//# sourceMappingURL=UILayoutCycleTracer.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../scripts/UILayoutCycleTracer.ts"],
|
|
4
|
+
"sourcesContent": ["/// #if DEV\n\n/**\n * UILayoutCycleTracer\n *\n * A development-only utility that detects and reports layout cycles in the\n * UIView layout system.\n *\n * A layout cycle occurs when layouting a view causes another setNeedsLayout()\n * call on the same view (or an ancestor) within the same layout pass, causing\n * the loop in layoutViewsIfNeeded() to iterate multiple times.\n *\n * Usage:\n * UILayoutCycleTracer.enable() \u2014 start tracing\n * UILayoutCycleTracer.disable() \u2014 stop tracing\n * UILayoutCycleTracer.isEnabled \u2014 check current state\n *\n * When a cycle is detected, a detailed report is printed to the console\n * including:\n * - Which view was re-queued\n * - Its full superview chain\n * - The call stack at the point of the re-queue (setNeedsLayout call)\n * - How many times the view has been laid out in this pass\n *\n * Integration:\n * Call UILayoutCycleTracer.willBeginLayoutPass() at the start of\n * layoutViewsIfNeeded(), UILayoutCycleTracer.didLayoutView(view) after each\n * view is laid out, and UILayoutCycleTracer.viewDidCallSetNeedsLayout(view)\n * from setNeedsLayout() while a pass is active.\n */\nexport class UILayoutCycleTracer {\n \n static _isEnabled: boolean = false\n static _isPassActive: boolean = false\n static _layoutCountsThisPass: Map<any, number> = new Map()\n static _setNeedsLayoutCallsThisPass: Map<any, { count: number; stacks: string[] }> = new Map()\n static _currentIteration: number = 0\n static _totalReportsThisPass: number = 0\n \n // How many times a view must be re-queued before reporting.\n // 1 means: report the first time a view is re-queued after being laid out.\n static reportThreshold: number = 1\n \n // Maximum number of cycle reports per layout pass to avoid console flooding.\n static maxReportsPerPass: number = 10\n \n // Prefixes of stack frames that belong to the tracer or framework internals\n // and should be stripped from the top of the captured stack so that the\n // first visible frame is always application code.\n static _noiseFramePrefixes: string[] = [\n \"UILayoutCycleTracer\",\n \"UIView.setNeedsLayout\",\n \"setNeedsLayout\",\n \"UIView.didLayoutSubviews\",\n \"didLayoutSubviews\",\n \"UIView.layoutSubviews\",\n \"UIView.layoutIfNeeded\",\n \"layoutIfNeeded\",\n \"UIView.layoutViewsIfNeeded\",\n \"layoutViewsIfNeeded\",\n ]\n \n static get isEnabled(): boolean {\n return UILayoutCycleTracer._isEnabled\n }\n \n static enable() {\n // Maximise the V8 stack trace depth so long chains are fully visible.\n // The default is 10 which truncates most interesting call stacks.\n ;(Error as any).stackTraceLimit = 100\n UILayoutCycleTracer._isEnabled = true\n console.log(\n \"%c[UILayoutCycleTracer] Layout cycle tracing ENABLED (Error.stackTraceLimit = 100)\",\n \"color: #4CAF50; font-weight: bold\"\n )\n }\n \n static disable() {\n ;(Error as any).stackTraceLimit = 10\n UILayoutCycleTracer._isEnabled = false\n console.log(\n \"%c[UILayoutCycleTracer] Layout cycle tracing DISABLED\",\n \"color: #9E9E9E; font-weight: bold\"\n )\n }\n \n /**\n * Called at the very start of each layoutViewsIfNeeded() invocation.\n */\n static willBeginLayoutPass() {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._isPassActive = true\n UILayoutCycleTracer._layoutCountsThisPass = new Map()\n UILayoutCycleTracer._setNeedsLayoutCallsThisPass = new Map()\n UILayoutCycleTracer._currentIteration = 0\n UILayoutCycleTracer._totalReportsThisPass = 0\n }\n \n /**\n * Called after each iteration increment in the layoutViewsIfNeeded() while loop.\n */\n static willBeginIteration(iteration: number) {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._currentIteration = iteration\n if (iteration > 0) {\n console.warn(\n `%c[UILayoutCycleTracer] Layout pass iteration ${iteration + 1} \u2014 views were re-queued during the previous iteration`,\n \"color: #FF9800; font-weight: bold\"\n )\n }\n }\n \n /**\n * Called after a view's layoutIfNeeded() completes.\n */\n static didLayoutView(view: any) {\n if (!UILayoutCycleTracer._isEnabled || !UILayoutCycleTracer._isPassActive) { return }\n const previous = UILayoutCycleTracer._layoutCountsThisPass.get(view) ?? 0\n UILayoutCycleTracer._layoutCountsThisPass.set(view, previous + 1)\n }\n \n /**\n * Called from setNeedsLayout() when a view enters the queue.\n * If a layout pass is currently active, this is a potential cycle.\n */\n static viewDidCallSetNeedsLayout(view: any) {\n if (!UILayoutCycleTracer._isEnabled || !UILayoutCycleTracer._isPassActive) { return }\n \n // Only report if this view has already been laid out at least once this pass.\n const layoutCount = UILayoutCycleTracer._layoutCountsThisPass.get(view) ?? 0\n if (layoutCount < UILayoutCycleTracer.reportThreshold) { return }\n \n if (UILayoutCycleTracer._totalReportsThisPass >= UILayoutCycleTracer.maxReportsPerPass) {\n if (UILayoutCycleTracer._totalReportsThisPass === UILayoutCycleTracer.maxReportsPerPass) {\n console.warn(\n `%c[UILayoutCycleTracer] Maximum reports per pass (${UILayoutCycleTracer.maxReportsPerPass}) reached. Further reports suppressed.`,\n \"color: #F44336\"\n )\n UILayoutCycleTracer._totalReportsThisPass++\n }\n return\n }\n \n UILayoutCycleTracer._totalReportsThisPass++\n \n const rawStack = new Error().stack ?? \"(stack unavailable)\"\n const cleanStack = UILayoutCycleTracer._cleanStack(rawStack)\n \n const existing = UILayoutCycleTracer._setNeedsLayoutCallsThisPass.get(view)\n if (existing) {\n existing.count++\n existing.stacks.push(cleanStack)\n }\n else {\n UILayoutCycleTracer._setNeedsLayoutCallsThisPass.set(view, { count: 1, stacks: [cleanStack] })\n }\n \n UILayoutCycleTracer._reportCycle(view, layoutCount, cleanStack)\n }\n \n /**\n * Called at the end of a layout pass.\n */\n static didFinishLayoutPass(iterationCount: number) {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._isPassActive = false\n \n if (iterationCount > 1) {\n console.warn(\n `%c[UILayoutCycleTracer] Layout pass completed in ${iterationCount} iteration(s). ` +\n `${UILayoutCycleTracer._totalReportsThisPass} cycle event(s) recorded.`,\n \"color: #FF9800\"\n )\n }\n }\n \n /**\n * Strips the \"Error\" header line and any leading framework/tracer noise\n * frames from a raw Error.stack string, so the first frame shown is always\n * the application code that triggered the re-queue.\n */\n static _cleanStack(rawStack: string): string {\n const lines = rawStack.split(\"\\n\")\n \n // Find the first line that is NOT the \"Error\" header and NOT a noise frame.\n let firstAppFrameIndex = 1 // skip \"Error\" on line 0\n for (let i = 1; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n const isNoise = UILayoutCycleTracer._noiseFramePrefixes.some(prefix =>\n trimmed.includes(prefix)\n )\n if (!isNoise) {\n firstAppFrameIndex = i\n break\n }\n }\n \n return lines.slice(firstAppFrameIndex).join(\"\\n\")\n }\n \n static _viewIdentifier(view: any): string {\n const className = view?.constructor?.name ?? \"UnknownView\"\n const elementID = view?.elementID ?? view?._UIViewIndex ?? \"?\"\n return `${className}#${elementID}`\n }\n \n static _superviewChain(view: any): string {\n const parts: string[] = []\n let current = view\n let depth = 0\n while (current && depth < 20) {\n parts.push(UILayoutCycleTracer._viewIdentifier(current))\n current = current.superview\n depth++\n }\n return parts.join(\" \u2192 \")\n }\n \n static _reportCycle(view: any, layoutCountThisPass: number, cleanStack: string) {\n const identifier = UILayoutCycleTracer._viewIdentifier(view)\n const chain = UILayoutCycleTracer._superviewChain(view)\n \n console.groupCollapsed(\n `%c[UILayoutCycleTracer] \u26A0\uFE0F Layout cycle: ${identifier} re-queued after being laid out ${layoutCountThisPass}x in iteration ${UILayoutCycleTracer._currentIteration + 1}`,\n \"color: #F44336; font-weight: bold\"\n )\n console.log(\"%cView:\", \"font-weight: bold\", view)\n console.log(\"%cSuperview chain:\", \"font-weight: bold\", chain)\n console.log(\"%cLayout count this pass:\", \"font-weight: bold\", layoutCountThisPass)\n console.log(\"%cIteration:\", \"font-weight: bold\", UILayoutCycleTracer._currentIteration + 1)\n console.log(\"%cCall stack (noise frames stripped):\", \"font-weight: bold\")\n cleanStack.split(\"\\n\").forEach(frame => console.log(\" \" + frame.trim()))\n console.groupEnd()\n }\n \n}\n\nwindow.UILayoutCycleTracer = UILayoutCycleTracer\n\ndeclare global {\n interface Window {\n UILayoutCycleTracer?: typeof UILayoutCycleTracer\n }\n}\n\n/// #endif\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -106,6 +106,10 @@ class UITableView extends import_UINativeScrollView.UINativeScrollView {
|
|
|
106
106
|
if (!this.isMemberOfViewTree) {
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
+
const target = event.target;
|
|
110
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
109
113
|
const rowCount = this.numberOfRows();
|
|
110
114
|
const hasHeader = this._keyboardFocusedRowIndex !== void 0;
|
|
111
115
|
if (event.key === "ArrowDown") {
|
|
@@ -654,15 +658,19 @@ class UITableView extends import_UINativeScrollView.UINativeScrollView {
|
|
|
654
658
|
const el = this._keyboardListenerElement;
|
|
655
659
|
el.addEventListener("keydown", this._keydownHandler);
|
|
656
660
|
el.addEventListener("pointerdown", (event) => {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
661
|
+
const target = event.target;
|
|
662
|
+
if ((target == null ? void 0 : target.tagName) === "INPUT" || (target == null ? void 0 : target.tagName) === "TEXTAREA") {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
let walkedTarget = target;
|
|
666
|
+
while (walkedTarget && walkedTarget !== el) {
|
|
667
|
+
const viewObject = walkedTarget.UIViewObject;
|
|
660
668
|
if ((viewObject == null ? void 0 : viewObject._UITableViewRowIndex) !== void 0) {
|
|
661
669
|
el.focus({ preventScroll: true });
|
|
662
670
|
this._setKeyboardFocus(viewObject._UITableViewRowIndex, this._keyboardFocusedCellIndex);
|
|
663
671
|
return;
|
|
664
672
|
}
|
|
665
|
-
|
|
673
|
+
walkedTarget = walkedTarget.parentElement;
|
|
666
674
|
}
|
|
667
675
|
el.focus({ preventScroll: true });
|
|
668
676
|
});
|
|
@@ -709,8 +717,11 @@ class UITableView extends import_UINativeScrollView.UINativeScrollView {
|
|
|
709
717
|
}
|
|
710
718
|
_layoutAllRows(positions = this._rowPositions) {
|
|
711
719
|
const bounds = this.bounds;
|
|
712
|
-
this._visibleRows.sort(
|
|
713
|
-
|
|
720
|
+
const sortedRows = this._visibleRows.sort(
|
|
721
|
+
(rowA, rowB) => rowA._UITableViewRowIndex - rowB._UITableViewRowIndex
|
|
722
|
+
);
|
|
723
|
+
sortedRows.forEach((row, i) => {
|
|
724
|
+
var _a, _b, _c;
|
|
714
725
|
const frame = bounds.copy();
|
|
715
726
|
const positionObject = this._rowPositionWithIndex(row._UITableViewRowIndex, positions);
|
|
716
727
|
frame.min.y = positionObject.topY;
|
|
@@ -719,7 +730,10 @@ class UITableView extends import_UINativeScrollView.UINativeScrollView {
|
|
|
719
730
|
row.style.width = "" + (bounds.width - this.sidePadding * 2).integerValue + "px";
|
|
720
731
|
row.style.left = "" + this.sidePadding.integerValue + "px";
|
|
721
732
|
row.viewHTMLElement.setAttribute("aria-rowindex", String(((_a = row._UITableViewRowIndex) != null ? _a : 0) + 1));
|
|
722
|
-
|
|
733
|
+
const nextSiblingElement = (_c = (_b = sortedRows[i + 1]) == null ? void 0 : _b.viewHTMLElement) != null ? _c : this._fullHeightView.viewHTMLElement;
|
|
734
|
+
if (row.viewHTMLElement.nextSibling !== nextSiblingElement) {
|
|
735
|
+
this.viewHTMLElement.insertBefore(row.viewHTMLElement, nextSiblingElement);
|
|
736
|
+
}
|
|
723
737
|
});
|
|
724
738
|
const numberOfRows = this.numberOfRows();
|
|
725
739
|
const fullContentHeight = numberOfRows ? this._rowPositionWithIndex(numberOfRows - 1, positions).bottomY : 0;
|