gesso-core 0.2.0 → 0.3.0
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/CHANGELOG.md +66 -0
- package/dist/{CanvasSurface-5dU-U3qQ.js → CanvasSurface-ifHkg0-g.js} +972 -59
- package/dist/CanvasSurface-ifHkg0-g.js.map +1 -0
- package/dist/{index-DAdauEn9.d.ts → index-B4o3nKt2.d.ts} +786 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +748 -576
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +10 -1
- package/dist/testing.js +24 -1
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
- package/dist/CanvasSurface-5dU-U3qQ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as prepareInputSurface, $i as
|
|
1
|
+
import { $ as prepareInputSurface, $a as UiBinding, $i as defaultTextStyle, $n as EditableTextModel, $r as minmax, $t as DEFAULT_FONT_WEIGHT, A as accumulatedOffsetTo, Aa as UiVisualState, Ai as themeExtension, An as setLinkHover, Ar as validateStates, At as proportionalFontMetrics, B as parseFlexDirection, Ba as UiInputEvent, Bi as spacingEqual, Bn as nextCaretToggle, Br as transformsEqual, Bt as layoutTextLines, C as describeLength, Ca as UiNode, Ci as lightTheme, Cn as isNodeTabStop, Cr as UI_ROLES, Ct as selectionRectsIn, D as formatNumber, Da as resetOverrideWarnings, Di as defineThemeExtension, Dn as linkHoverOf, Dr as normalizeStates, Dt as spannedRunsFor, E as formatExplanation, Ea as overrideSources, Ei as withDensity, En as hasTextLinks, Er as isUiSemanticState, Et as ParagraphTextMeasurer, F as constraintsEqual, Fa as MOUSE_POINTER, Fi as raiseContrast, Fn as editorFor, Fr as pathValuesEqual, Ft as applyCanvasTextStyle, G as MainAxisAlignment, Ga as UiSelectionChangeEvent, Gi as boxShadowArraysEqual, Gn as caretRectFor, Gr as radialGradient, Gt as textRunBackgrounds, H as sizeGridTracks, Ha as UiPasteEvent, Hi as defaultShadows, Hn as resolveColorValue, Hr as gradientPaint, Ht as runFontString, I as tightenConstraints, Ia as UiBeforeInputEvent, Ii as relativeLuminance, In as editorOf, Ir as UiTransforms, It as buildFontString, J as parseMainAxisAlignment, Ja as UiWheelEvent, Ji as isShapeName, Jn as lineLimit, Jr as fr, Jt as clearFontStacks, K as parseAlignContent, Ka as UiTextChangeEvent, Ki as boxShadowsEqual, Kn as lineIndexAtY, Kr as validateGradient, Kt as textRunDecorations, L as MAX_MEASURES_PER_CHILD, La as UiEventType, Li as densityFactors, Ln as isEditableNode, Lr as parseTransform, Lt as createCanvasTextStyle, M as LayoutRecord, Ma as hasVisualState, Mi as withThemeExtension, Mn as CARET_WIDTH, Mr as validateSubgrid, Mt as detectEditingPlatform, N as Constraints, Na as visualState, Ni as contrastRatio, Nn as EDITOR_PROP, Nr as inputsEqual, Nt as isPrintable, O as labelNode, Oa as writeDeclaredProperty, Oi as hasThemeExtension, On as linkOf, Or as statesEqual, Ot as ELLIPSIS, P as clampSize, Pa as visualStatesEqual, Pi as highContrastColors, Pn as caretVisibleAt, Pr as paintValuesEqual, Pt as EditableLayout, Q as pointerDeviceOf, Qa as DirtyFlags, Qi as typographyEqual, Qn as selectionRects, Qr as isPercentLength, Qt as DEFAULT_FONT_SIZE, R as isLayoutProtocol, Ra as UiFocusEvent, Ri as defaultSpacing, Rn as isMultiline, Rr as transform, Rt as drawText, S as SCROLLBAR_LINGER_MS, Sa as UiEnvironment, Si as darkTheme, Sn as isNodeSelectable, Sr as UiProperties, St as paragraphGeometryFrom, T as formatConstraints, Ta as clearOverrideProperty, Ti as withContrast, Tn as clearLinkHover, Tr as isUiRole, Tt as CharacterCountTextMeasurer, U as AlignContent, Ua as UiPinchEvent, Ui as shadowsEqual, Un as isVideoSurface, Ur as gradientsEqual, Ut as spanOf, V as placeGridItems, Va as UiKeyboardEvent, Vi as spacingSteps, Vn as syncEditorValue, Vr as MAX_GRADIENT_STOPS, Vt as runCanvasStyleInto, W as CrossAxisAlignment, Wa as UiPointerEvent, Wi as boxShadow, Wn as videoFrameSize, Wr as linearGradient, Wt as textMeasureRequest, X as UiPlatformAdapter, Xa as wheelDeltaYOf, Xi as defaultTypography, Xn as offsetAtX, Xr as isFrLength, Xt as registerFontStack, Y as CanvasPlatformSurface, Ya as noKeyModifiers, Yi as shapesEqual, Yn as offsetAtPoint, Yr as isAutoLength, Yt as fontStackFor, Z as capturePointer, Za as UiNodeType, Zi as isTypographyRole, Zn as offsetForVerticalMove, Zr as isMinMaxTrack, Zt as DEFAULT_FONT_FAMILY, _ as LABEL_PADDING_X, _a as parseColor, _i as observeViewportInsets, _n as selectionRangeOf, _r as inheritedPropertyFlags, _t as scrollbarZoneAt, a as PaintPictureCache, aa as textContentOf, ai as borderRadiusCorners, an as normalizeTextAlign, ar as previousGraphemeStart, at as UiWheelController, b as LayoutEngine, ba as createEnvironmentKey, bi as containerBands, bn as isNodeHitTestable, br as propertyIsInherited, bt as offsetAtPointIn, c as EMPTY_RECORDING, ca as colorsEqualPalette, ci as borderRadiusValuesEqual, cn as DEFAULT_LINE_HEIGHT_FACTOR, cr as resolveBoolean, ct as UiPointerController, d as tracePathData, da as UiBasicColors, di as defineProperty, dn as matchRangesOf, dr as resolvePropertyByName, dt as SCROLLBAR_HOVER_ZONE, ea as editableSpansOf, ei as percent, en as DEFAULT_TEXT_COLOR, er as graphemeBoundaries, et as touchActionFor, f as ScaledImageCache, fa as colorToHex, fi as propertyValuesEqual, fn as setMatchRanges, fr as resolveString, ft as SCROLLBAR_INSET, g as paintsAfterChildren, ga as normalizeColor, gi as noInsets, gn as selectableTextOf, gr as getPropertyNames, gt as scrollbarThumbs, h as hasDecorationPhase, ha as colorsEqual, hi as insetsEqual, hn as selectableTextNodes, hr as findPropertyDefinition, ht as scrollbarThumb, i as traceRoundedRect, ia as spannedTextOf, ii as borderRadius, in as isPaintVisible, ir as nextWordEnd, it as UiPinchRecognizer, j as contentOffset, ja as defaultVisualState, ji as themeExtensionsEqual, jn as CARET_BLINK_MS, jr as UI_SUBGRID_AXES, jt as commandForKey, k as withOverrideSource, ka as writeOverrideProperty, ki as noThemeExtensions, kn as linkRunAtPointIn, kr as validateRole, kt as layoutParagraph, l as PaintRecorder, la as darkColors, li as normalizeBorderRadius, ln as TEXT_MATCHES_PROP, lr as resolveNumber, lt as UiFocusManager, m as decorationRect, ma as colorValuesEqual, mi as UiInsetRegistry, mn as clearSelectionRange, mr as closestPropertyName, mt as SCROLLBAR_THICKNESS, n as createCanvasSurface, na as resolvedSpansOf, ni as resolveLength, nn as computeObjectFitRect, nr as lineStartAt, nt as UiTouchScroller, o as paintPictures, oa as textSpansEqual, oi as borderRadiusEqual, on as normalizeVerticalAlign, or as previousWordStart, ot as isScrollContainer, p as decorationColor, pa as colorToRgba, pi as UiEnvironmentKeys, pn as TEXT_SELECTION_PROP, pr as UiGraph, pt as SCROLLBAR_MIN_THUMB, q as parseCrossAxisAlignment, qa as UiWheelDeltaMode, qi as defaultShapes, qn as lineIndexForOffset, qr as auto, qt as bumpFontStack, r as Canvas2DRenderer, ra as spanAtOffset, ri as UiBorderRadiuses, rn as createPaintState, rr as nextGraphemeEnd, rt as UiGestureRecognizer, s as PaintTarget, sa as textStylesEqual, si as borderRadiusIsZero, sn as resolvePaintState, sr as wordRangeAt, st as UiKeyboardController, t as CanvasSurface, ta as flattenTextSpans, ti as repeat, tn as colorToCss, tr as lineEndAt, tt as UiEditingController, u as replayPaint, ua as lightColors, ui as uniformBorderRadius, un as clearMatchRanges, ur as resolveProperty, ut as UiHitTester, v as drawOverlayShapes, va as rgb8, vi as UiContainerSizeSource, vn as setSelectionRange, vr as inheritedPropertyNames, vt as UiInputDispatcher, w as describeOverrides, wa as DirtyNodeSet, wi as themesEqual, wn as resolveCursor, wr as UI_SEMANTIC_STATES, wt as wordRangeIn, x as SCROLLBAR_FADE_MS, xa as findEnvironmentKey, xi as unknownContainerSize, xn as isNodeInert, xr as propertyValuesEqualByName, xt as paragraphGeometry, y as labelOrigin, ya as rgba, yi as bandOf, yn as isNodeFocusable, yr as propertyEffects, yt as hasDrawnText, z as FlexDirection, za as UiGestureEvent, zi as scaleSpacing, zn as isReadOnly, zr as transformIsIdentity, zt as fillTextRects } from "./CanvasSurface-ifHkg0-g.js";
|
|
2
2
|
import { BehaviorSubject, EMPTY, Subject, combineLatest, distinctUntilChanged, isObservable as isObservable$1, map, of } from "rxjs";
|
|
3
3
|
//#region src/animation/UiEasing.ts
|
|
4
4
|
/** Progress equals time. What a spinner and a sweep want. */
|
|
@@ -1425,9 +1425,11 @@ const EVENT_PROPS = {
|
|
|
1425
1425
|
onKeyDown: "keydown",
|
|
1426
1426
|
onKeyUp: "keyup",
|
|
1427
1427
|
onFocus: "focus",
|
|
1428
|
+
onPaste: "paste",
|
|
1428
1429
|
onBlur: "blur",
|
|
1429
1430
|
onBeforeInput: "beforeinput",
|
|
1430
1431
|
onInput: "input",
|
|
1432
|
+
onSelectionChange: "selectionchange",
|
|
1431
1433
|
onClick: "click",
|
|
1432
1434
|
onLongPress: "longpress",
|
|
1433
1435
|
onDragStart: "dragstart",
|
|
@@ -2883,7 +2885,7 @@ var UiVirtualWindow = class {
|
|
|
2883
2885
|
const total = this.totalExtent();
|
|
2884
2886
|
const header = viewport.lead ?? 0;
|
|
2885
2887
|
this.checkViewport(viewport.extent, total + header);
|
|
2886
|
-
const scrolled = clamp$
|
|
2888
|
+
const scrolled = clamp$2(viewport.scroll + scrollAdjust, 0, Math.max(0, total + header - viewport.extent));
|
|
2887
2889
|
const scroll = Math.max(0, scrolled - header);
|
|
2888
2890
|
let first = 0;
|
|
2889
2891
|
let last = -1;
|
|
@@ -3054,7 +3056,7 @@ function markRow(row, props, subject) {
|
|
|
3054
3056
|
}
|
|
3055
3057
|
};
|
|
3056
3058
|
}
|
|
3057
|
-
function clamp$
|
|
3059
|
+
function clamp$2(value, min, max) {
|
|
3058
3060
|
return Math.min(Math.max(value, min), max);
|
|
3059
3061
|
}
|
|
3060
3062
|
function assertCount(count) {
|
|
@@ -3074,10 +3076,10 @@ function assertCount(count) {
|
|
|
3074
3076
|
const lazySource = defineModifier({
|
|
3075
3077
|
name: "lazySource",
|
|
3076
3078
|
attach(host, args) {
|
|
3077
|
-
follow$
|
|
3079
|
+
follow$2(host, args.count, (value) => args.window.setCount(value));
|
|
3078
3080
|
if (args.revision !== void 0) {
|
|
3079
3081
|
let first = true;
|
|
3080
|
-
follow$
|
|
3082
|
+
follow$2(host, args.revision, () => {
|
|
3081
3083
|
if (first) {
|
|
3082
3084
|
first = false;
|
|
3083
3085
|
return;
|
|
@@ -3087,7 +3089,7 @@ const lazySource = defineModifier({
|
|
|
3087
3089
|
}
|
|
3088
3090
|
}
|
|
3089
3091
|
});
|
|
3090
|
-
function follow$
|
|
3092
|
+
function follow$2(host, source, apply) {
|
|
3091
3093
|
if (!isObservable$1(source)) {
|
|
3092
3094
|
apply(source);
|
|
3093
3095
|
return;
|
|
@@ -3287,120 +3289,609 @@ function Responsive(props, build) {
|
|
|
3287
3289
|
}, [children]);
|
|
3288
3290
|
}
|
|
3289
3291
|
//#endregion
|
|
3290
|
-
//#region src/
|
|
3291
|
-
/**
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
}
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
*
|
|
3303
|
-
*
|
|
3304
|
-
*
|
|
3305
|
-
*
|
|
3306
|
-
*
|
|
3307
|
-
*
|
|
3308
|
-
*
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3292
|
+
//#region src/composition/UiVirtualSheet.ts
|
|
3293
|
+
/** The sheet sits on its scroll container under this property. */
|
|
3294
|
+
const VIRTUAL_SHEET_PROP = "virtualSheet";
|
|
3295
|
+
const EMPTY_RANGE = {
|
|
3296
|
+
firstRow: 0,
|
|
3297
|
+
lastRow: -1,
|
|
3298
|
+
firstColumn: 0,
|
|
3299
|
+
lastColumn: -1
|
|
3300
|
+
};
|
|
3301
|
+
var UiVirtualSheet = class {
|
|
3302
|
+
children$ = new BehaviorSubject([]);
|
|
3303
|
+
/**
|
|
3304
|
+
* The range now mounted, for whoever has to fetch the values in it.
|
|
3305
|
+
*
|
|
3306
|
+
* This is the whole reason the window is reachable from outside: the
|
|
3307
|
+
* render thread does not hold the cells, so the range it settles on
|
|
3308
|
+
* each frame is what it asks the application thread for. Emits only
|
|
3309
|
+
* when the range changes, so binding a command to it does not send
|
|
3310
|
+
* one per frame.
|
|
3311
|
+
*/
|
|
3312
|
+
range$ = new BehaviorSubject(EMPTY_RANGE);
|
|
3313
|
+
rowCount;
|
|
3314
|
+
columnCount;
|
|
3315
|
+
rowOverscan;
|
|
3316
|
+
columnOverscan;
|
|
3317
|
+
rowHeight;
|
|
3318
|
+
gutterWidth;
|
|
3319
|
+
headerHeight;
|
|
3320
|
+
frozenRows;
|
|
3321
|
+
frozenColumns;
|
|
3322
|
+
renderRow;
|
|
3323
|
+
renderHeader;
|
|
3324
|
+
extendRange;
|
|
3325
|
+
/** One width per column, or undefined when every column is the same. */
|
|
3326
|
+
widths;
|
|
3327
|
+
/** `offsets[c]` is where column `c` starts, measured past the gutter. */
|
|
3328
|
+
offsets = [];
|
|
3329
|
+
uniformWidth = 0;
|
|
3330
|
+
/**
|
|
3331
|
+
* The rows that are not `rowHeight` tall, in row order.
|
|
3332
|
+
*
|
|
3333
|
+
* Three parallel arrays rather than a map of objects: this is
|
|
3334
|
+
* searched on every hit test and on every frame that scrolls, and
|
|
3335
|
+
* the arrays are read in step.
|
|
3336
|
+
*/
|
|
3337
|
+
exceptionRows = [];
|
|
3338
|
+
exceptionHeights = [];
|
|
3339
|
+
/** `exceptionStarts[i]` is where `exceptionRows[i]` starts, past the header. */
|
|
3340
|
+
exceptionStarts = [];
|
|
3341
|
+
range = EMPTY_RANGE;
|
|
3342
|
+
viewport;
|
|
3343
|
+
/** Rows built for the current column range, so a vertical scroll rebuilds one row. */
|
|
3344
|
+
mountedRows = /* @__PURE__ */ new Map();
|
|
3345
|
+
/** The frozen rows, kept separately because they never leave. */
|
|
3346
|
+
frozenMounted = /* @__PURE__ */ new Map();
|
|
3347
|
+
mountedHeader;
|
|
3348
|
+
constructor(options, renderRow, renderHeader) {
|
|
3349
|
+
this.rowCount = typeof options.rowCount === "number" ? options.rowCount : 0;
|
|
3350
|
+
this.columnCount = typeof options.columnCount === "number" ? options.columnCount : 0;
|
|
3351
|
+
this.rowHeight = positive("rowHeight", options.rowHeight);
|
|
3352
|
+
this.adoptHeights(options.rowHeights);
|
|
3353
|
+
this.gutterWidth = Math.max(0, options.gutterWidth ?? 0);
|
|
3354
|
+
this.headerHeight = Math.max(0, options.headerHeight ?? 0);
|
|
3355
|
+
this.frozenRows = Math.max(0, Math.floor(options.frozenRows ?? 0));
|
|
3356
|
+
this.frozenColumns = Math.max(0, Math.floor(options.frozenColumns ?? 0));
|
|
3357
|
+
this.rowOverscan = typeof options.rowOverscan === "number" ? options.rowOverscan : 3;
|
|
3358
|
+
this.columnOverscan = typeof options.columnOverscan === "number" ? options.columnOverscan : 2;
|
|
3359
|
+
this.renderRow = renderRow;
|
|
3360
|
+
this.renderHeader = renderHeader;
|
|
3361
|
+
this.extendRange = options.extendRange;
|
|
3362
|
+
this.adoptWidths(options.columnWidth);
|
|
3363
|
+
this.viewport = {
|
|
3364
|
+
scrollX: 0,
|
|
3365
|
+
scrollY: 0,
|
|
3366
|
+
width: options.initialViewport?.width ?? 800,
|
|
3367
|
+
height: options.initialViewport?.height ?? 600
|
|
3368
|
+
};
|
|
3369
|
+
this.recompute();
|
|
3370
|
+
}
|
|
3371
|
+
/**
|
|
3372
|
+
* New column widths, as a drag on a header edge produces.
|
|
3373
|
+
*
|
|
3374
|
+
* The prefix sum is rebuilt whole. It is one pass over the columns
|
|
3375
|
+
* and it happens once per frame of a drag at most, against a
|
|
3376
|
+
* per-column offset that would otherwise have to be corrected
|
|
3377
|
+
* everywhere downstream of the column that moved.
|
|
3378
|
+
*/
|
|
3379
|
+
setColumnWidths(widths) {
|
|
3380
|
+
this.adoptWidths(widths);
|
|
3381
|
+
this.invalidate();
|
|
3382
|
+
}
|
|
3383
|
+
adoptWidths(widths) {
|
|
3384
|
+
if (typeof widths === "number") {
|
|
3385
|
+
this.uniformWidth = positive("columnWidth", widths);
|
|
3386
|
+
this.widths = void 0;
|
|
3387
|
+
this.offsets = [];
|
|
3388
|
+
return;
|
|
3335
3389
|
}
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3390
|
+
const owned = widths.slice();
|
|
3391
|
+
for (const width of owned) if (!(width >= 0)) throw new Error(`LazySheet column widths must not be negative, got ${String(width)}.`);
|
|
3392
|
+
this.widths = owned;
|
|
3393
|
+
this.offsets = Array.from({ length: owned.length + 1 });
|
|
3394
|
+
let running = 0;
|
|
3395
|
+
for (let column = 0; column < owned.length; column++) {
|
|
3396
|
+
this.offsets[column] = running;
|
|
3397
|
+
running += owned[column];
|
|
3398
|
+
}
|
|
3399
|
+
this.offsets[owned.length] = running;
|
|
3400
|
+
}
|
|
3401
|
+
/**
|
|
3402
|
+
* New row heights, as hiding a row or autofitting one produces.
|
|
3403
|
+
*
|
|
3404
|
+
* The exceptions are rebuilt whole, which is one pass over *them*
|
|
3405
|
+
* and not over the rows — the whole reason they are sparse. A
|
|
3406
|
+
* height of zero is a hidden row and is allowed; a negative one is
|
|
3407
|
+
* not.
|
|
3408
|
+
*/
|
|
3409
|
+
setRowHeights(heights) {
|
|
3410
|
+
this.adoptHeights(heights);
|
|
3411
|
+
this.invalidate();
|
|
3412
|
+
}
|
|
3413
|
+
/** The height every row has unless it is an exception. */
|
|
3414
|
+
setRowHeight(height) {
|
|
3415
|
+
const next = positive("rowHeight", height);
|
|
3416
|
+
if (next === this.rowHeight) return;
|
|
3417
|
+
this.rowHeight = next;
|
|
3418
|
+
this.rebuildExceptionStarts();
|
|
3419
|
+
this.invalidate();
|
|
3420
|
+
}
|
|
3421
|
+
adoptHeights(heights) {
|
|
3422
|
+
this.exceptionRows = [];
|
|
3423
|
+
this.exceptionHeights = [];
|
|
3424
|
+
if (heights !== void 0) {
|
|
3425
|
+
const rows = [...heights.keys()].sort((a, b) => a - b);
|
|
3426
|
+
for (const row of rows) {
|
|
3427
|
+
const height = heights.get(row) ?? 0;
|
|
3428
|
+
if (!(height >= 0)) throw new Error(`LazySheet row heights must not be negative, got ${String(height)}.`);
|
|
3429
|
+
if (height !== this.rowHeight) {
|
|
3430
|
+
this.exceptionRows.push(row);
|
|
3431
|
+
this.exceptionHeights.push(height);
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
this.rebuildExceptionStarts();
|
|
3436
|
+
}
|
|
3437
|
+
/**
|
|
3438
|
+
* Where each exception begins, and what they cost in total.
|
|
3439
|
+
*
|
|
3440
|
+
* `start` is the row's own offset past the header, so the inverse —
|
|
3441
|
+
* `rowAt` — can binary search it directly rather than reconstructing
|
|
3442
|
+
* it per probe.
|
|
3443
|
+
*/
|
|
3444
|
+
rebuildExceptionStarts() {
|
|
3445
|
+
this.exceptionStarts = Array.from({ length: this.exceptionRows.length });
|
|
3446
|
+
let extra = 0;
|
|
3447
|
+
for (let index = 0; index < this.exceptionRows.length; index++) {
|
|
3448
|
+
this.exceptionStarts[index] = this.exceptionRows[index] * this.rowHeight + extra;
|
|
3449
|
+
extra += this.exceptionHeights[index] - this.rowHeight;
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
/** The index of the last exception starting at or before an offset, or -1. */
|
|
3453
|
+
exceptionAt(offset) {
|
|
3454
|
+
let low = 0;
|
|
3455
|
+
let high = this.exceptionStarts.length - 1;
|
|
3456
|
+
let found = -1;
|
|
3457
|
+
while (low <= high) {
|
|
3458
|
+
const mid = low + high >> 1;
|
|
3459
|
+
if (this.exceptionStarts[mid] <= offset) {
|
|
3460
|
+
found = mid;
|
|
3461
|
+
low = mid + 1;
|
|
3462
|
+
} else high = mid - 1;
|
|
3463
|
+
}
|
|
3464
|
+
return found;
|
|
3465
|
+
}
|
|
3466
|
+
/** How many exceptions come before a row, as an index into them. */
|
|
3467
|
+
exceptionsBefore(row) {
|
|
3468
|
+
let low = 0;
|
|
3469
|
+
let high = this.exceptionRows.length;
|
|
3470
|
+
while (low < high) {
|
|
3471
|
+
const mid = low + high >> 1;
|
|
3472
|
+
if (this.exceptionRows[mid] < row) low = mid + 1;
|
|
3473
|
+
else high = mid;
|
|
3351
3474
|
}
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3475
|
+
return low;
|
|
3476
|
+
}
|
|
3477
|
+
/** The height of one row. Zero when it is hidden. */
|
|
3478
|
+
rowHeightOf(row) {
|
|
3479
|
+
if (this.exceptionRows.length === 0) return this.rowHeight;
|
|
3480
|
+
const index = this.exceptionsBefore(row);
|
|
3481
|
+
return this.exceptionRows[index] === row ? this.exceptionHeights[index] : this.rowHeight;
|
|
3482
|
+
}
|
|
3483
|
+
/** Where a row starts, measured past the header. */
|
|
3484
|
+
rowOffsetOf(row) {
|
|
3485
|
+
const clamped = clamp$1(row, 0, this.rowCount);
|
|
3486
|
+
return clamped * this.rowHeight + this.extraWithin(clamped);
|
|
3487
|
+
}
|
|
3488
|
+
/** Total height of the rows, not counting the header. */
|
|
3489
|
+
get rowsHeight() {
|
|
3490
|
+
return this.rowCount * this.rowHeight + this.extraWithin(this.rowCount);
|
|
3491
|
+
}
|
|
3492
|
+
/**
|
|
3493
|
+
* What the exceptions before `row` add to or take off the offset.
|
|
3494
|
+
*
|
|
3495
|
+
* Read off the last exception before it rather than summed: the
|
|
3496
|
+
* start of an exception already carries everything before it, so
|
|
3497
|
+
* its end minus where it *would* have ended is the running total.
|
|
3498
|
+
*/
|
|
3499
|
+
extraWithin(row) {
|
|
3500
|
+
const before = this.exceptionsBefore(row);
|
|
3501
|
+
if (before === 0) return 0;
|
|
3502
|
+
const index = before - 1;
|
|
3503
|
+
return this.exceptionStarts[index] + this.exceptionHeights[index] - (this.exceptionRows[index] + 1) * this.rowHeight;
|
|
3504
|
+
}
|
|
3505
|
+
/** The width of one column. */
|
|
3506
|
+
widthOf(column) {
|
|
3507
|
+
if (this.widths === void 0) return this.uniformWidth;
|
|
3508
|
+
return this.widths[column] ?? 0;
|
|
3509
|
+
}
|
|
3510
|
+
/** Where a column starts, measured past the gutter. */
|
|
3511
|
+
offsetOf(column) {
|
|
3512
|
+
if (this.widths === void 0) return column * this.uniformWidth;
|
|
3513
|
+
const clamped = clamp$1(column, 0, this.offsets.length - 1);
|
|
3514
|
+
return this.offsets[clamped] ?? 0;
|
|
3515
|
+
}
|
|
3516
|
+
/** Total width of the columns, not counting the gutter. */
|
|
3517
|
+
get columnsWidth() {
|
|
3518
|
+
return this.widths === void 0 ? this.columnCount * this.uniformWidth : this.offsets[this.widths.length] ?? 0;
|
|
3519
|
+
}
|
|
3520
|
+
get contentWidth() {
|
|
3521
|
+
return this.gutterWidth + this.columnsWidth;
|
|
3522
|
+
}
|
|
3523
|
+
/** Where the scrolled columns begin, past the gutter and the frozen ones. */
|
|
3524
|
+
get frozenWidth() {
|
|
3525
|
+
return this.offsetOf(this.frozenColumns);
|
|
3526
|
+
}
|
|
3527
|
+
get contentHeight() {
|
|
3528
|
+
return this.headerHeight + this.rowsHeight;
|
|
3529
|
+
}
|
|
3530
|
+
/**
|
|
3531
|
+
* The cell under a point in the scroll container's own coordinates.
|
|
3532
|
+
*
|
|
3533
|
+
* The window already knows where everything is and how far it is
|
|
3534
|
+
* scrolled, which is the whole of this: a drag towards a cell — a
|
|
3535
|
+
* fill handle, a selection being stretched — is usually heading for
|
|
3536
|
+
* one that has not been mounted, so there is no node to hit-test and
|
|
3537
|
+
* arithmetic is the only answer available.
|
|
3538
|
+
*/
|
|
3539
|
+
cellAt(x, y) {
|
|
3540
|
+
return {
|
|
3541
|
+
row: this.rowAt(this.viewport.scrollY + y),
|
|
3542
|
+
column: this.columnAt(this.viewport.scrollX + x)
|
|
3357
3543
|
};
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3544
|
+
}
|
|
3545
|
+
/**
|
|
3546
|
+
* The row at a content offset, clamped to the sheet.
|
|
3547
|
+
*
|
|
3548
|
+
* A division when every row is the default height — the common case
|
|
3549
|
+
* by a very long way — and otherwise a binary search over the
|
|
3550
|
+
* exceptions followed by a division inside the uniform run that
|
|
3551
|
+
* follows the one it lands in.
|
|
3552
|
+
*
|
|
3553
|
+
* A hidden row occupies no offset at all, so the offset where it
|
|
3554
|
+
* *would* be belongs to the row after it. That falls out of the
|
|
3555
|
+
* arithmetic rather than being special-cased: a zero-height
|
|
3556
|
+
* exception starts and ends at the same place, so the point is
|
|
3557
|
+
* never inside it.
|
|
3558
|
+
*/
|
|
3559
|
+
rowAt(offset) {
|
|
3560
|
+
const last = Math.max(0, this.rowCount - 1);
|
|
3561
|
+
const past = offset - this.headerHeight;
|
|
3562
|
+
if (this.exceptionRows.length === 0) return clamp$1(Math.floor(past / this.rowHeight), 0, last);
|
|
3563
|
+
if (past <= 0) return 0;
|
|
3564
|
+
const index = this.exceptionAt(past);
|
|
3565
|
+
if (index === -1) return clamp$1(Math.floor(past / this.rowHeight), 0, last);
|
|
3566
|
+
const start = this.exceptionStarts[index];
|
|
3567
|
+
const height = this.exceptionHeights[index];
|
|
3568
|
+
if (past < start + height) return clamp$1(this.exceptionRows[index], 0, last);
|
|
3569
|
+
return clamp$1(this.exceptionRows[index] + 1 + Math.floor((past - start - height) / this.rowHeight), 0, last);
|
|
3570
|
+
}
|
|
3571
|
+
/**
|
|
3572
|
+
* The column at a content offset, clamped to the sheet.
|
|
3573
|
+
*
|
|
3574
|
+
* A binary search over the prefix sum when the columns differ, and
|
|
3575
|
+
* a division when they do not — the uniform case is the common one
|
|
3576
|
+
* and should not pay for the general one.
|
|
3577
|
+
*/
|
|
3578
|
+
columnAt(offset) {
|
|
3579
|
+
const last = Math.max(0, this.columnCount - 1);
|
|
3580
|
+
const past = offset - this.gutterWidth;
|
|
3581
|
+
if (this.widths === void 0) return clamp$1(Math.floor(past / this.uniformWidth), 0, last);
|
|
3582
|
+
if (past <= 0) return 0;
|
|
3583
|
+
if (past >= this.columnsWidth) return last;
|
|
3584
|
+
let low = 0;
|
|
3585
|
+
let high = last;
|
|
3586
|
+
while (low < high) {
|
|
3587
|
+
const mid = low + high + 1 >> 1;
|
|
3588
|
+
if (this.offsets[mid] <= past) low = mid;
|
|
3589
|
+
else high = mid - 1;
|
|
3590
|
+
}
|
|
3591
|
+
return low;
|
|
3592
|
+
}
|
|
3593
|
+
setRowCount(count) {
|
|
3594
|
+
if (count === this.rowCount) return;
|
|
3595
|
+
this.rowCount = Math.max(0, Math.floor(count));
|
|
3596
|
+
this.invalidate();
|
|
3597
|
+
}
|
|
3598
|
+
setColumnCount(count) {
|
|
3599
|
+
if (count === this.columnCount) return;
|
|
3600
|
+
this.columnCount = Math.max(0, Math.floor(count));
|
|
3601
|
+
this.invalidate();
|
|
3602
|
+
}
|
|
3603
|
+
/**
|
|
3604
|
+
* The bands, which the spike tunes from the screen.
|
|
3605
|
+
*
|
|
3606
|
+
* A band is the only defence against the round trip to the
|
|
3607
|
+
* application thread: rows inside it already hold their values when
|
|
3608
|
+
* the scroll reaches them. Widening it costs nodes on every frame
|
|
3609
|
+
* and cells on every publish, so what it should be is a measurement,
|
|
3610
|
+
* not a default — hence setters rather than a constant.
|
|
3611
|
+
*/
|
|
3612
|
+
setOverscan(rows, columns) {
|
|
3613
|
+
const nextRows = Math.max(0, Math.floor(rows));
|
|
3614
|
+
const nextColumns = Math.max(0, Math.floor(columns));
|
|
3615
|
+
if (nextRows === this.rowOverscan && nextColumns === this.columnOverscan) return;
|
|
3616
|
+
this.rowOverscan = nextRows;
|
|
3617
|
+
this.columnOverscan = nextColumns;
|
|
3618
|
+
this.invalidate();
|
|
3619
|
+
}
|
|
3620
|
+
/**
|
|
3621
|
+
* How many rows and columns stay put while the rest scrolls.
|
|
3622
|
+
*
|
|
3623
|
+
* Freezing changes which rows are in the scrolled window, so every
|
|
3624
|
+
* mounted row is rebuilt — a row that was scrolling is now frozen
|
|
3625
|
+
* or the other way about.
|
|
3626
|
+
*/
|
|
3627
|
+
setFrozen(rows, columns) {
|
|
3628
|
+
const nextRows = Math.max(0, Math.floor(rows));
|
|
3629
|
+
const nextColumns = Math.max(0, Math.floor(columns));
|
|
3630
|
+
if (nextRows === this.frozenRows && nextColumns === this.frozenColumns) return;
|
|
3631
|
+
this.frozenRows = nextRows;
|
|
3632
|
+
this.frozenColumns = nextColumns;
|
|
3633
|
+
this.invalidate();
|
|
3634
|
+
}
|
|
3635
|
+
/** Drops every mounted row and rebuilds against the last viewport. */
|
|
3636
|
+
invalidate() {
|
|
3637
|
+
this.mountedRows.clear();
|
|
3638
|
+
this.frozenMounted.clear();
|
|
3639
|
+
this.mountedHeader = void 0;
|
|
3640
|
+
this.range = EMPTY_RANGE;
|
|
3641
|
+
this.recompute();
|
|
3642
|
+
}
|
|
3643
|
+
/**
|
|
3644
|
+
* One step, run by the host each frame before layout.
|
|
3645
|
+
*
|
|
3646
|
+
* Nothing is measured and nothing is anchored: the offsets are exact,
|
|
3647
|
+
* so the rows a scroll reveals are placed correctly on the frame
|
|
3648
|
+
* that reveals them rather than corrected on the one after.
|
|
3649
|
+
*/
|
|
3650
|
+
update(viewport) {
|
|
3651
|
+
this.viewport = viewport;
|
|
3652
|
+
this.recompute();
|
|
3653
|
+
}
|
|
3654
|
+
recompute() {
|
|
3655
|
+
const next = this.windowFor(this.viewport);
|
|
3656
|
+
if (sameRange(next, this.range)) return;
|
|
3657
|
+
if (next.firstColumn !== this.range.firstColumn || next.lastColumn !== this.range.lastColumn) {
|
|
3658
|
+
this.mountedRows.clear();
|
|
3659
|
+
this.frozenMounted.clear();
|
|
3660
|
+
this.mountedHeader = void 0;
|
|
3661
|
+
}
|
|
3662
|
+
this.range = next;
|
|
3663
|
+
this.children$.next(this.buildChildren());
|
|
3664
|
+
this.range$.next(next);
|
|
3665
|
+
}
|
|
3666
|
+
/**
|
|
3667
|
+
* The window, in content coordinates.
|
|
3668
|
+
*
|
|
3669
|
+
* The header and the gutter are *content*: they occupy the first
|
|
3670
|
+
* `headerHeight` pixels and the first `gutterWidth`, and `rowAt` and
|
|
3671
|
+
* `columnAt` already subtract them. So the range comes out slightly
|
|
3672
|
+
* generous at the near edges — the rows a header is covering are
|
|
3673
|
+
* mounted, and the columns behind the gutter are too — which is
|
|
3674
|
+
* correct rather than wasteful, since a sticky strip is drawn over
|
|
3675
|
+
* cells that are really there.
|
|
3676
|
+
*/
|
|
3677
|
+
windowFor(viewport) {
|
|
3678
|
+
if (this.rowCount === 0 || this.columnCount === 0) return EMPTY_RANGE;
|
|
3679
|
+
const firstRow = Math.max(this.frozenRows, this.rowAt(viewport.scrollY) - this.rowOverscan);
|
|
3680
|
+
const lastRow = Math.min(this.rowCount - 1, this.rowAt(viewport.scrollY + Math.max(0, viewport.height)) + this.rowOverscan);
|
|
3681
|
+
const firstColumn = Math.max(this.frozenColumns, this.columnAt(viewport.scrollX) - this.columnOverscan);
|
|
3682
|
+
const lastColumn = Math.min(this.columnCount - 1, this.columnAt(viewport.scrollX + Math.max(0, viewport.width)) + this.columnOverscan);
|
|
3683
|
+
const range = {
|
|
3684
|
+
firstRow,
|
|
3685
|
+
lastRow,
|
|
3686
|
+
firstColumn,
|
|
3687
|
+
lastColumn
|
|
3363
3688
|
};
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
kind: "delete",
|
|
3372
|
-
unit: word ? "word" : "grapheme",
|
|
3373
|
-
direction: -1
|
|
3374
|
-
};
|
|
3375
|
-
case "Delete": return {
|
|
3376
|
-
kind: "delete",
|
|
3377
|
-
unit: word ? "word" : "grapheme",
|
|
3378
|
-
direction: 1
|
|
3689
|
+
if (this.extendRange === void 0) return range;
|
|
3690
|
+
const wanted = this.extendRange(range);
|
|
3691
|
+
return {
|
|
3692
|
+
firstRow: clamp$1(Math.min(wanted.firstRow, firstRow), this.frozenRows, firstRow),
|
|
3693
|
+
lastRow: clamp$1(Math.max(wanted.lastRow, lastRow), lastRow, this.rowCount - 1),
|
|
3694
|
+
firstColumn: clamp$1(Math.min(wanted.firstColumn, firstColumn), this.frozenColumns, firstColumn),
|
|
3695
|
+
lastColumn: clamp$1(Math.max(wanted.lastColumn, lastColumn), lastColumn, this.columnCount - 1)
|
|
3379
3696
|
};
|
|
3380
|
-
case "Enter": return primary ? null : { kind: "newline" };
|
|
3381
3697
|
}
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3698
|
+
buildChildren() {
|
|
3699
|
+
const { firstRow, lastRow, firstColumn, lastColumn } = this.range;
|
|
3700
|
+
if (lastRow < firstRow) return [];
|
|
3701
|
+
const width = this.contentWidth;
|
|
3702
|
+
const lead = this.offsetOf(firstColumn) - this.frozenWidth;
|
|
3703
|
+
const children = [];
|
|
3704
|
+
if (this.renderHeader !== void 0) {
|
|
3705
|
+
this.mountedHeader ??= this.wrapHeader(this.renderHeader(firstColumn, lastColumn), width, lead);
|
|
3706
|
+
children.push(this.mountedHeader);
|
|
3707
|
+
}
|
|
3708
|
+
for (let row = 0; row < this.frozenRows && row < this.rowCount; row++) {
|
|
3709
|
+
let built = this.frozenMounted.get(row);
|
|
3710
|
+
if (built === void 0) {
|
|
3711
|
+
built = this.wrap(row, width, lead);
|
|
3712
|
+
this.frozenMounted.set(row, built);
|
|
3713
|
+
}
|
|
3714
|
+
children.push(built);
|
|
3715
|
+
}
|
|
3716
|
+
children.push(spacer("sheet:top", this.rowOffsetOf(firstRow) - this.rowOffsetOf(this.frozenRows)));
|
|
3717
|
+
for (const row of this.mountedRows.keys()) if (row < firstRow || row > lastRow) this.mountedRows.delete(row);
|
|
3718
|
+
for (let row = firstRow; row <= lastRow; row++) {
|
|
3719
|
+
let built = this.mountedRows.get(row);
|
|
3720
|
+
if (built === void 0) {
|
|
3721
|
+
built = this.wrap(row, width, lead);
|
|
3722
|
+
this.mountedRows.set(row, built);
|
|
3723
|
+
}
|
|
3724
|
+
children.push(built);
|
|
3387
3725
|
}
|
|
3388
|
-
|
|
3726
|
+
children.push(spacer("sheet:bottom", this.rowsHeight - this.rowOffsetOf(lastRow + 1)));
|
|
3727
|
+
return children;
|
|
3389
3728
|
}
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3729
|
+
/**
|
|
3730
|
+
* The header, given the same geometry a row gets.
|
|
3731
|
+
*
|
|
3732
|
+
* Its own children are laid out exactly like a row's, so that a
|
|
3733
|
+
* column header lines up with the column under it without either
|
|
3734
|
+
* being told where the other is: the same leading spacer, the same
|
|
3735
|
+
* gutter-sized first cell, the same widths.
|
|
3736
|
+
*/
|
|
3737
|
+
wrapHeader(built, width, lead) {
|
|
3738
|
+
if (!isRowElement(built)) throw new Error("A sheet's header must be a Row element, laid out like the rows it labels.");
|
|
3739
|
+
return {
|
|
3740
|
+
...built,
|
|
3741
|
+
props: {
|
|
3742
|
+
...built.props,
|
|
3743
|
+
key: "sheet:header",
|
|
3744
|
+
width,
|
|
3745
|
+
height: this.headerHeight,
|
|
3746
|
+
flexShrink: 0
|
|
3747
|
+
},
|
|
3748
|
+
children: this.withLead(built.children, lead)
|
|
3749
|
+
};
|
|
3750
|
+
}
|
|
3751
|
+
/**
|
|
3752
|
+
* A row's children with the leading spacer in the right place.
|
|
3753
|
+
*
|
|
3754
|
+
* With a gutter, the renderer's first child *is* the gutter: it sits
|
|
3755
|
+
* at content x 0 and the columns begin past it, so the spacer for
|
|
3756
|
+
* the columns scrolled out of view goes after it. Without one the
|
|
3757
|
+
* spacer goes first. Either way the renderer is handed a range of
|
|
3758
|
+
* columns and never has to know where the window is.
|
|
3759
|
+
*/
|
|
3760
|
+
withLead(children, lead) {
|
|
3761
|
+
const pad = spacer("sheet:lead", void 0, lead);
|
|
3762
|
+
const stays = (this.gutterWidth > 0 ? 1 : 0) + this.frozenColumns;
|
|
3763
|
+
if (stays === 0) return [pad, ...children];
|
|
3764
|
+
return [
|
|
3765
|
+
...children.slice(0, stays),
|
|
3766
|
+
pad,
|
|
3767
|
+
...children.slice(stays)
|
|
3768
|
+
];
|
|
3769
|
+
}
|
|
3770
|
+
/**
|
|
3771
|
+
* The row the renderer returned, given its place in the sheet.
|
|
3772
|
+
*
|
|
3773
|
+
* The leading spacer is prepended here rather than asked of the
|
|
3774
|
+
* renderer, because it is windowing bookkeeping and not a cell: it is
|
|
3775
|
+
* what puts column `firstColumn` at its own offset without the row
|
|
3776
|
+
* holding the columns to its left.
|
|
3777
|
+
*/
|
|
3778
|
+
wrap(row, width, lead) {
|
|
3779
|
+
const built = this.renderRow(row, this.range.firstColumn, this.range.lastColumn);
|
|
3780
|
+
if (!isRowElement(built)) throw new Error(`A sheet row must be a Row element; row ${row} rendered something else. Its cells are laid out along one axis at widths the window already knows.`);
|
|
3781
|
+
return {
|
|
3782
|
+
...built,
|
|
3783
|
+
props: {
|
|
3784
|
+
...built.props,
|
|
3785
|
+
key: `sheet:row:${row}`,
|
|
3786
|
+
width,
|
|
3787
|
+
height: this.rowHeightOf(row),
|
|
3788
|
+
flexShrink: 0
|
|
3789
|
+
},
|
|
3790
|
+
children: this.withLead(built.children, lead)
|
|
3791
|
+
};
|
|
3792
|
+
}
|
|
3793
|
+
};
|
|
3794
|
+
function spacer(key, height, width) {
|
|
3795
|
+
return Box({
|
|
3796
|
+
key,
|
|
3797
|
+
height,
|
|
3798
|
+
width,
|
|
3799
|
+
flexShrink: 0
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
function isRowElement(child) {
|
|
3803
|
+
return typeof child === "object" && child !== null && child.type === "row" && Array.isArray(child.children);
|
|
3804
|
+
}
|
|
3805
|
+
function sameRange(a, b) {
|
|
3806
|
+
return a.firstRow === b.firstRow && a.lastRow === b.lastRow && a.firstColumn === b.firstColumn && a.lastColumn === b.lastColumn;
|
|
3395
3807
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3808
|
+
function clamp$1(value, min, max) {
|
|
3809
|
+
return Math.min(Math.max(value, min), max);
|
|
3810
|
+
}
|
|
3811
|
+
function positive(name, value) {
|
|
3812
|
+
if (!(value > 0)) throw new Error(`LazySheet ${name} must be positive, got ${String(value)}.`);
|
|
3813
|
+
return value;
|
|
3814
|
+
}
|
|
3815
|
+
/**
|
|
3816
|
+
* Feeds a sheet the inputs that may change, with the node's lifetime.
|
|
3817
|
+
*
|
|
3818
|
+
* The same reasoning as `lazySource`: a modifier is released inside
|
|
3819
|
+
* `removeSubtree`, and it attaches after the element's props and
|
|
3820
|
+
* before its children, so a count that arrives with the tree is in the
|
|
3821
|
+
* window before the first children are read from it.
|
|
3822
|
+
*/
|
|
3823
|
+
const sheetSource = defineModifier({
|
|
3824
|
+
name: "sheetSource",
|
|
3825
|
+
attach(host, args) {
|
|
3826
|
+
follow$1(host, args.rowCount, (value) => args.sheet.setRowCount(value));
|
|
3827
|
+
follow$1(host, args.columnCount, (value) => args.sheet.setColumnCount(value));
|
|
3828
|
+
let rows;
|
|
3829
|
+
let columns;
|
|
3830
|
+
const apply = () => {
|
|
3831
|
+
if (rows !== void 0 || columns !== void 0) args.sheet.setOverscan(rows ?? 3, columns ?? 2);
|
|
3832
|
+
};
|
|
3833
|
+
if (args.rowOverscan !== void 0) follow$1(host, args.rowOverscan, (value) => {
|
|
3834
|
+
rows = value;
|
|
3835
|
+
apply();
|
|
3836
|
+
});
|
|
3837
|
+
if (args.columnOverscan !== void 0) follow$1(host, args.columnOverscan, (value) => {
|
|
3838
|
+
columns = value;
|
|
3839
|
+
apply();
|
|
3840
|
+
});
|
|
3402
3841
|
}
|
|
3403
|
-
|
|
3842
|
+
});
|
|
3843
|
+
function follow$1(host, source, apply) {
|
|
3844
|
+
if (!isObservable$1(source)) {
|
|
3845
|
+
apply(source);
|
|
3846
|
+
return;
|
|
3847
|
+
}
|
|
3848
|
+
host.own(source.subscribe((value) => {
|
|
3849
|
+
apply(value);
|
|
3850
|
+
host.requestFrame();
|
|
3851
|
+
}));
|
|
3852
|
+
}
|
|
3853
|
+
/**
|
|
3854
|
+
* A scrolling surface that mounts only the cells in view, on both axes.
|
|
3855
|
+
*
|
|
3856
|
+
* LazySheet({ rowCount: 10_000, columnCount: 100, rowHeight: 24, columnWidth: 96 },
|
|
3857
|
+
* (row, first, last) => Row({}, ...cells(row, first, last)))
|
|
3858
|
+
*
|
|
3859
|
+
* It is a ScrollView whose children are a spacer for the rows above
|
|
3860
|
+
* the window, the mounted rows, and a spacer for the rows below; each
|
|
3861
|
+
* row carries a spacer for the columns to its left and is as wide as
|
|
3862
|
+
* the whole sheet, so the container's content extent — and both
|
|
3863
|
+
* scrollbars — are the sheet's real size on the first frame rather
|
|
3864
|
+
* than an estimate that settles.
|
|
3865
|
+
*/
|
|
3866
|
+
function LazySheet(props, renderRow) {
|
|
3867
|
+
const { rowCount, columnCount, rowHeight, rowHeights, columnWidth, rowOverscan, columnOverscan, gutterWidth, headerHeight, frozenRows, frozenColumns, header, extendRange, modifiers, sheetRef, ...rest } = props;
|
|
3868
|
+
const sheet = new UiVirtualSheet({
|
|
3869
|
+
rowCount,
|
|
3870
|
+
columnCount,
|
|
3871
|
+
rowHeight,
|
|
3872
|
+
rowHeights,
|
|
3873
|
+
columnWidth,
|
|
3874
|
+
rowOverscan,
|
|
3875
|
+
columnOverscan,
|
|
3876
|
+
gutterWidth,
|
|
3877
|
+
headerHeight,
|
|
3878
|
+
frozenRows,
|
|
3879
|
+
frozenColumns,
|
|
3880
|
+
extendRange
|
|
3881
|
+
}, renderRow, header);
|
|
3882
|
+
sheetRef?.(sheet);
|
|
3883
|
+
return createElement("scroll-view", {
|
|
3884
|
+
...rest,
|
|
3885
|
+
modifiers: [...modifiers ?? [], sheetSource({
|
|
3886
|
+
sheet,
|
|
3887
|
+
rowCount,
|
|
3888
|
+
columnCount,
|
|
3889
|
+
rowOverscan,
|
|
3890
|
+
columnOverscan
|
|
3891
|
+
})],
|
|
3892
|
+
direction: "column",
|
|
3893
|
+
[VIRTUAL_SHEET_PROP]: sheet
|
|
3894
|
+
}, [sheet.children$]);
|
|
3404
3895
|
}
|
|
3405
3896
|
//#endregion
|
|
3406
3897
|
//#region src/environment/EnvironmentNotifier.ts
|
|
@@ -4328,449 +4819,6 @@ function dragSessionFor(node) {
|
|
|
4328
4819
|
return session;
|
|
4329
4820
|
}
|
|
4330
4821
|
//#endregion
|
|
4331
|
-
//#region src/input/UiEditingController.ts
|
|
4332
|
-
/** How long two presses may be apart and still count as a double click. */
|
|
4333
|
-
const MULTI_CLICK_MS$1 = 500;
|
|
4334
|
-
const MULTI_CLICK_SLOP$1 = 4;
|
|
4335
|
-
/** Room kept around the caret when scrolling it into view. */
|
|
4336
|
-
const CARET_REVEAL_PADDING = 4;
|
|
4337
|
-
/**
|
|
4338
|
-
* Editing behaviour for EditableText nodes.
|
|
4339
|
-
*
|
|
4340
|
-
* The keyboard and pointer controllers call in here as their default
|
|
4341
|
-
* behaviour for an editable target — after the app's listeners have
|
|
4342
|
-
* run and only when none of them called preventDefault() — and the
|
|
4343
|
-
* host feeds it the shell's text input: `beforeinput` edits,
|
|
4344
|
-
* composition, paste. The model on the node is the single source of
|
|
4345
|
-
* truth; this class turns intents into model changes, marks the node
|
|
4346
|
-
* dirty so layout and paint follow, reports the change through an
|
|
4347
|
-
* `input` event, and keeps the caret in view.
|
|
4348
|
-
*
|
|
4349
|
-
* Text arrives one of two ways. A shell with an editing proxy sends
|
|
4350
|
-
* `beforeinput`, where the browser has already resolved the IME, dead
|
|
4351
|
-
* keys and the keyboard layout; then printable key presses are not
|
|
4352
|
-
* text and `textFromKeys` is off. Without a proxy — tests, a bare
|
|
4353
|
-
* runtime — printable keys become insertions.
|
|
4354
|
-
*/
|
|
4355
|
-
var UiEditingController = class {
|
|
4356
|
-
host;
|
|
4357
|
-
dispatcher;
|
|
4358
|
-
focus;
|
|
4359
|
-
/** Insert printable key presses as text; off when a shell delivers text through beforeinput. */
|
|
4360
|
-
textFromKeys = true;
|
|
4361
|
-
platform;
|
|
4362
|
-
paint = createPaintState();
|
|
4363
|
-
focusedEditable = null;
|
|
4364
|
-
visible = true;
|
|
4365
|
-
/** x the current run of vertical moves keeps returning to. */
|
|
4366
|
-
verticalGoalX = void 0;
|
|
4367
|
-
dragging = null;
|
|
4368
|
-
lastPress = null;
|
|
4369
|
-
compositionOpen = false;
|
|
4370
|
-
constructor(host, dispatcher, focus, options = {}) {
|
|
4371
|
-
this.host = host;
|
|
4372
|
-
this.dispatcher = dispatcher;
|
|
4373
|
-
this.focus = focus;
|
|
4374
|
-
this.platform = options.platform ?? detectEditingPlatform();
|
|
4375
|
-
this.focus.onFocusChange((node) => this.handleFocusChange(node));
|
|
4376
|
-
}
|
|
4377
|
-
/** The editable that holds focus, or null. */
|
|
4378
|
-
get focused() {
|
|
4379
|
-
return this.focusedEditable;
|
|
4380
|
-
}
|
|
4381
|
-
/** Whether the pointer controller should hand presses on the node to this controller. */
|
|
4382
|
-
isEditable(node) {
|
|
4383
|
-
return isEditableNode(node);
|
|
4384
|
-
}
|
|
4385
|
-
/**
|
|
4386
|
-
* Default keyboard behaviour for the focused editable. Returns true
|
|
4387
|
-
* when the key was an editing command, so the caller can mark the
|
|
4388
|
-
* event handled.
|
|
4389
|
-
*/
|
|
4390
|
-
handleKey(node, key, modifiers) {
|
|
4391
|
-
if (!isEditableNode(node)) return false;
|
|
4392
|
-
const command = commandForKey(key, modifiers, this.platform, this.textFromKeys);
|
|
4393
|
-
if (command === null) return false;
|
|
4394
|
-
return this.execute(node, command);
|
|
4395
|
-
}
|
|
4396
|
-
execute(node, command) {
|
|
4397
|
-
const model = editorFor(node);
|
|
4398
|
-
const readOnly = isReadOnly(node);
|
|
4399
|
-
switch (command.kind) {
|
|
4400
|
-
case "move":
|
|
4401
|
-
this.move(node, model, command.unit, command.direction, command.extend);
|
|
4402
|
-
return true;
|
|
4403
|
-
case "delete":
|
|
4404
|
-
if (readOnly) return true;
|
|
4405
|
-
return this.applyEdit(node, model, deleteInputType(command.unit, command.direction), null, () => {
|
|
4406
|
-
if (command.direction < 0) model.deleteBackward(command.unit);
|
|
4407
|
-
else model.deleteForward(command.unit);
|
|
4408
|
-
});
|
|
4409
|
-
case "newline":
|
|
4410
|
-
if (!isMultiline(node)) return false;
|
|
4411
|
-
if (readOnly) return true;
|
|
4412
|
-
return this.applyEdit(node, model, "insertLineBreak", "\n", () => model.insertText("\n"));
|
|
4413
|
-
case "insert":
|
|
4414
|
-
if (readOnly) return true;
|
|
4415
|
-
return this.applyEdit(node, model, "insertText", command.text, () => model.insertText(command.text));
|
|
4416
|
-
case "selectAll":
|
|
4417
|
-
model.selectAll();
|
|
4418
|
-
this.afterSelectionChange(node, model);
|
|
4419
|
-
return true;
|
|
4420
|
-
case "undo":
|
|
4421
|
-
if (readOnly) return true;
|
|
4422
|
-
this.history(node, model, "historyUndo", () => model.undo());
|
|
4423
|
-
return true;
|
|
4424
|
-
case "redo":
|
|
4425
|
-
if (readOnly) return true;
|
|
4426
|
-
this.history(node, model, "historyRedo", () => model.redo());
|
|
4427
|
-
return true;
|
|
4428
|
-
}
|
|
4429
|
-
}
|
|
4430
|
-
move(node, model, unit, direction, extend) {
|
|
4431
|
-
if (unit === "vertical") {
|
|
4432
|
-
const target = this.layoutOf(node).verticalMove(model.focus, direction, this.verticalGoalX);
|
|
4433
|
-
if (target === null) {
|
|
4434
|
-
model.moveTo(direction < 0 ? 0 : model.text.length, extend);
|
|
4435
|
-
this.verticalGoalX = void 0;
|
|
4436
|
-
} else {
|
|
4437
|
-
model.moveTo(target.offset, extend);
|
|
4438
|
-
this.verticalGoalX = target.x;
|
|
4439
|
-
}
|
|
4440
|
-
this.afterSelectionChange(node, model, true);
|
|
4441
|
-
return;
|
|
4442
|
-
}
|
|
4443
|
-
if (unit === "line") {
|
|
4444
|
-
const layout = this.layoutOf(node);
|
|
4445
|
-
const caret = layout.caretRect(model.focus);
|
|
4446
|
-
const line = layout.lines[caret.line];
|
|
4447
|
-
const hard = direction < 0 ? lineStartAt(model.text, model.focus) : lineEndAt(model.text, model.focus);
|
|
4448
|
-
const visual = direction < 0 ? line.start : line.end;
|
|
4449
|
-
model.moveTo(direction < 0 ? Math.max(hard, visual) : Math.min(hard, visual), extend);
|
|
4450
|
-
this.afterSelectionChange(node, model);
|
|
4451
|
-
return;
|
|
4452
|
-
}
|
|
4453
|
-
model.move(unit, direction, extend);
|
|
4454
|
-
this.afterSelectionChange(node, model);
|
|
4455
|
-
}
|
|
4456
|
-
/**
|
|
4457
|
-
* A `beforeinput` from the shell's editing proxy, in the DOM's
|
|
4458
|
-
* vocabulary. Returns true when it was applied to the focused
|
|
4459
|
-
* editable. Composition types are not handled here: the composition
|
|
4460
|
-
* methods carry them, and an engine that fires both must not insert
|
|
4461
|
-
* twice.
|
|
4462
|
-
*/
|
|
4463
|
-
beforeInput(inputType, data) {
|
|
4464
|
-
const node = this.focusedEditable;
|
|
4465
|
-
if (node === null) return false;
|
|
4466
|
-
const model = editorFor(node);
|
|
4467
|
-
const readOnly = isReadOnly(node);
|
|
4468
|
-
switch (inputType) {
|
|
4469
|
-
case "insertText":
|
|
4470
|
-
case "insertReplacementText":
|
|
4471
|
-
case "insertFromDrop":
|
|
4472
|
-
case "insertFromYank":
|
|
4473
|
-
if (data === null || data.length === 0) return false;
|
|
4474
|
-
return readOnly || this.insert(node, model, inputType, data);
|
|
4475
|
-
case "insertFromPaste": return readOnly || data !== null && this.insert(node, model, inputType, data);
|
|
4476
|
-
case "insertLineBreak":
|
|
4477
|
-
case "insertParagraph":
|
|
4478
|
-
if (!isMultiline(node)) return false;
|
|
4479
|
-
return readOnly || this.applyEdit(node, model, inputType, "\n", () => model.insertText("\n"));
|
|
4480
|
-
case "deleteContentBackward":
|
|
4481
|
-
case "deleteWordBackward":
|
|
4482
|
-
case "deleteSoftLineBackward":
|
|
4483
|
-
case "deleteHardLineBackward":
|
|
4484
|
-
case "deleteContentForward":
|
|
4485
|
-
case "deleteWordForward":
|
|
4486
|
-
case "deleteSoftLineForward":
|
|
4487
|
-
case "deleteHardLineForward":
|
|
4488
|
-
case "deleteContent":
|
|
4489
|
-
case "deleteByCut":
|
|
4490
|
-
case "deleteByDrag": {
|
|
4491
|
-
if (readOnly) return true;
|
|
4492
|
-
const unit = /Word/.test(inputType) ? "word" : /Line/.test(inputType) ? "line" : "grapheme";
|
|
4493
|
-
const forward = /Forward/.test(inputType);
|
|
4494
|
-
return this.applyEdit(node, model, inputType, null, () => {
|
|
4495
|
-
if (inputType === "deleteContent" || inputType === "deleteByCut" || inputType === "deleteByDrag") model.replaceRange(model.start, model.end, "");
|
|
4496
|
-
else if (forward) model.deleteForward(unit);
|
|
4497
|
-
else model.deleteBackward(unit);
|
|
4498
|
-
});
|
|
4499
|
-
}
|
|
4500
|
-
case "historyUndo": return readOnly || this.history(node, model, inputType, () => model.undo());
|
|
4501
|
-
case "historyRedo": return readOnly || this.history(node, model, inputType, () => model.redo());
|
|
4502
|
-
default: return false;
|
|
4503
|
-
}
|
|
4504
|
-
}
|
|
4505
|
-
/** Inserts text at the selection of the focused editable, as typing would. */
|
|
4506
|
-
insertText(text) {
|
|
4507
|
-
const node = this.focusedEditable;
|
|
4508
|
-
if (node === null || isReadOnly(node)) return false;
|
|
4509
|
-
return this.insert(node, editorFor(node), "insertText", text);
|
|
4510
|
-
}
|
|
4511
|
-
/**
|
|
4512
|
-
* Replaces the focused editable's whole text, as an assistive
|
|
4513
|
-
* technology's "set value" does.
|
|
4514
|
-
*
|
|
4515
|
-
* `insertReplacementText` is the DOM's own input type for this, so an
|
|
4516
|
-
* `onBeforeInput` that vets edits sees the same vocabulary it sees
|
|
4517
|
-
* for typing, and an app that rejects the edit rejects this one too.
|
|
4518
|
-
* Selecting everything first is what makes it one undo step and one
|
|
4519
|
-
* `onInput` rather than a delete and an insert.
|
|
4520
|
-
*/
|
|
4521
|
-
replaceText(text) {
|
|
4522
|
-
const node = this.focusedEditable;
|
|
4523
|
-
if (node === null || isReadOnly(node)) return false;
|
|
4524
|
-
const model = editorFor(node);
|
|
4525
|
-
model.selectAll();
|
|
4526
|
-
return this.insert(node, model, "insertReplacementText", text);
|
|
4527
|
-
}
|
|
4528
|
-
/** Pastes text: newlines are kept in a multiline field and become spaces in a single-line one. */
|
|
4529
|
-
paste(text) {
|
|
4530
|
-
const node = this.focusedEditable;
|
|
4531
|
-
if (node === null || isReadOnly(node)) return false;
|
|
4532
|
-
return this.insert(node, editorFor(node), "insertFromPaste", text);
|
|
4533
|
-
}
|
|
4534
|
-
insert(node, model, inputType, text) {
|
|
4535
|
-
const data = isMultiline(node) ? text : text.replace(/\r\n|\r|\n/g, " ");
|
|
4536
|
-
return this.applyEdit(node, model, inputType, data, () => model.insertText(data));
|
|
4537
|
-
}
|
|
4538
|
-
compositionStart() {
|
|
4539
|
-
const node = this.focusedEditable;
|
|
4540
|
-
if (node === null || isReadOnly(node)) return;
|
|
4541
|
-
const model = editorFor(node);
|
|
4542
|
-
model.beginComposition();
|
|
4543
|
-
this.compositionOpen = true;
|
|
4544
|
-
this.afterTextChange(node, model, false);
|
|
4545
|
-
}
|
|
4546
|
-
/** The composition text so far and the caret within it. */
|
|
4547
|
-
compositionUpdate(text, caret = text.length) {
|
|
4548
|
-
const node = this.focusedEditable;
|
|
4549
|
-
if (node === null || isReadOnly(node)) return;
|
|
4550
|
-
const model = editorFor(node);
|
|
4551
|
-
model.updateComposition(text, caret);
|
|
4552
|
-
this.compositionOpen = true;
|
|
4553
|
-
this.afterTextChange(node, model, false);
|
|
4554
|
-
}
|
|
4555
|
-
/** The IME committed `text` (possibly empty: the composition was cancelled). */
|
|
4556
|
-
compositionEnd(text) {
|
|
4557
|
-
const node = this.focusedEditable;
|
|
4558
|
-
this.compositionOpen = false;
|
|
4559
|
-
if (node === null || isReadOnly(node)) return;
|
|
4560
|
-
const model = editorFor(node);
|
|
4561
|
-
const data = isMultiline(node) ? text : text.replace(/\r\n|\r|\n/g, " ");
|
|
4562
|
-
const before = model.text;
|
|
4563
|
-
model.commitComposition(data);
|
|
4564
|
-
this.afterTextChange(node, model, before !== model.text);
|
|
4565
|
-
}
|
|
4566
|
-
/**
|
|
4567
|
-
* A press on an editable places the caret (Shift extends the
|
|
4568
|
-
* selection to it); a second press within the double-click window
|
|
4569
|
-
* selects the word, a third the line. Dragging afterwards extends the
|
|
4570
|
-
* selection from the anchor.
|
|
4571
|
-
*/
|
|
4572
|
-
pointerDown(node, x, y, modifiers) {
|
|
4573
|
-
if (!isEditableNode(node)) return;
|
|
4574
|
-
const model = editorFor(node);
|
|
4575
|
-
const layout = this.layoutOf(node);
|
|
4576
|
-
const local = this.host.toLocal(node, x, y);
|
|
4577
|
-
const offset = layout.offsetAt(local.x, local.y);
|
|
4578
|
-
const now = this.host.now();
|
|
4579
|
-
const last = this.lastPress;
|
|
4580
|
-
const count = !modifiers.shift && last !== null && last.node === node && now - last.at <= MULTI_CLICK_MS$1 && Math.abs(last.x - x) <= MULTI_CLICK_SLOP$1 && Math.abs(last.y - y) <= MULTI_CLICK_SLOP$1 ? last.count + 1 : 1;
|
|
4581
|
-
this.lastPress = {
|
|
4582
|
-
node,
|
|
4583
|
-
x,
|
|
4584
|
-
y,
|
|
4585
|
-
at: now,
|
|
4586
|
-
count
|
|
4587
|
-
};
|
|
4588
|
-
this.dragging = node;
|
|
4589
|
-
if (modifiers.shift) model.moveTo(offset, true);
|
|
4590
|
-
else if (count === 2) {
|
|
4591
|
-
const word = wordRangeAt(model.text, offset);
|
|
4592
|
-
model.select(word.start, word.end);
|
|
4593
|
-
} else if (count >= 3) model.select(lineStartAt(model.text, offset), lineEndAt(model.text, offset));
|
|
4594
|
-
else model.select(offset);
|
|
4595
|
-
model.breakUndoGroup();
|
|
4596
|
-
this.afterSelectionChange(node, model);
|
|
4597
|
-
}
|
|
4598
|
-
pointerMove(node, x, y) {
|
|
4599
|
-
if (this.dragging !== node) return;
|
|
4600
|
-
const model = editorFor(node);
|
|
4601
|
-
const layout = this.layoutOf(node);
|
|
4602
|
-
const local = this.host.toLocal(node, x, y);
|
|
4603
|
-
const offset = layout.offsetAt(local.x, local.y);
|
|
4604
|
-
if (offset !== model.focus) {
|
|
4605
|
-
model.moveTo(offset, true);
|
|
4606
|
-
this.afterSelectionChange(node, model);
|
|
4607
|
-
}
|
|
4608
|
-
}
|
|
4609
|
-
pointerUp() {
|
|
4610
|
-
this.dragging = null;
|
|
4611
|
-
}
|
|
4612
|
-
/** The focused editable's state for the shell to mirror, or null. */
|
|
4613
|
-
state() {
|
|
4614
|
-
const node = this.focusedEditable;
|
|
4615
|
-
if (node === null || this.host.recordFor(node) === void 0) return null;
|
|
4616
|
-
const model = editorFor(node);
|
|
4617
|
-
const caret = this.layoutOf(node).caretRect();
|
|
4618
|
-
const visible = this.host.visibleBox(node);
|
|
4619
|
-
return {
|
|
4620
|
-
text: model.text,
|
|
4621
|
-
selectionStart: model.start,
|
|
4622
|
-
selectionEnd: model.end,
|
|
4623
|
-
caret: {
|
|
4624
|
-
x: visible.x + caret.x,
|
|
4625
|
-
y: visible.y + caret.y,
|
|
4626
|
-
width: 1,
|
|
4627
|
-
height: caret.height
|
|
4628
|
-
},
|
|
4629
|
-
multiline: isMultiline(node),
|
|
4630
|
-
composing: model.composing
|
|
4631
|
-
};
|
|
4632
|
-
}
|
|
4633
|
-
/**
|
|
4634
|
-
* When the focused caret next toggles, or undefined when nothing
|
|
4635
|
-
* blinks: no focused editable, a composition in progress (the caret
|
|
4636
|
-
* holds), or a hidden page.
|
|
4637
|
-
*/
|
|
4638
|
-
nextCaretChange(now) {
|
|
4639
|
-
const node = this.focusedEditable;
|
|
4640
|
-
if (node === null || !this.visible) return;
|
|
4641
|
-
const model = editorFor(node);
|
|
4642
|
-
if (model.composing || !model.collapsed) return;
|
|
4643
|
-
return nextCaretToggle(model, now);
|
|
4644
|
-
}
|
|
4645
|
-
/**
|
|
4646
|
-
* The page was hidden or shown. A hidden page stops the blink so a
|
|
4647
|
-
* background tab schedules no frames; showing it restarts the caret.
|
|
4648
|
-
*/
|
|
4649
|
-
setVisible(visible) {
|
|
4650
|
-
if (this.visible === visible) return;
|
|
4651
|
-
this.visible = visible;
|
|
4652
|
-
const node = this.focusedEditable;
|
|
4653
|
-
if (node !== null) {
|
|
4654
|
-
editorFor(node).blinkOrigin = this.host.now();
|
|
4655
|
-
this.host.markDirty(node, 2);
|
|
4656
|
-
}
|
|
4657
|
-
}
|
|
4658
|
-
/** Whether the caret may be drawn at all (the page is visible). */
|
|
4659
|
-
get caretEnabled() {
|
|
4660
|
-
return this.visible;
|
|
4661
|
-
}
|
|
4662
|
-
handleFocusChange(node) {
|
|
4663
|
-
const previous = this.focusedEditable;
|
|
4664
|
-
const next = node !== null && isEditableNode(node) ? node : null;
|
|
4665
|
-
if (previous === next) return;
|
|
4666
|
-
if (previous !== null) {
|
|
4667
|
-
const model = editorFor(previous);
|
|
4668
|
-
if (model.composing) model.commitComposition(model.text.slice(model.composition.start, model.composition.end));
|
|
4669
|
-
model.focused = false;
|
|
4670
|
-
this.host.markDirty(previous, 2);
|
|
4671
|
-
}
|
|
4672
|
-
this.focusedEditable = next;
|
|
4673
|
-
this.compositionOpen = false;
|
|
4674
|
-
this.verticalGoalX = void 0;
|
|
4675
|
-
if (next !== null) {
|
|
4676
|
-
const model = editorFor(next);
|
|
4677
|
-
model.focused = true;
|
|
4678
|
-
model.blinkOrigin = this.host.now();
|
|
4679
|
-
this.host.markDirty(next, 2);
|
|
4680
|
-
}
|
|
4681
|
-
}
|
|
4682
|
-
/**
|
|
4683
|
-
* Runs an edit unless a `beforeinput` listener cancels it, then
|
|
4684
|
-
* reports it. Returns true when the input was consumed either way.
|
|
4685
|
-
*/
|
|
4686
|
-
applyEdit(node, model, inputType, data, run) {
|
|
4687
|
-
const before = new UiBeforeInputEvent(inputType, data);
|
|
4688
|
-
this.dispatcher.dispatch(before, node);
|
|
4689
|
-
if (before.defaultPrevented) return true;
|
|
4690
|
-
const text = model.text;
|
|
4691
|
-
run();
|
|
4692
|
-
this.afterTextChange(node, model, text !== model.text);
|
|
4693
|
-
return true;
|
|
4694
|
-
}
|
|
4695
|
-
history(node, model, inputType, run) {
|
|
4696
|
-
const before = new UiBeforeInputEvent(inputType, null);
|
|
4697
|
-
this.dispatcher.dispatch(before, node);
|
|
4698
|
-
if (before.defaultPrevented) return true;
|
|
4699
|
-
const text = model.text;
|
|
4700
|
-
run();
|
|
4701
|
-
this.afterTextChange(node, model, text !== model.text);
|
|
4702
|
-
return true;
|
|
4703
|
-
}
|
|
4704
|
-
afterTextChange(node, model, changed) {
|
|
4705
|
-
this.verticalGoalX = void 0;
|
|
4706
|
-
model.blinkOrigin = this.host.now();
|
|
4707
|
-
this.host.markDirty(node, changed || this.compositionOpen ? 5 : 2);
|
|
4708
|
-
if (changed) this.dispatcher.dispatch(new UiTextChangeEvent(model.text, model.start, model.end), node);
|
|
4709
|
-
this.revealCaret(node);
|
|
4710
|
-
}
|
|
4711
|
-
afterSelectionChange(node, model, keepGoal = false) {
|
|
4712
|
-
if (!keepGoal) this.verticalGoalX = void 0;
|
|
4713
|
-
model.blinkOrigin = this.host.now();
|
|
4714
|
-
this.host.markDirty(node, 2);
|
|
4715
|
-
this.revealCaret(node);
|
|
4716
|
-
}
|
|
4717
|
-
/**
|
|
4718
|
-
* Asks the host to scroll the caret into view, when the node has been
|
|
4719
|
-
* laid out: the field's own text first, then any scroll container
|
|
4720
|
-
* around it.
|
|
4721
|
-
*
|
|
4722
|
-
* `layoutOf` places the text where it is seen, so the caret comes
|
|
4723
|
-
* back with the field's own scroll already taken off; the reveal is
|
|
4724
|
-
* asked for in the node's unscrolled coordinates, which is where the
|
|
4725
|
-
* offset it wants is measured from.
|
|
4726
|
-
*/
|
|
4727
|
-
revealCaret(node) {
|
|
4728
|
-
const rec = this.host.recordFor(node);
|
|
4729
|
-
if (rec === void 0) return;
|
|
4730
|
-
const caret = this.layoutOf(node).caretRect();
|
|
4731
|
-
this.host.reveal(node, {
|
|
4732
|
-
x: caret.x + rec.scrollX - CARET_REVEAL_PADDING,
|
|
4733
|
-
y: caret.y + rec.scrollY - CARET_REVEAL_PADDING,
|
|
4734
|
-
width: 9,
|
|
4735
|
-
height: caret.height + 8
|
|
4736
|
-
});
|
|
4737
|
-
}
|
|
4738
|
-
/**
|
|
4739
|
-
* The node's text laid out in node-local coordinates: the content box
|
|
4740
|
-
* starts at the padding and is shifted by the field's own scroll, as
|
|
4741
|
-
* it is for the renderers in the parent's space. Points from the
|
|
4742
|
-
* pointer and the caret box the shell is given are both in that same
|
|
4743
|
-
* seen space, so neither has to know the offset. Before the first
|
|
4744
|
-
* layout the record is empty and the lines fall at the origin, which
|
|
4745
|
-
* is still a valid place for a caret.
|
|
4746
|
-
*/
|
|
4747
|
-
layoutOf(node) {
|
|
4748
|
-
const rec = this.host.recordFor(node);
|
|
4749
|
-
const state = resolvePaintState(node, this.paint);
|
|
4750
|
-
const box = rec === void 0 ? {
|
|
4751
|
-
x: 0,
|
|
4752
|
-
y: 0,
|
|
4753
|
-
width: 0,
|
|
4754
|
-
height: 0
|
|
4755
|
-
} : {
|
|
4756
|
-
x: rec.paddingLeft - rec.scrollX,
|
|
4757
|
-
y: rec.paddingTop - rec.scrollY,
|
|
4758
|
-
width: Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight),
|
|
4759
|
-
height: Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom)
|
|
4760
|
-
};
|
|
4761
|
-
return new EditableLayout(state.editor, box, state, this.host.measurer);
|
|
4762
|
-
}
|
|
4763
|
-
};
|
|
4764
|
-
function deleteInputType(unit, direction) {
|
|
4765
|
-
const side = direction < 0 ? "Backward" : "Forward";
|
|
4766
|
-
switch (unit) {
|
|
4767
|
-
case "word": return `deleteWord${side}`;
|
|
4768
|
-
case "line":
|
|
4769
|
-
case "document": return `deleteSoftLine${side}`;
|
|
4770
|
-
default: return `deleteContent${side}`;
|
|
4771
|
-
}
|
|
4772
|
-
}
|
|
4773
|
-
//#endregion
|
|
4774
4822
|
//#region src/layout/LayoutNotifier.ts
|
|
4775
4823
|
/**
|
|
4776
4824
|
* Tells listeners when a node's layout changed.
|
|
@@ -4976,6 +5024,110 @@ const DEFAULT_RING = kind$3(Object.freeze({}));
|
|
|
4976
5024
|
function focusRing(options) {
|
|
4977
5025
|
return options === void 0 ? DEFAULT_RING : kind$3(options);
|
|
4978
5026
|
}
|
|
5027
|
+
/**
|
|
5028
|
+
* A border per edge, as paint rather than as layout.
|
|
5029
|
+
*
|
|
5030
|
+
* `borderWidth` is one number and `borderColor` one colour, so a node
|
|
5031
|
+
* cannot have a heavy bottom edge and a hairline top. This is the gap
|
|
5032
|
+
* closed from the cheap side. A border in Gesso is paint-only —
|
|
5033
|
+
* `paintBorder` strokes inside the box and touches no layout — so four
|
|
5034
|
+
* edges are four filled rectangles in the node's own paint pass, with
|
|
5035
|
+
* nothing to lay out and nothing to hit test. A bordered cell costs
|
|
5036
|
+
* four draw instances and no extra nodes.
|
|
5037
|
+
*
|
|
5038
|
+
* Box({ modifiers: [borders({ bottom: 2, right: 1, color: 'border' })] })
|
|
5039
|
+
*
|
|
5040
|
+
* They lie *inside* the box, where `borderWidth` puts them, so a border
|
|
5041
|
+
* never changes where anything sits and never overlaps a neighbour.
|
|
5042
|
+
*
|
|
5043
|
+
* **Why this is a modifier and not four props, and why that is the
|
|
5044
|
+
* answer rather than a stopgap.** First-class `borderTopWidth` is a
|
|
5045
|
+
* much larger change, and the cost is not where it looks: the
|
|
5046
|
+
* property, the paint state and the Canvas2D stroke are easy, but the
|
|
5047
|
+
* WebGPU instance packs `radius, opacity, borderWidth` as one
|
|
5048
|
+
* `float32x3`, the fragment shader draws the border as an isotropic
|
|
5049
|
+
* SDF band, and one instance carries one colour — so four widths need
|
|
5050
|
+
* a wider vertex format, an anisotropic inner rect in the shader, and
|
|
5051
|
+
* up to four instances when the colours differ. This needs none of it,
|
|
5052
|
+
* because a decoration is already a rectangle with its own colour that
|
|
5053
|
+
* both backends draw.
|
|
5054
|
+
*
|
|
5055
|
+
* What the properties would buy over this, once written, is a rounded
|
|
5056
|
+
* per-edge border and one instance instead of four. The first is a
|
|
5057
|
+
* thing square-cornered borders are for — a grid, a table, a rule —
|
|
5058
|
+
* and the second is four draws on a node that already costs one. So
|
|
5059
|
+
* the properties are not planned. If something turns up that wants a
|
|
5060
|
+
* heavy bottom edge on a rounded card, that is the case to reopen it
|
|
5061
|
+
* with, and it will be a better case than "for completeness".
|
|
5062
|
+
*
|
|
5063
|
+
* What it does not do is round its corners. Four rectangles meeting at
|
|
5064
|
+
* a square corner is right for a grid, a table and a rule, which is
|
|
5065
|
+
* what per-edge borders are for; a rounded box wants the single
|
|
5066
|
+
* `borderWidth`, which the renderers draw as one band and get right.
|
|
5067
|
+
*/
|
|
5068
|
+
function borders(options) {
|
|
5069
|
+
return decorated(borderShapes(options));
|
|
5070
|
+
}
|
|
5071
|
+
/**
|
|
5072
|
+
* The rectangles `borders()` draws, without the modifier around them.
|
|
5073
|
+
*
|
|
5074
|
+
* For a surface that cannot afford a modifier per node and pushes its
|
|
5075
|
+
* decorations in instead. A spreadsheet is the case: ten thousand
|
|
5076
|
+
* mounted cells, each with its own border, fed from one writer into a
|
|
5077
|
+
* subject per cell — a modifier per cell would be ten thousand
|
|
5078
|
+
* attachments to do what ten thousand array writes already do. The
|
|
5079
|
+
* arithmetic is the same either way, and sharing it is the point:
|
|
5080
|
+
* an application that rolled its own would be the second place a
|
|
5081
|
+
* corner could be painted twice.
|
|
5082
|
+
*/
|
|
5083
|
+
function borderShapes(options) {
|
|
5084
|
+
const shared = options.color ?? "border";
|
|
5085
|
+
const edge = (side) => {
|
|
5086
|
+
if (side === void 0) return;
|
|
5087
|
+
const resolved = typeof side === "number" ? { width: side } : side;
|
|
5088
|
+
return resolved.width > 0 ? resolved : void 0;
|
|
5089
|
+
};
|
|
5090
|
+
const top = edge(options.top);
|
|
5091
|
+
const bottom = edge(options.bottom);
|
|
5092
|
+
const left = edge(options.left);
|
|
5093
|
+
const right = edge(options.right);
|
|
5094
|
+
const between = {
|
|
5095
|
+
y: top?.width ?? 0,
|
|
5096
|
+
bottom: bottom?.width ?? 0
|
|
5097
|
+
};
|
|
5098
|
+
const shapes = [];
|
|
5099
|
+
if (top !== void 0) shapes.push({
|
|
5100
|
+
kind: "fill",
|
|
5101
|
+
color: top.color ?? shared,
|
|
5102
|
+
y: 0,
|
|
5103
|
+
height: top.width,
|
|
5104
|
+
radius: 0
|
|
5105
|
+
});
|
|
5106
|
+
if (bottom !== void 0) shapes.push({
|
|
5107
|
+
kind: "fill",
|
|
5108
|
+
color: bottom.color ?? shared,
|
|
5109
|
+
bottom: 0,
|
|
5110
|
+
height: bottom.width,
|
|
5111
|
+
radius: 0
|
|
5112
|
+
});
|
|
5113
|
+
if (left !== void 0) shapes.push({
|
|
5114
|
+
kind: "fill",
|
|
5115
|
+
color: left.color ?? shared,
|
|
5116
|
+
x: 0,
|
|
5117
|
+
width: left.width,
|
|
5118
|
+
radius: 0,
|
|
5119
|
+
...between
|
|
5120
|
+
});
|
|
5121
|
+
if (right !== void 0) shapes.push({
|
|
5122
|
+
kind: "fill",
|
|
5123
|
+
color: right.color ?? shared,
|
|
5124
|
+
right: 0,
|
|
5125
|
+
width: right.width,
|
|
5126
|
+
radius: 0,
|
|
5127
|
+
...between
|
|
5128
|
+
});
|
|
5129
|
+
return shapes;
|
|
5130
|
+
}
|
|
4979
5131
|
//#endregion
|
|
4980
5132
|
//#region src/modifiers/focus.ts
|
|
4981
5133
|
/**
|
|
@@ -9660,12 +9812,16 @@ function buildRenderList(root, layout, measurer, logicalWidth, logicalHeight, dp
|
|
|
9660
9812
|
if (text.editor !== void 0) {
|
|
9661
9813
|
const model = text.editor;
|
|
9662
9814
|
const editable = new EditableLayout(model, contentBox, text, measurer);
|
|
9815
|
+
pushTextRects(textRunBackgrounds(editable.lines, text));
|
|
9663
9816
|
if (model.focused && !model.collapsed) {
|
|
9664
9817
|
const selection = parseColor$1(text.selectionColor);
|
|
9665
9818
|
if (selection !== void 0) for (const box of editable.selectionBoxes()) pushContentRect(box, selection);
|
|
9666
9819
|
}
|
|
9667
9820
|
if (editable.placeholderLines.length > 0) pushTextRun(editable.placeholderLines, colorToCss(text.placeholderColor));
|
|
9668
|
-
else
|
|
9821
|
+
else {
|
|
9822
|
+
pushTextRun(editable.lines, colorToCss(text.textColor));
|
|
9823
|
+
pushTextRects(textRunDecorations(editable.lines, text));
|
|
9824
|
+
}
|
|
9669
9825
|
if (model.composing) {
|
|
9670
9826
|
const underline = parseColor$1(text.textColor);
|
|
9671
9827
|
if (underline !== void 0) {
|
|
@@ -13380,6 +13536,7 @@ var UiScheduler = class {
|
|
|
13380
13536
|
dirty;
|
|
13381
13537
|
onFrame;
|
|
13382
13538
|
beforeCollect;
|
|
13539
|
+
onFrameError;
|
|
13383
13540
|
disposed = false;
|
|
13384
13541
|
active = true;
|
|
13385
13542
|
pending = false;
|
|
@@ -13398,6 +13555,9 @@ var UiScheduler = class {
|
|
|
13398
13555
|
this.dirty = options.dirty;
|
|
13399
13556
|
this.onFrame = options.onFrame;
|
|
13400
13557
|
this.beforeCollect = options.beforeCollect;
|
|
13558
|
+
this.onFrameError = options.onFrameError ?? ((error) => {
|
|
13559
|
+
console.error("A Gesso frame threw. The frame was abandoned and the clock kept running.", error);
|
|
13560
|
+
});
|
|
13401
13561
|
this.clock = options.clock((time) => {
|
|
13402
13562
|
this.handleFrame(time);
|
|
13403
13563
|
});
|
|
@@ -13505,30 +13665,42 @@ var UiScheduler = class {
|
|
|
13505
13665
|
this.collecting = true;
|
|
13506
13666
|
let frame;
|
|
13507
13667
|
let collected = 0;
|
|
13668
|
+
/**
|
|
13669
|
+
* Everything a frame does, inside one guard.
|
|
13670
|
+
*
|
|
13671
|
+
* The guard is the whole point: an exception used to unwind past
|
|
13672
|
+
* the re-arm at the bottom of this method, and since `pending` had
|
|
13673
|
+
* already been cleared at the top, nothing asked for another
|
|
13674
|
+
* frame. One throw stopped the application for good.
|
|
13675
|
+
*/
|
|
13508
13676
|
try {
|
|
13509
|
-
|
|
13677
|
+
try {
|
|
13678
|
+
this.beforeCollect?.(time);
|
|
13679
|
+
if (measuring) {
|
|
13680
|
+
collected = markNow();
|
|
13681
|
+
measureSpan("before collect", started, collected);
|
|
13682
|
+
}
|
|
13683
|
+
frame = this.collectFrame(time);
|
|
13684
|
+
} finally {
|
|
13685
|
+
this.collecting = false;
|
|
13686
|
+
}
|
|
13687
|
+
let processed = 0;
|
|
13510
13688
|
if (measuring) {
|
|
13511
|
-
|
|
13512
|
-
measureSpan("
|
|
13689
|
+
processed = markNow();
|
|
13690
|
+
measureSpan("collect", collected, processed);
|
|
13513
13691
|
}
|
|
13514
|
-
frame
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
|
|
13518
|
-
|
|
13519
|
-
|
|
13520
|
-
|
|
13521
|
-
|
|
13522
|
-
|
|
13523
|
-
|
|
13524
|
-
|
|
13525
|
-
|
|
13526
|
-
const detail = {
|
|
13527
|
-
frame: frame.id,
|
|
13528
|
-
nodes: frame.size
|
|
13529
|
-
};
|
|
13530
|
-
measureSpan("process", processed, finished, detail);
|
|
13531
|
-
measureSpan("frame", started, finished, detail);
|
|
13692
|
+
if (frame.size > 0) this.onFrame(frame);
|
|
13693
|
+
if (measuring) {
|
|
13694
|
+
const finished = markNow();
|
|
13695
|
+
const detail = {
|
|
13696
|
+
frame: frame.id,
|
|
13697
|
+
nodes: frame.size
|
|
13698
|
+
};
|
|
13699
|
+
measureSpan("process", processed, finished, detail);
|
|
13700
|
+
measureSpan("frame", started, finished, detail);
|
|
13701
|
+
}
|
|
13702
|
+
} catch (error) {
|
|
13703
|
+
this.onFrameError(error, time);
|
|
13532
13704
|
}
|
|
13533
13705
|
if (!this.dirty.isEmpty()) this.notifyDirty();
|
|
13534
13706
|
}
|
|
@@ -14667,6 +14839,6 @@ function recordsEqual(a, b) {
|
|
|
14667
14839
|
return true;
|
|
14668
14840
|
}
|
|
14669
14841
|
//#endregion
|
|
14670
|
-
export { AlignContent, AnimationDriver, BUTTON_INTERACTION, Box, Button, CARET_BLINK_MS, CARET_WIDTH, CLIP_STRIDE_BYTES, CLIP_STRIDE_FLOATS, Canvas2DRenderer, CanvasPlatformSurface, CanvasSurface, CanvasTextMeasurer, CharacterCountTextMeasurer, Column, CommandKind, Constraints, CrossAxisAlignment, DEFAULT_BLOCK_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_FONT_WEIGHT, DEFAULT_LINE_HEIGHT_FACTOR, DEFAULT_TEXT_COLOR, DefaultImageResolver, DefaultVideoResolver, DirtyFlags, DirtyNodeSet, EDITOR_PROP, ELLIPSIS, EMPTY_RECORDING, EXTERNAL_FILES, EditableLayout, EditableText, EditableTextModel, EnvironmentNotifier, FlexDirection, FocusNotifier, GLYPH_SUBPIXEL_PHASES, GlyphShaper, Grid, INSPECTOR_HEAT_MS, INSTANCE_STRIDE_BYTES, INSTANCE_STRIDE_FLOATS, IconRasterizer, LABEL_PADDING_X, LayoutEngine, LayoutInspector, LayoutNotifier, LayoutRecord, LazyColumn, LazyGrid, LazyRow, MARK_PREFIX, MAX_GRADIENT_STOPS, MAX_MEASURES_PER_CHILD, MOTION_CHANNELS, MOTION_REST, MOUSE_POINTER, MainAxisAlignment, Mp3Frames, NO_CLIP_INDEX, NodeTransitions, Paint, PaintPictureCache, PaintRecorder, PaintTarget, ParagraphTextMeasurer, PrimitiveKind, Responsive, Row, SCROLLBAR_FADE_MS, SCROLLBAR_HOVER_ZONE, SCROLLBAR_INSET, SCROLLBAR_LINGER_MS, SCROLLBAR_MIN_THUMB, SCROLLBAR_THICKNESS, ScrollView, Stack, TEXTURED_STRIDE_BYTES, TEXTURED_STRIDE_FLOATS, TEXT_MATCHES_PROP, TEXT_RUN_ID_SEPARATOR, TEXT_SELECTION_PROP, Text, UI_ROLES, UI_SEMANTIC_STATES, UI_SUBGRID_AXES, UiAnimation, UiAnimationFrameClock, UiBasicColors, UiBeforeInputEvent, UiBinding, UiBorderRadiuses, UiChildrenBinding, UiContainerSizeSource, UiDragSession, UiEditingController, UiEnvironment, UiEnvironmentKeys, UiEventBinding, UiEventType, UiFindController, UiFocusEvent, UiFocusManager, UiFrame, UiGestureEvent, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiHostFrameClock, UiInputDispatcher, UiInputEvent, UiInsetRegistry, UiKeyboardController, UiKeyboardEvent, UiManualFrameClock, UiModifierSet, UiNode, UiNodeType, UiPinchEvent, UiPinchRecognizer, UiPlatformAdapter, UiPointerController, UiPointerEvent, UiProperties, UiScheduler, UiSelectionController, UiSharedElements, UiShortcutRegistry, UiSpring, UiTextChangeEvent, UiTimerFrameClock, UiTouchScroller, UiTransforms, UiTween, UiUnknownModifierPropertyError, UiVirtualWindow, UiVisualState, UiWheelController, UiWheelDeltaMode, UiWheelEvent, VIRTUAL_INDEX_PROP, VIRTUAL_LEAD_PROP, VIRTUAL_WINDOW_PROP, WebGPUError, WebGPUGlyphAtlas, WebGPUGlyphPages, WebGPURenderer, WebGPUSurface, WebGPUTextureCache, accumulatedOffsetTo, animateLayout, applyCanvasTextStyle, assertModifierList, assertTransitionMap, auto, autoFocus, bandOf, borderRadius, borderRadiusCorners, borderRadiusEqual, borderRadiusIsZero, borderRadiusValuesEqual, boxShadow, boxShadowArraysEqual, boxShadowsEqual, breakpoint, bufferSource, buildFontString, buildRenderList, buildSemanticsSubtree, buildSemanticsTree, bumpFontStack, bundle, canDecodeVideo, capturePointer, caretRectFor, caretVisibleAt, clampSize, clearFontStacks, clearLinkHover, clearMatchRanges, clearOverrideProperty, clearSelectionRange, clickOutside, closestPropertyName, colorToCss, colorToHex, colorToRgba, colorValuesEqual, colorsEqual, colorsEqualPalette, commandForKey, computeObjectFitRect, constraintsEqual, containerBands, contentOffset, contextMenu, contrastRatio, createCanvasSurface, createCanvasTextStyle, createElement, createEnvironmentKey, createPaintState, createTextCache, createTween, createWebGPUSurface, cubicBezier, cueAt, darkColors, darkTheme, decorated, decorationColor, decorationRect, defaultMotion, defaultShadows, defaultShapes, defaultSpacing, defaultTextStyle, defaultTypography, defaultVisualState, defineModifier, defineProperty, defineThemeExtension, demuxMp4Audio, demuxMp4Video, densityFactors, describeLength, describeOverrides, detectEditingPlatform, diffSemantics, dragSessionFor, dragSource, draggable, drawOverlayShapes, drawText, dropTarget, easings, editorFor, editorOf, fade, fillTextRects, findEnvironmentKey, findMatchesIn, findPropertyDefinition, flattenTextSpans, focusRing, fontStackFor, formatConstraints, formatExplanation, formatShortcut, fr, getPropertyNames, glyphCount, gradientPaint, gradientsEqual, graphemeBoundaries, hasDecorationPhase, hasDrawnText, hasTextLinks, hasThemeExtension, hasVisualState, highContrastColors, hoverable, iconKey, iconSource, imageSource, inheritedPropertyFlags, inheritedPropertyNames, initializeWebGPU, inputsEqual, insetPadding, insetsEqual, interactive, interpolateColor, interpolateNumber, interpolateTransform, interpolatorFor, isAutoLength, isComponentLikeElement, isEditableNode, isFrLength, isLayoutProtocol, isMinMaxTrack, isMotionRest, isMultiline, isNodeFocusable, isNodeHitTestable, isNodeInert, isNodeSelectable, isObservable, isPaintVisible, isPercentLength, isPrintable, isReadOnly, isScrollContainer, isShapeName, isTypographyRole, isUiChild, isUiElement, isUiModifier, isUiRole, isUiSemanticState, isVideoSurface, isWebGPUAvailable, labelNode, labelOrigin, layoutParagraph, layoutTextLines, lazySource, lightColors, lightTheme, lineEndAt, lineIndexAtY, lineIndexForOffset, lineLimit, lineStartAt, linear, linearGradient, linkHoverOf, linkOf, linkRunAtPointIn, markInstant, markNow, matchRangesOf, measure, measureSpan, minmax, motion, nextCaretToggle, nextGraphemeEnd, nextWordEnd, noInsets, noKeyModifiers, noModifiers, noThemeExtensions, normalizeBorderRadius, normalizeColor, normalizeStates, normalizeTextAlign, normalizeTransition, normalizeVerticalAlign, observeViewportInsets, offsetAtPoint, offsetAtPointIn, offsetAtX, offsetForVerticalMove, overrideSources, paintPictures, paintValuesEqual, paintsAfterChildren, paragraphGeometry, paragraphGeometryFrom, parseAlignContent, parseColor, parseCrossAxisAlignment, parseFlexDirection, parseMainAxisAlignment, parseShortcut, parseTransform, parseWebVtt, pathValuesEqual, percent, performanceMarksEnabled, pinchable, placeGridItems, pointerDeviceOf, pop, prepareInputSurface, pressable, previousGraphemeStart, previousWordStart, propertyEffects, propertyIsInherited, propertyValuesEqual, propertyValuesEqualByName, proportionalFontMetrics, provideEnvironment, publishInset, radialGradient, raiseContrast, rangeSource, readMp3Header, recordsEqual, registerFontStack, relativeLuminance, reorderable, repeat, replayPaint, resetOverrideWarnings, resetTransitionWarnings, resolveBoolean, resolveCursor, resolveLength, resolveMotionState, resolveNumber, resolvePaintState, resolveProperty, resolvePropertyByName, resolveString, resolvedSpansOf, rgb8, rgba, rotateFrom, runCanvasStyleInto, runFontString, sameArgs, scaleFrom, scaleSpacing, scrollPosition, scrollbarThumb, scrollbarThumbs, scrollbarZoneAt, selectableTextNodes, selectableTextOf, selectionRangeOf, selectionRects, selectionRectsIn, semanticsInertAbove, setLinkHover, setMatchRanges, setPerformanceMarks, setSelectionRange, shadowsEqual, shapesEqual, sharedElement, shortcut, shortcuts, sizeContainer, sizeGridTracks, slideDown, slideFrom, slideUp, spacingEqual, spacingSteps, spanAtOffset, spannedRunsFor, spannedTextOf, splitMp3Frames, spring, statesEqual, steps, syncEditorValue, textContentOf, textRunBackgrounds, textRunDecorations, textRunOfRecordId, textRuns, textSpansEqual, textStylesEqual, themeExtension, themeExtensionsEqual, themeTokens, themesEqual, tightenConstraints, touchActionFor, tracePathData, traceRoundedRect, transform, transformIsIdentity, transformsEqual, tween, typographyEqual, uniformBorderRadius, unknownContainerSize, validateGradient, validateRole, validateStates, validateSubgrid, videoFrameSize, videoSource, visualState, visualStatesEqual, wheelDeltaYOf, withContrast, withDensity, withOverrideSource, withThemeExtension, wordRangeAt, wordRangeIn, writeDeclaredProperty, writeOverrideProperty };
|
|
14842
|
+
export { AlignContent, AnimationDriver, BUTTON_INTERACTION, Box, Button, CARET_BLINK_MS, CARET_WIDTH, CLIP_STRIDE_BYTES, CLIP_STRIDE_FLOATS, Canvas2DRenderer, CanvasPlatformSurface, CanvasSurface, CanvasTextMeasurer, CharacterCountTextMeasurer, Column, CommandKind, Constraints, CrossAxisAlignment, DEFAULT_BLOCK_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_FONT_WEIGHT, DEFAULT_LINE_HEIGHT_FACTOR, DEFAULT_TEXT_COLOR, DefaultImageResolver, DefaultVideoResolver, DirtyFlags, DirtyNodeSet, EDITOR_PROP, ELLIPSIS, EMPTY_RECORDING, EXTERNAL_FILES, EditableLayout, EditableText, EditableTextModel, EnvironmentNotifier, FlexDirection, FocusNotifier, GLYPH_SUBPIXEL_PHASES, GlyphShaper, Grid, INSPECTOR_HEAT_MS, INSTANCE_STRIDE_BYTES, INSTANCE_STRIDE_FLOATS, IconRasterizer, LABEL_PADDING_X, LayoutEngine, LayoutInspector, LayoutNotifier, LayoutRecord, LazyColumn, LazyGrid, LazyRow, LazySheet, MARK_PREFIX, MAX_GRADIENT_STOPS, MAX_MEASURES_PER_CHILD, MOTION_CHANNELS, MOTION_REST, MOUSE_POINTER, MainAxisAlignment, Mp3Frames, NO_CLIP_INDEX, NodeTransitions, Paint, PaintPictureCache, PaintRecorder, PaintTarget, ParagraphTextMeasurer, PrimitiveKind, Responsive, Row, SCROLLBAR_FADE_MS, SCROLLBAR_HOVER_ZONE, SCROLLBAR_INSET, SCROLLBAR_LINGER_MS, SCROLLBAR_MIN_THUMB, SCROLLBAR_THICKNESS, ScrollView, Stack, TEXTURED_STRIDE_BYTES, TEXTURED_STRIDE_FLOATS, TEXT_MATCHES_PROP, TEXT_RUN_ID_SEPARATOR, TEXT_SELECTION_PROP, Text, UI_ROLES, UI_SEMANTIC_STATES, UI_SUBGRID_AXES, UiAnimation, UiAnimationFrameClock, UiBasicColors, UiBeforeInputEvent, UiBinding, UiBorderRadiuses, UiChildrenBinding, UiContainerSizeSource, UiDragSession, UiEditingController, UiEnvironment, UiEnvironmentKeys, UiEventBinding, UiEventType, UiFindController, UiFocusEvent, UiFocusManager, UiFrame, UiGestureEvent, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiHostFrameClock, UiInputDispatcher, UiInputEvent, UiInsetRegistry, UiKeyboardController, UiKeyboardEvent, UiManualFrameClock, UiModifierSet, UiNode, UiNodeType, UiPasteEvent, UiPinchEvent, UiPinchRecognizer, UiPlatformAdapter, UiPointerController, UiPointerEvent, UiProperties, UiScheduler, UiSelectionChangeEvent, UiSelectionController, UiSharedElements, UiShortcutRegistry, UiSpring, UiTextChangeEvent, UiTimerFrameClock, UiTouchScroller, UiTransforms, UiTween, UiUnknownModifierPropertyError, UiVirtualSheet, UiVirtualWindow, UiVisualState, UiWheelController, UiWheelDeltaMode, UiWheelEvent, VIRTUAL_INDEX_PROP, VIRTUAL_LEAD_PROP, VIRTUAL_SHEET_PROP, VIRTUAL_WINDOW_PROP, WebGPUError, WebGPUGlyphAtlas, WebGPUGlyphPages, WebGPURenderer, WebGPUSurface, WebGPUTextureCache, accumulatedOffsetTo, animateLayout, applyCanvasTextStyle, assertModifierList, assertTransitionMap, auto, autoFocus, bandOf, borderRadius, borderRadiusCorners, borderRadiusEqual, borderRadiusIsZero, borderRadiusValuesEqual, borderShapes, borders, boxShadow, boxShadowArraysEqual, boxShadowsEqual, breakpoint, bufferSource, buildFontString, buildRenderList, buildSemanticsSubtree, buildSemanticsTree, bumpFontStack, bundle, canDecodeVideo, capturePointer, caretRectFor, caretVisibleAt, clampSize, clearFontStacks, clearLinkHover, clearMatchRanges, clearOverrideProperty, clearSelectionRange, clickOutside, closestPropertyName, colorToCss, colorToHex, colorToRgba, colorValuesEqual, colorsEqual, colorsEqualPalette, commandForKey, computeObjectFitRect, constraintsEqual, containerBands, contentOffset, contextMenu, contrastRatio, createCanvasSurface, createCanvasTextStyle, createElement, createEnvironmentKey, createPaintState, createTextCache, createTween, createWebGPUSurface, cubicBezier, cueAt, darkColors, darkTheme, decorated, decorationColor, decorationRect, defaultMotion, defaultShadows, defaultShapes, defaultSpacing, defaultTextStyle, defaultTypography, defaultVisualState, defineModifier, defineProperty, defineThemeExtension, demuxMp4Audio, demuxMp4Video, densityFactors, describeLength, describeOverrides, detectEditingPlatform, diffSemantics, dragSessionFor, dragSource, draggable, drawOverlayShapes, drawText, dropTarget, easings, editableSpansOf, editorFor, editorOf, fade, fillTextRects, findEnvironmentKey, findMatchesIn, findPropertyDefinition, flattenTextSpans, focusRing, fontStackFor, formatConstraints, formatExplanation, formatShortcut, fr, getPropertyNames, glyphCount, gradientPaint, gradientsEqual, graphemeBoundaries, hasDecorationPhase, hasDrawnText, hasTextLinks, hasThemeExtension, hasVisualState, highContrastColors, hoverable, iconKey, iconSource, imageSource, inheritedPropertyFlags, inheritedPropertyNames, initializeWebGPU, inputsEqual, insetPadding, insetsEqual, interactive, interpolateColor, interpolateNumber, interpolateTransform, interpolatorFor, isAutoLength, isComponentLikeElement, isEditableNode, isFrLength, isLayoutProtocol, isMinMaxTrack, isMotionRest, isMultiline, isNodeFocusable, isNodeHitTestable, isNodeInert, isNodeSelectable, isNodeTabStop, isObservable, isPaintVisible, isPercentLength, isPrintable, isReadOnly, isScrollContainer, isShapeName, isTypographyRole, isUiChild, isUiElement, isUiModifier, isUiRole, isUiSemanticState, isVideoSurface, isWebGPUAvailable, labelNode, labelOrigin, layoutParagraph, layoutTextLines, lazySource, lightColors, lightTheme, lineEndAt, lineIndexAtY, lineIndexForOffset, lineLimit, lineStartAt, linear, linearGradient, linkHoverOf, linkOf, linkRunAtPointIn, markInstant, markNow, matchRangesOf, measure, measureSpan, minmax, motion, nextCaretToggle, nextGraphemeEnd, nextWordEnd, noInsets, noKeyModifiers, noModifiers, noThemeExtensions, normalizeBorderRadius, normalizeColor, normalizeStates, normalizeTextAlign, normalizeTransition, normalizeVerticalAlign, observeViewportInsets, offsetAtPoint, offsetAtPointIn, offsetAtX, offsetForVerticalMove, overrideSources, paintPictures, paintValuesEqual, paintsAfterChildren, paragraphGeometry, paragraphGeometryFrom, parseAlignContent, parseColor, parseCrossAxisAlignment, parseFlexDirection, parseMainAxisAlignment, parseShortcut, parseTransform, parseWebVtt, pathValuesEqual, percent, performanceMarksEnabled, pinchable, placeGridItems, pointerDeviceOf, pop, prepareInputSurface, pressable, previousGraphemeStart, previousWordStart, propertyEffects, propertyIsInherited, propertyValuesEqual, propertyValuesEqualByName, proportionalFontMetrics, provideEnvironment, publishInset, radialGradient, raiseContrast, rangeSource, readMp3Header, recordsEqual, registerFontStack, relativeLuminance, reorderable, repeat, replayPaint, resetOverrideWarnings, resetTransitionWarnings, resolveBoolean, resolveCursor, resolveLength, resolveMotionState, resolveNumber, resolvePaintState, resolveProperty, resolvePropertyByName, resolveString, resolvedSpansOf, rgb8, rgba, rotateFrom, runCanvasStyleInto, runFontString, sameArgs, scaleFrom, scaleSpacing, scrollPosition, scrollbarThumb, scrollbarThumbs, scrollbarZoneAt, selectableTextNodes, selectableTextOf, selectionRangeOf, selectionRects, selectionRectsIn, semanticsInertAbove, setLinkHover, setMatchRanges, setPerformanceMarks, setSelectionRange, shadowsEqual, shapesEqual, sharedElement, sheetSource, shortcut, shortcuts, sizeContainer, sizeGridTracks, slideDown, slideFrom, slideUp, spacingEqual, spacingSteps, spanAtOffset, spannedRunsFor, spannedTextOf, splitMp3Frames, spring, statesEqual, steps, syncEditorValue, textContentOf, textRunBackgrounds, textRunDecorations, textRunOfRecordId, textRuns, textSpansEqual, textStylesEqual, themeExtension, themeExtensionsEqual, themeTokens, themesEqual, tightenConstraints, touchActionFor, tracePathData, traceRoundedRect, transform, transformIsIdentity, transformsEqual, tween, typographyEqual, uniformBorderRadius, unknownContainerSize, validateGradient, validateRole, validateStates, validateSubgrid, videoFrameSize, videoSource, visualState, visualStatesEqual, wheelDeltaYOf, withContrast, withDensity, withOverrideSource, withThemeExtension, wordRangeAt, wordRangeIn, writeDeclaredProperty, writeOverrideProperty };
|
|
14671
14843
|
|
|
14672
14844
|
//# sourceMappingURL=index.js.map
|