@ng-native/fabric 0.18.1 → 0.18.2
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/dist/engine.d.ts +21 -3
- package/dist/engine.js +124 -5
- package/dist/engine.js.map +1 -1
- package/package.json +1 -1
package/dist/engine.d.ts
CHANGED
|
@@ -1172,12 +1172,30 @@ export declare class Engine implements HostEngine {
|
|
|
1172
1172
|
* keyboard's top edge, for a field the keyboard would otherwise hide.
|
|
1173
1173
|
*
|
|
1174
1174
|
* Measured in the window, all of it, so the scroll view's offset needs no tracking: it is how
|
|
1175
|
-
* far its content's top edge is above its own.
|
|
1175
|
+
* far its content's top edge is above its own. In a sheet on iOS the window is the sheet's: see
|
|
1176
|
+
* `sheetTop`.
|
|
1177
|
+
*
|
|
1178
|
+
* A scroll view under a navigation bar rests scrolled above zero, by the inset the bar adds, and
|
|
1179
|
+
* is not scrolled up past where it rests, nor past zero from below it: how far above zero it can
|
|
1180
|
+
* go is known only while it is there.
|
|
1176
1181
|
*/
|
|
1177
1182
|
reveal(node: EngineNode, options?: {
|
|
1178
1183
|
visibleBottom?: number;
|
|
1179
1184
|
margin?: number;
|
|
1180
1185
|
}): void;
|
|
1186
|
+
/**
|
|
1187
|
+
* How far down the window the iOS sheet a node is presented in starts, where that is known, so
|
|
1188
|
+
* the keyboard's `visibleBottom` can be brought into the sheet. React Native measures a view in a
|
|
1189
|
+
* presented sheet from the sheet's own top: the sheet's place on the screen is UIKit's, and the
|
|
1190
|
+
* shadow tree has the sheet at the top of the window.
|
|
1191
|
+
*
|
|
1192
|
+
* A sheet as wide as the window is attached to its bottom edge, so it starts the window's height
|
|
1193
|
+
* less its own down. One that is narrower (an iPad's form sheet, or one floating at a partial
|
|
1194
|
+
* detent) could be anywhere, and one no taller than the room above the keyboard may have risen
|
|
1195
|
+
* over it, so for those nothing is known and nothing is taken off. On Android the sheet is laid
|
|
1196
|
+
* out in the shadow tree, so nothing is either.
|
|
1197
|
+
*/
|
|
1198
|
+
private sheetTop;
|
|
1181
1199
|
/**
|
|
1182
1200
|
* The view a scroll view's content is in: an element that scrolls its overflow holds its
|
|
1183
1201
|
* children in one of its own, and any other scroll view's first child is it.
|
|
@@ -3531,8 +3549,8 @@ export declare class Engine implements HostEngine {
|
|
|
3531
3549
|
* `unstable_*EventPriority` constants) is read into the facade but not acted on. Angular's
|
|
3532
3550
|
* zoneless scheduler already flushes on a microtask, which is soon enough for taps. It stops
|
|
3533
3551
|
* being soon enough for a controlled TextInput, where the echo has to beat the next
|
|
3534
|
-
* keystroke.
|
|
3535
|
-
* a synchronous flush for discrete priority is the fix if it ever stops being enough.
|
|
3552
|
+
* keystroke. The text input's event count covers that today by letting native reject a stale
|
|
3553
|
+
* write; a synchronous flush for discrete priority is the fix if it ever stops being enough.
|
|
3536
3554
|
*
|
|
3537
3555
|
* **Nothing thrown here may escape.** Fabric calls this synchronously from C++
|
|
3538
3556
|
* (`UIManagerBinding::dispatchEventToJS`), often from inside a mounting transaction, and a throw
|
package/dist/engine.js
CHANGED
|
@@ -341,6 +341,8 @@ const ENGINE_PROPS = new Set([
|
|
|
341
341
|
'disabled',
|
|
342
342
|
'intrinsicSize',
|
|
343
343
|
'styleOverride',
|
|
344
|
+
// A text field's latest event count, committed as `mostRecentEventCount`: see `echoedEventCount`.
|
|
345
|
+
'latestEventCount',
|
|
344
346
|
...Object.values(EVENT_OPT_IN_PROPS),
|
|
345
347
|
...Object.values(VIEW_EVENT_OPT_IN_PROPS).flatMap((props) => Object.values(props)),
|
|
346
348
|
]);
|
|
@@ -3442,6 +3444,65 @@ function alignMultiline(viewName, props) {
|
|
|
3442
3444
|
return;
|
|
3443
3445
|
props['textAlignVertical'] ??= 'top';
|
|
3444
3446
|
}
|
|
3447
|
+
/** The props React Native builds a text field's text attributes from, as `BaseTextProps` reads them. */
|
|
3448
|
+
const TEXT_ATTRIBUTES = [
|
|
3449
|
+
'color',
|
|
3450
|
+
'backgroundColor',
|
|
3451
|
+
'opacity',
|
|
3452
|
+
'fontFamily',
|
|
3453
|
+
'fontSize',
|
|
3454
|
+
'fontStyle',
|
|
3455
|
+
'fontWeight',
|
|
3456
|
+
'fontVariant',
|
|
3457
|
+
'fontVariationSettings',
|
|
3458
|
+
'allowFontScaling',
|
|
3459
|
+
'maxFontSizeMultiplier',
|
|
3460
|
+
'dynamicTypeRamp',
|
|
3461
|
+
'letterSpacing',
|
|
3462
|
+
'lineHeight',
|
|
3463
|
+
'textAlign',
|
|
3464
|
+
'writingDirection',
|
|
3465
|
+
'direction',
|
|
3466
|
+
'lineBreakStrategyIOS',
|
|
3467
|
+
'lineBreakModeIOS',
|
|
3468
|
+
'textDecorationLine',
|
|
3469
|
+
'textDecorationColor',
|
|
3470
|
+
'textDecorationStyle',
|
|
3471
|
+
'textShadowColor',
|
|
3472
|
+
'textShadowOffset',
|
|
3473
|
+
'textShadowRadius',
|
|
3474
|
+
'textTransform',
|
|
3475
|
+
];
|
|
3476
|
+
/**
|
|
3477
|
+
* The prop a text field that echoes its text puts its latest event count in, for
|
|
3478
|
+
* `echoedEventCount` to commit as `mostRecentEventCount`. It never reaches native.
|
|
3479
|
+
*/
|
|
3480
|
+
const LATEST_EVENT_COUNT = 'latestEventCount';
|
|
3481
|
+
/**
|
|
3482
|
+
* The event count a text field that echoes its text is committed with: the latest on Android, and
|
|
3483
|
+
* on iOS 0, the count native starts at, but for a commit that changes a multiline field's text
|
|
3484
|
+
* style, which carries the latest.
|
|
3485
|
+
*
|
|
3486
|
+
* iOS takes the `text` prop only with the count it holds, and then replaces the field's text unless
|
|
3487
|
+
* the two compare equal, attributes and all. UIKit gives an empty field's first typed text a
|
|
3488
|
+
* paragraph style whose line break strategy differs from the one RN compares it with, so an echo
|
|
3489
|
+
* of that very text replaces it, and the keystrokes queued behind the replace are lost. Text from
|
|
3490
|
+
* code goes as `setTextAndSelection` instead, which carries the count. A restyle still needs the
|
|
3491
|
+
* replace on a multiline field: a single-line one restyles its text from its new default
|
|
3492
|
+
* attributes, a multiline one only takes them for what is typed next.
|
|
3493
|
+
*
|
|
3494
|
+
* A field that sets `mostRecentEventCount` itself, and never echoes, is left as it is.
|
|
3495
|
+
*/
|
|
3496
|
+
function echoedEventCount(viewName, props, previous) {
|
|
3497
|
+
const latest = props[LATEST_EVENT_COUNT];
|
|
3498
|
+
if (latest === undefined)
|
|
3499
|
+
return;
|
|
3500
|
+
delete props[LATEST_EVENT_COUNT];
|
|
3501
|
+
const restyled = props['multiline'] === true &&
|
|
3502
|
+
previous !== undefined &&
|
|
3503
|
+
TEXT_ATTRIBUTES.some((key) => !Object.is(previous[key], props[key]));
|
|
3504
|
+
props['mostRecentEventCount'] = viewName !== 'TextInput' || restyled ? latest : 0;
|
|
3505
|
+
}
|
|
3445
3506
|
/** The props of a run of text that change how tall its line is drawn. */
|
|
3446
3507
|
const SIZES_A_RUN = new Set(['style', 'allowFontScaling', 'maxFontSizeMultiplier']);
|
|
3447
3508
|
/**
|
|
@@ -3631,6 +3692,12 @@ function seenByCss(merged) {
|
|
|
3631
3692
|
/** A screen of a native stack, which is what the swipe back is a gesture of. */
|
|
3632
3693
|
const STACK_SCREEN = 'RNSScreen';
|
|
3633
3694
|
const FIXED_BOUNDARIES = new Set([MODAL_HOST, STACK_SCREEN, 'RNSFullWindowOverlay']);
|
|
3695
|
+
/**
|
|
3696
|
+
* The presentations that can leave a sheet short of the top of the screen on iOS: a screen's
|
|
3697
|
+
* `stackPresentation` (`modal` is a page sheet from iOS 13) or a modal host's `presentationStyle`.
|
|
3698
|
+
* The others cover the screen, or what they are presented over.
|
|
3699
|
+
*/
|
|
3700
|
+
const SHEETS = new Set(['modal', 'formSheet', 'pageSheet']);
|
|
3634
3701
|
/** What an element's `touch-action` is: bound on it, or from the rules it matches. */
|
|
3635
3702
|
function touchActionOf(node) {
|
|
3636
3703
|
return inlineOf(node)['touchAction'] ?? node.styleCache?.style['touchAction'];
|
|
@@ -6011,7 +6078,12 @@ export class Engine {
|
|
|
6011
6078
|
* keyboard's top edge, for a field the keyboard would otherwise hide.
|
|
6012
6079
|
*
|
|
6013
6080
|
* Measured in the window, all of it, so the scroll view's offset needs no tracking: it is how
|
|
6014
|
-
* far its content's top edge is above its own.
|
|
6081
|
+
* far its content's top edge is above its own. In a sheet on iOS the window is the sheet's: see
|
|
6082
|
+
* `sheetTop`.
|
|
6083
|
+
*
|
|
6084
|
+
* A scroll view under a navigation bar rests scrolled above zero, by the inset the bar adds, and
|
|
6085
|
+
* is not scrolled up past where it rests, nor past zero from below it: how far above zero it can
|
|
6086
|
+
* go is known only while it is there.
|
|
6015
6087
|
*/
|
|
6016
6088
|
reveal(node, options = {}) {
|
|
6017
6089
|
const scroll = scrollingDown(node);
|
|
@@ -6019,23 +6091,68 @@ export class Engine {
|
|
|
6019
6091
|
if (!scroll || !content)
|
|
6020
6092
|
return;
|
|
6021
6093
|
const margin = options.margin ?? 16;
|
|
6094
|
+
const { visibleBottom } = options;
|
|
6095
|
+
const covered = visibleBottom === undefined ? Infinity : visibleBottom - this.sheetTop(node, visibleBottom);
|
|
6022
6096
|
this.measure(scroll, (view) => {
|
|
6023
6097
|
this.measureHandle(content, (inner) => {
|
|
6024
6098
|
this.measure(node, (target) => {
|
|
6025
6099
|
const offset = view.y - inner.y;
|
|
6026
6100
|
const top = view.y + margin;
|
|
6027
|
-
const bottom = Math.min(view.y + view.height,
|
|
6101
|
+
const bottom = Math.min(view.y + view.height, covered) - margin;
|
|
6028
6102
|
let by = 0;
|
|
6029
6103
|
if (target.y + target.height > bottom)
|
|
6030
6104
|
by = target.y + target.height - bottom;
|
|
6031
6105
|
else if (target.y < top)
|
|
6032
6106
|
by = target.y - top;
|
|
6033
6107
|
if (by !== 0)
|
|
6034
|
-
this.dispatchCommand(scroll, 'scrollTo', [
|
|
6108
|
+
this.dispatchCommand(scroll, 'scrollTo', [
|
|
6109
|
+
0,
|
|
6110
|
+
Math.max(Math.min(0, offset), offset + by),
|
|
6111
|
+
true,
|
|
6112
|
+
]);
|
|
6035
6113
|
});
|
|
6036
6114
|
});
|
|
6037
6115
|
});
|
|
6038
6116
|
}
|
|
6117
|
+
/**
|
|
6118
|
+
* How far down the window the iOS sheet a node is presented in starts, where that is known, so
|
|
6119
|
+
* the keyboard's `visibleBottom` can be brought into the sheet. React Native measures a view in a
|
|
6120
|
+
* presented sheet from the sheet's own top: the sheet's place on the screen is UIKit's, and the
|
|
6121
|
+
* shadow tree has the sheet at the top of the window.
|
|
6122
|
+
*
|
|
6123
|
+
* A sheet as wide as the window is attached to its bottom edge, so it starts the window's height
|
|
6124
|
+
* less its own down. One that is narrower (an iPad's form sheet, or one floating at a partial
|
|
6125
|
+
* detent) could be anywhere, and one no taller than the room above the keyboard may have risen
|
|
6126
|
+
* over it, so for those nothing is known and nothing is taken off. On Android the sheet is laid
|
|
6127
|
+
* out in the shadow tree, so nothing is either.
|
|
6128
|
+
*/
|
|
6129
|
+
sheetTop(node, keyboardTop) {
|
|
6130
|
+
if (platformOS !== 'ios')
|
|
6131
|
+
return 0;
|
|
6132
|
+
for (let up = node.parent; up; up = up.parent) {
|
|
6133
|
+
if (up.nodeKind !== 'element')
|
|
6134
|
+
continue;
|
|
6135
|
+
const viewName = viewNameOf(up);
|
|
6136
|
+
// React Native's own modal measures what it holds from its own top, wherever it is written.
|
|
6137
|
+
const modal = viewName === MODAL_HOST;
|
|
6138
|
+
const presented = modal
|
|
6139
|
+
? up.props['presentationStyle']
|
|
6140
|
+
: viewName === STACK_SCREEN && up.props['stackPresentation'];
|
|
6141
|
+
// A screen pushed in a stack is measured in whatever it is pushed in.
|
|
6142
|
+
if (!modal && (!presented || presented === 'push'))
|
|
6143
|
+
continue;
|
|
6144
|
+
if (!SHEETS.has(String(presented)))
|
|
6145
|
+
return 0;
|
|
6146
|
+
let top = 0;
|
|
6147
|
+
this.measure(up, (sheet) => {
|
|
6148
|
+
if (sheet.width < this.viewportSize.width || sheet.height <= keyboardTop)
|
|
6149
|
+
return;
|
|
6150
|
+
top = Math.max(0, this.viewportSize.height - (modal ? 0 : sheet.y) - sheet.height);
|
|
6151
|
+
});
|
|
6152
|
+
return top;
|
|
6153
|
+
}
|
|
6154
|
+
return 0;
|
|
6155
|
+
}
|
|
6039
6156
|
/**
|
|
6040
6157
|
* The view a scroll view's content is in: an element that scrolls its overflow holds its
|
|
6041
6158
|
* children in one of its own, and any other scroll view's first child is it.
|
|
@@ -10323,6 +10440,8 @@ export class Engine {
|
|
|
10323
10440
|
// On iOS: Android's field says the baseline of the line it is sized and centred by.
|
|
10324
10441
|
if (field)
|
|
10325
10442
|
centreSingleLine(viewName, merged, this.fontScale, isOnBaseline(node, viewName));
|
|
10443
|
+
if (field)
|
|
10444
|
+
echoedEventCount(viewName, merged, node.committed?.props);
|
|
10326
10445
|
// After a transition, which eases the basis as the basis it was written as.
|
|
10327
10446
|
basisAsSize(node, merged);
|
|
10328
10447
|
// Each asked of the key it reads first, here and in `engineLayout`: it is asked of every node
|
|
@@ -14006,8 +14125,8 @@ export class Engine {
|
|
|
14006
14125
|
* `unstable_*EventPriority` constants) is read into the facade but not acted on. Angular's
|
|
14007
14126
|
* zoneless scheduler already flushes on a microtask, which is soon enough for taps. It stops
|
|
14008
14127
|
* being soon enough for a controlled TextInput, where the echo has to beat the next
|
|
14009
|
-
* keystroke.
|
|
14010
|
-
* a synchronous flush for discrete priority is the fix if it ever stops being enough.
|
|
14128
|
+
* keystroke. The text input's event count covers that today by letting native reject a stale
|
|
14129
|
+
* write; a synchronous flush for discrete priority is the fix if it ever stops being enough.
|
|
14011
14130
|
*
|
|
14012
14131
|
* **Nothing thrown here may escape.** Fabric calls this synchronously from C++
|
|
14013
14132
|
* (`UIManagerBinding::dispatchEventToJS`), often from inside a mounting transaction, and a throw
|