create-octane-xplat 0.5.0 → 0.7.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/README.md +37 -12
- package/package.json +6 -1
- package/template/.agents/skills/xplat/SKILL.md +18 -11
- package/template/.agents/skills/xplat/references/components.md +78 -29
- package/template/.agents/skills/xplat/references/navigation.md +8 -5
- package/template/.agents/skills/xplat/references/platform.md +21 -21
- package/template/.agents/skills/xplat/references/styling.md +1 -1
- package/template/AGENTS.md +2 -1
- package/template/App_Resources/iOS/src/ShaderEffectRegistration.swift +31 -0
- package/template/package.json +8 -9
- package/template/patches/@nativescript-community__octane@0.2.1.patch +16 -0
- package/template/patches/@nativescript__core@9.1.2.patch +752 -0
- package/template/patches/@nativescript__core@9.1.3-next.2.patch +428 -0
- package/template/patches/@nativescript__vite@8.0.11.patch +531 -261
- package/template/patches/@nativescript__vite@8.0.13.patch +233 -0
- package/template/patches/css@3.0.0.patch +52 -0
- package/template/patches/octane@0.6.3.patch +1149 -0
- package/template/pnpm-workspace.yaml +54 -12
- package/template/src/App.tsrx +6 -4
- package/template/tsconfig.json +1 -1
- package/template/tsconfig.native.json +1 -1
- package/template/vite.config.ts +3 -3
- /package/template/src/{index.native.ts → index.ts} +0 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
diff --git a/ui/layouts/flexbox-layout/flexbox-layout-common.js b/ui/layouts/flexbox-layout/flexbox-layout-common.js
|
|
2
|
+
index 54a037138b909446c71da47eb1cd682d5286fb18..2e1eafc92ff54e4c952b99dfd89816b222981aa3 100644
|
|
3
|
+
--- a/ui/layouts/flexbox-layout/flexbox-layout-common.js
|
|
4
|
+
+++ b/ui/layouts/flexbox-layout/flexbox-layout-common.js
|
|
5
|
+
@@ -193,7 +193,21 @@ let FlexboxLayoutBase = class FlexboxLayoutBase extends LayoutBase {
|
|
6
|
+
validateArgs(view).style.alignSelf = align;
|
|
7
|
+
}
|
|
8
|
+
static getAlignSelf(view) {
|
|
9
|
+
- return validateArgs(view).style.alignSelf;
|
|
10
|
+
+ const style = validateArgs(view).style;
|
|
11
|
+
+ const alignSelf = style.alignSelf;
|
|
12
|
+
+ if (alignSelf !== 'auto' && alignSelf !== 'stretch') {
|
|
13
|
+
+ return alignSelf;
|
|
14
|
+
+ }
|
|
15
|
+
+ const parent = view.parent;
|
|
16
|
+
+ if (!(parent instanceof FlexboxLayoutBase)) {
|
|
17
|
+
+ return alignSelf;
|
|
18
|
+
+ }
|
|
19
|
+
+ if (alignSelf === 'auto' && parent.alignItems !== 'stretch') {
|
|
20
|
+
+ return alignSelf;
|
|
21
|
+
+ }
|
|
22
|
+
+ const isRow = parent.flexDirection === 'row' || parent.flexDirection === 'row-reverse';
|
|
23
|
+
+ const crossSize = isRow ? style.height : style.width;
|
|
24
|
+
+ return crossSize == null || crossSize === 'auto' ? alignSelf : 'flex-start';
|
|
25
|
+
}
|
|
26
|
+
static setFlexWrapBefore(view, wrap) {
|
|
27
|
+
validateArgs(view).style.flexWrapBefore = wrap;
|
|
28
|
+
diff --git a/ui/layouts/flexbox-layout/index.android.js b/ui/layouts/flexbox-layout/index.android.js
|
|
29
|
+
index ed354fddfe1caf982e73b603798cb8f5bf0d3501..58dfc04a6a4549c8fe52f04022fff82b321b0301 100644
|
|
30
|
+
--- a/ui/layouts/flexbox-layout/index.android.js
|
|
31
|
+
+++ b/ui/layouts/flexbox-layout/index.android.js
|
|
32
|
+
@@ -1,7 +1,7 @@
|
|
33
|
+
import { FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, FlexboxLayoutBase, orderProperty, flexGrowProperty, flexShrinkProperty, flexWrapBeforeProperty, alignSelfProperty, AlignSelf, flexDirectionProperty, flexWrapProperty, justifyContentProperty, alignItemsProperty, alignContentProperty } from './flexbox-layout-common';
|
|
34
|
+
import { View } from '../../core/view';
|
|
35
|
+
import { Length } from '../../styling/length-shared';
|
|
36
|
+
-import { columnGapProperty, rowGapProperty } from '../../styling/style-properties';
|
|
37
|
+
+import { columnGapProperty, rowGapProperty, widthProperty, heightProperty } from '../../styling/style-properties';
|
|
38
|
+
export * from './flexbox-layout-common';
|
|
39
|
+
let widgetFlexboxLayout;
|
|
40
|
+
let widgetLayoutParams;
|
|
41
|
+
@@ -27,6 +27,25 @@ View.prototype[flexGrowProperty.setNative] = makeNativeSetter((lp, value) => (lp
|
|
42
|
+
View.prototype[flexShrinkProperty.setNative] = makeNativeSetter((lp, value) => (lp.flexShrink = value));
|
|
43
|
+
View.prototype[flexWrapBeforeProperty.setNative] = makeNativeSetter((lp, value) => (lp.wrapBefore = value));
|
|
44
|
+
View.prototype[alignSelfProperty.setNative] = makeNativeSetter((lp, value) => (lp.alignSelf = alignSelfMap[value]));
|
|
45
|
+
+function refreshFlexboxParent(child) {
|
|
46
|
+
+ const parent = child.parent;
|
|
47
|
+
+ if (parent instanceof FlexboxLayoutBase) {
|
|
48
|
+
+ parent._updateNativeLayoutParams(child);
|
|
49
|
+
+ }
|
|
50
|
+
+}
|
|
51
|
+
+function wrapChildLayoutProperty(property) {
|
|
52
|
+
+ const setter = View.prototype[property.setNative];
|
|
53
|
+
+ if (typeof setter !== 'function') {
|
|
54
|
+
+ return;
|
|
55
|
+
+ }
|
|
56
|
+
+ View.prototype[property.setNative] = function (value) {
|
|
57
|
+
+ setter.call(this, value);
|
|
58
|
+
+ refreshFlexboxParent(this);
|
|
59
|
+
+ };
|
|
60
|
+
+}
|
|
61
|
+
+wrapChildLayoutProperty(widthProperty);
|
|
62
|
+
+wrapChildLayoutProperty(heightProperty);
|
|
63
|
+
+wrapChildLayoutProperty(alignSelfProperty);
|
|
64
|
+
const flexDirectionMap = {
|
|
65
|
+
[FlexDirection.ROW]: 0, //FlexboxLayoutWidget.FLEX_DIRECTION_ROW,
|
|
66
|
+
[FlexDirection.ROW_REVERSE]: 1, //FlexboxLayoutWidget.FLEX_DIRECTION_ROW_REVERSE,
|
|
67
|
+
@@ -85,6 +104,7 @@ export class FlexboxLayout extends FlexboxLayoutBase {
|
|
68
|
+
}
|
|
69
|
+
[flexDirectionProperty.setNative](flexDirection) {
|
|
70
|
+
this.nativeViewProtected.setFlexDirection(flexDirectionMap[flexDirection]);
|
|
71
|
+
+ this.eachLayoutChild((child) => this._updateNativeLayoutParams(child));
|
|
72
|
+
}
|
|
73
|
+
[flexWrapProperty.getDefault]() {
|
|
74
|
+
return flexWrapProperty.defaultValue;
|
|
75
|
+
@@ -103,6 +123,7 @@ export class FlexboxLayout extends FlexboxLayoutBase {
|
|
76
|
+
}
|
|
77
|
+
[alignItemsProperty.setNative](alignItems) {
|
|
78
|
+
this.nativeViewProtected.setAlignItems(alignItemsMap[alignItems]);
|
|
79
|
+
+ this.eachLayoutChild((child) => this._updateNativeLayoutParams(child));
|
|
80
|
+
}
|
|
81
|
+
[alignContentProperty.getDefault]() {
|
|
82
|
+
return alignContentProperty.defaultValue;
|
|
83
|
+
@@ -136,7 +157,7 @@ export class FlexboxLayout extends FlexboxLayoutBase {
|
|
84
|
+
lp.flexGrow = style.flexGrow;
|
|
85
|
+
lp.flexShrink = style.flexShrink;
|
|
86
|
+
lp.wrapBefore = style.flexWrapBefore;
|
|
87
|
+
- lp.alignSelf = alignSelfMap[style.alignSelf];
|
|
88
|
+
+ lp.alignSelf = alignSelfMap[FlexboxLayoutBase.getAlignSelf(child)];
|
|
89
|
+
child.nativeViewProtected.setLayoutParams(lp);
|
|
90
|
+
}
|
|
91
|
+
_setChildMinWidthNative(child, value) {
|
|
92
|
+
diff --git a/ui/styling/background.ios.js b/ui/styling/background.ios.js
|
|
93
|
+
index ed4b6883680229ffd1aa92a7bbcba2d36f1efebc..3882cad9a8d17fb5b8a80a4795b0161bc2f82e28 100644
|
|
94
|
+
--- a/ui/styling/background.ios.js
|
|
95
|
+
+++ b/ui/styling/background.ios.js
|
|
96
|
+
@@ -197,8 +197,8 @@ export var ios;
|
|
97
|
+
bottomLeft: cappedRadius + spreadRadius,
|
|
98
|
+
bottomRight: cappedRadius + spreadRadius,
|
|
99
|
+
};
|
|
100
|
+
- innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
|
|
101
|
+
- shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius);
|
|
102
|
+
+ innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii, 0, background.cornerShape === CoreTypes.CornerShape.squircle);
|
|
103
|
+
+ shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius, background.cornerShape === CoreTypes.CornerShape.squircle);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const outerTopLeftRadius = layout.toDeviceIndependentPixels(background.borderTopLeftRadius);
|
|
107
|
+
@@ -223,8 +223,8 @@ export var ios;
|
|
108
|
+
bottomLeft: cappedOuterRadii.bottomLeft > 0 ? cappedOuterRadii.bottomLeft + spreadRadius : cappedOuterRadii.bottomLeft,
|
|
109
|
+
bottomRight: cappedOuterRadii.bottomRight > 0 ? cappedOuterRadii.bottomRight + spreadRadius : cappedOuterRadii.bottomRight,
|
|
110
|
+
};
|
|
111
|
+
- innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
|
|
112
|
+
- shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius);
|
|
113
|
+
+ innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii, 0, background.cornerShape === CoreTypes.CornerShape.squircle);
|
|
114
|
+
+ shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius, background.cornerShape === CoreTypes.CornerShape.squircle);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
@@ -292,7 +292,7 @@ export var ios;
|
|
119
|
+
function generateNonUniformBorderOuterClipRoundedPath(view, bounds) {
|
|
120
|
+
const background = view.style.backgroundInternal;
|
|
121
|
+
const cappedOuterRadii = calculateNonUniformBorderCappedRadii(bounds, background);
|
|
122
|
+
- return generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
|
|
123
|
+
+ return generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii, 0, background.cornerShape === CoreTypes.CornerShape.squircle);
|
|
124
|
+
}
|
|
125
|
+
ios.generateNonUniformBorderOuterClipRoundedPath = generateNonUniformBorderOuterClipRoundedPath;
|
|
126
|
+
function generateNonUniformMultiColorBorderRoundedPaths(view, bounds) {
|
|
127
|
+
@@ -626,7 +626,7 @@ function drawNonUniformBorders(nativeView, background) {
|
|
128
|
+
layer.cornerRadius = 0;
|
|
129
|
+
const cappedOuterRadii = calculateNonUniformBorderCappedRadii(layerBounds, background);
|
|
130
|
+
if (nativeView.maskType === iosViewUtils.LayerMask.BORDER && layer.mask instanceof CAShapeLayer) {
|
|
131
|
+
- layer.mask.path = generateNonUniformBorderOuterClipPath(layerBounds, cappedOuterRadii);
|
|
132
|
+
+ layer.mask.path = generateNonUniformBorderOuterClipPath(layerBounds, cappedOuterRadii, 0, background.cornerShape === CoreTypes.CornerShape.squircle);
|
|
133
|
+
}
|
|
134
|
+
if (background.hasBorderWidth()) {
|
|
135
|
+
if (!nativeView.hasNonUniformBorder) {
|
|
136
|
+
@@ -694,15 +694,57 @@ function calculateInnerBorderClipRadius(radius, insetX, insetY) {
|
|
137
|
+
maxRadius: innerMaxRadius,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
+// Squircle corner curve — superellipse |x|^n + |y|^n = r^n with n=4, the
|
|
141
|
+
+// approximation of Apple's "continuous" corner that CSS `corner-shape:
|
|
142
|
+
+// squircle` specifies. Sampled as a short polyline per corner (the mask is
|
|
143
|
+
+// rasterized once per bounds change, so segment count doesn't matter at
|
|
144
|
+
+// screen scale).
|
|
145
|
+
+const SQUIRCLE_EXPONENT = 4;
|
|
146
|
+
+const SQUIRCLE_CORNER_STEPS = 12;
|
|
147
|
+
+/**
|
|
148
|
+
+ * Appends a straight line to the incoming tangent point, then a squircle
|
|
149
|
+
+ * quarter arc to the exit tangent — same semantics as CGPathAddArcToPoint.
|
|
150
|
+
+ *
|
|
151
|
+
+ * @param path
|
|
152
|
+
+ * @param t1x/t1y tangent point on the incoming edge
|
|
153
|
+
+ * @param t2x/t2y tangent point on the outgoing edge
|
|
154
|
+
+ * @param vx/vy corner vertex where the two edges would meet
|
|
155
|
+
+ */
|
|
156
|
+
+function addSquircleArcToPoint(path, t1x, t1y, t2x, t2y, vx, vy) {
|
|
157
|
+
+ CGPathAddLineToPoint(path, null, t1x, t1y);
|
|
158
|
+
+ // The corner-box corner opposite the vertex is the arc's center.
|
|
159
|
+
+ const cx = t1x + t2x - vx;
|
|
160
|
+
+ const cy = t1y + t2y - vy;
|
|
161
|
+
+ const e = 2 / SQUIRCLE_EXPONENT;
|
|
162
|
+
+ // t1 sits on a horizontal edge when its offset from the center is
|
|
163
|
+
+ // vertical; parametrize θ: 0 → π/2 so the polyline always runs t1 → t2.
|
|
164
|
+
+ if (Math.abs(t1y - cy) >= Math.abs(t1x - cx)) {
|
|
165
|
+
+ const ax = t2x - cx;
|
|
166
|
+
+ const ay = t1y - cy;
|
|
167
|
+
+ for (let i = 1; i <= SQUIRCLE_CORNER_STEPS; i++) {
|
|
168
|
+
+ const a = (i / SQUIRCLE_CORNER_STEPS) * (Math.PI / 2);
|
|
169
|
+
+ CGPathAddLineToPoint(path, null, cx + ax * Math.pow(Math.sin(a), e), cy + ay * Math.pow(Math.cos(a), e));
|
|
170
|
+
+ }
|
|
171
|
+
+ }
|
|
172
|
+
+ else {
|
|
173
|
+
+ const ax = t1x - cx;
|
|
174
|
+
+ const ay = t2y - cy;
|
|
175
|
+
+ for (let i = 1; i <= SQUIRCLE_CORNER_STEPS; i++) {
|
|
176
|
+
+ const a = (i / SQUIRCLE_CORNER_STEPS) * (Math.PI / 2);
|
|
177
|
+
+ CGPathAddLineToPoint(path, null, cx + ax * Math.pow(Math.cos(a), e), cy + ay * Math.pow(Math.sin(a), e));
|
|
178
|
+
+ }
|
|
179
|
+
+ }
|
|
180
|
+
+}
|
|
181
|
+
/**
|
|
182
|
+
* Generates a path that represents the rounded view area.
|
|
183
|
+
*
|
|
184
|
+
* @param bounds
|
|
185
|
+
* @param cappedRadii
|
|
186
|
+
* @param offset
|
|
187
|
+
+ * @param squircle draw corner arcs as squircles instead of circular arcs
|
|
188
|
+
* @returns
|
|
189
|
+
*/
|
|
190
|
+
-function generateNonUniformBorderOuterClipPath(bounds, cappedRadii, offset = 0) {
|
|
191
|
+
+function generateNonUniformBorderOuterClipPath(bounds, cappedRadii, offset = 0, squircle = false) {
|
|
192
|
+
const { width, height } = bounds.size;
|
|
193
|
+
const { x, y } = bounds.origin;
|
|
194
|
+
const left = x - offset;
|
|
195
|
+
@@ -711,10 +753,18 @@ function generateNonUniformBorderOuterClipPath(bounds, cappedRadii, offset = 0)
|
|
196
|
+
const bottom = y + height + offset;
|
|
197
|
+
const clipPath = CGPathCreateMutable();
|
|
198
|
+
CGPathMoveToPoint(clipPath, null, left + cappedRadii.topLeft, top);
|
|
199
|
+
- CGPathAddArcToPoint(clipPath, null, right, top, right, top + cappedRadii.topRight, cappedRadii.topRight);
|
|
200
|
+
- CGPathAddArcToPoint(clipPath, null, right, bottom, right - cappedRadii.bottomRight, bottom, cappedRadii.bottomRight);
|
|
201
|
+
- CGPathAddArcToPoint(clipPath, null, left, bottom, left, bottom - cappedRadii.bottomLeft, cappedRadii.bottomLeft);
|
|
202
|
+
- CGPathAddArcToPoint(clipPath, null, left, top, left + cappedRadii.topLeft, top, cappedRadii.topLeft);
|
|
203
|
+
+ if (squircle) {
|
|
204
|
+
+ addSquircleArcToPoint(clipPath, right - cappedRadii.topRight, top, right, top + cappedRadii.topRight, right, top);
|
|
205
|
+
+ addSquircleArcToPoint(clipPath, right, bottom - cappedRadii.bottomRight, right - cappedRadii.bottomRight, bottom, right, bottom);
|
|
206
|
+
+ addSquircleArcToPoint(clipPath, left + cappedRadii.bottomLeft, bottom, left, bottom - cappedRadii.bottomLeft, left, bottom);
|
|
207
|
+
+ addSquircleArcToPoint(clipPath, left, top + cappedRadii.topLeft, left + cappedRadii.topLeft, top, left, top);
|
|
208
|
+
+ }
|
|
209
|
+
+ else {
|
|
210
|
+
+ CGPathAddArcToPoint(clipPath, null, right, top, right, top + cappedRadii.topRight, cappedRadii.topRight);
|
|
211
|
+
+ CGPathAddArcToPoint(clipPath, null, right, bottom, right - cappedRadii.bottomRight, bottom, cappedRadii.bottomRight);
|
|
212
|
+
+ CGPathAddArcToPoint(clipPath, null, left, bottom, left, bottom - cappedRadii.bottomLeft, cappedRadii.bottomLeft);
|
|
213
|
+
+ CGPathAddArcToPoint(clipPath, null, left, top, left + cappedRadii.topLeft, top, cappedRadii.topLeft);
|
|
214
|
+
+ }
|
|
215
|
+
CGPathCloseSubpath(clipPath);
|
|
216
|
+
return clipPath;
|
|
217
|
+
}
|
|
218
|
+
@@ -747,6 +797,7 @@ function generateNonUniformBorderInnerClipPath(bounds, background, cappedOuterRa
|
|
219
|
+
const cappedBorderLeftWidth = borderLeftWidth && borderLeftWidth * Math.min(1, width / borderHWidth);
|
|
220
|
+
const clipPath = CGPathCreateMutable();
|
|
221
|
+
CGPathAddRect(clipPath, null, CGRectMake(x, y, width, height));
|
|
222
|
+
+ const squircle = background.cornerShape === CoreTypes.CornerShape.squircle;
|
|
223
|
+
// Inner clip paths
|
|
224
|
+
if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
225
|
+
CGPathMoveToPoint(clipPath, null, position.left + cappedOuterRadii.topLeft, position.top + cappedBorderTopWidth);
|
|
226
|
+
@@ -756,32 +807,52 @@ function generateNonUniformBorderInnerClipPath(bounds, background, cappedOuterRa
|
|
227
|
+
}
|
|
228
|
+
if (cappedBorderTopWidth > 0 || cappedBorderRightWidth > 0) {
|
|
229
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.topRight, cappedBorderRightWidth, cappedBorderTopWidth);
|
|
230
|
+
- const innerTopRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.top + cappedBorderTopWidth + yRadius);
|
|
231
|
+
- CGPathAddArc(clipPath, innerTopRightTransform, 0, 0, maxRadius, (Math.PI * 3) / 2, 0, false);
|
|
232
|
+
+ if (squircle) {
|
|
233
|
+
+ addSquircleArcToPoint(clipPath, position.right - cappedBorderRightWidth - xRadius, position.top + cappedBorderTopWidth, position.right - cappedBorderRightWidth, position.top + cappedBorderTopWidth + yRadius, position.right - cappedBorderRightWidth, position.top + cappedBorderTopWidth);
|
|
234
|
+
+ }
|
|
235
|
+
+ else {
|
|
236
|
+
+ const innerTopRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.top + cappedBorderTopWidth + yRadius);
|
|
237
|
+
+ CGPathAddArc(clipPath, innerTopRightTransform, 0, 0, maxRadius, (Math.PI * 3) / 2, 0, false);
|
|
238
|
+
+ }
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
CGPathAddLineToPoint(clipPath, null, position.right, position.top);
|
|
242
|
+
}
|
|
243
|
+
if (cappedBorderBottomWidth > 0 || cappedBorderRightWidth > 0) {
|
|
244
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.bottomRight, cappedBorderRightWidth, cappedBorderBottomWidth);
|
|
245
|
+
- const innerBottomRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
|
|
246
|
+
- CGPathAddArc(clipPath, innerBottomRightTransform, 0, 0, maxRadius, 0, Math.PI / 2, false);
|
|
247
|
+
+ if (squircle) {
|
|
248
|
+
+ addSquircleArcToPoint(clipPath, position.right - cappedBorderRightWidth, position.bottom - cappedBorderBottomWidth - yRadius, position.right - cappedBorderRightWidth - xRadius, position.bottom - cappedBorderBottomWidth, position.right - cappedBorderRightWidth, position.bottom - cappedBorderBottomWidth);
|
|
249
|
+
+ }
|
|
250
|
+
+ else {
|
|
251
|
+
+ const innerBottomRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
|
|
252
|
+
+ CGPathAddArc(clipPath, innerBottomRightTransform, 0, 0, maxRadius, 0, Math.PI / 2, false);
|
|
253
|
+
+ }
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
CGPathAddLineToPoint(clipPath, null, position.right, position.bottom);
|
|
257
|
+
}
|
|
258
|
+
if (cappedBorderBottomWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
259
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.bottomLeft, cappedBorderLeftWidth, cappedBorderBottomWidth);
|
|
260
|
+
- const innerBottomLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
|
|
261
|
+
- CGPathAddArc(clipPath, innerBottomLeftTransform, 0, 0, maxRadius, Math.PI / 2, Math.PI, false);
|
|
262
|
+
+ if (squircle) {
|
|
263
|
+
+ addSquircleArcToPoint(clipPath, position.left + cappedBorderLeftWidth + xRadius, position.bottom - cappedBorderBottomWidth, position.left + cappedBorderLeftWidth, position.bottom - cappedBorderBottomWidth - yRadius, position.left + cappedBorderLeftWidth, position.bottom - cappedBorderBottomWidth);
|
|
264
|
+
+ }
|
|
265
|
+
+ else {
|
|
266
|
+
+ const innerBottomLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
|
|
267
|
+
+ CGPathAddArc(clipPath, innerBottomLeftTransform, 0, 0, maxRadius, Math.PI / 2, Math.PI, false);
|
|
268
|
+
+ }
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
CGPathAddLineToPoint(clipPath, null, position.left, position.bottom);
|
|
272
|
+
}
|
|
273
|
+
if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
|
|
274
|
+
const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.topLeft, cappedBorderLeftWidth, cappedBorderTopWidth);
|
|
275
|
+
- const innerTopLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.top + cappedBorderTopWidth + yRadius);
|
|
276
|
+
- CGPathAddArc(clipPath, innerTopLeftTransform, 0, 0, maxRadius, Math.PI, (Math.PI * 3) / 2, false);
|
|
277
|
+
+ if (squircle) {
|
|
278
|
+
+ addSquircleArcToPoint(clipPath, position.left + cappedBorderLeftWidth, position.top + cappedBorderTopWidth + yRadius, position.left + cappedBorderLeftWidth + xRadius, position.top + cappedBorderTopWidth, position.left + cappedBorderLeftWidth, position.top + cappedBorderTopWidth);
|
|
279
|
+
+ }
|
|
280
|
+
+ else {
|
|
281
|
+
+ const innerTopLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.top + cappedBorderTopWidth + yRadius);
|
|
282
|
+
+ CGPathAddArc(clipPath, innerTopLeftTransform, 0, 0, maxRadius, Math.PI, (Math.PI * 3) / 2, false);
|
|
283
|
+
+ }
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
CGPathAddLineToPoint(clipPath, null, position.left, position.top);
|
|
287
|
+
diff --git a/css/css-tree-parser.js b/css/css-tree-parser.js
|
|
288
|
+
index 0c13e7e..7ce38a9 100644
|
|
289
|
+
--- a/css/css-tree-parser.js
|
|
290
|
+
+++ b/css/css-tree-parser.js
|
|
291
|
+
@@ -66,6 +66,10 @@ function transformAst(node, css, type = null) {
|
|
292
|
+
atrule.keyframes = transformAst(node.block, css, 'keyframe');
|
|
293
|
+
atrule.vendor = undefined;
|
|
294
|
+
}
|
|
295
|
+
+ else if (node.name === 'layer') {
|
|
296
|
+
+ atrule.layer = node.prelude ? node.prelude.value.trim() : '';
|
|
297
|
+
+ atrule.rules = transformAst(node.block, css);
|
|
298
|
+
+ }
|
|
299
|
+
else {
|
|
300
|
+
atrule.rules = transformAst(node.block, css);
|
|
301
|
+
}
|
|
302
|
+
diff --git a/css/reworkcss.d.ts b/css/reworkcss.d.ts
|
|
303
|
+
index 3ac4f61..1906ed1 100644
|
|
304
|
+
--- a/css/reworkcss.d.ts
|
|
305
|
+
+++ b/css/reworkcss.d.ts
|
|
306
|
+
@@ -10,7 +10,7 @@ export interface Position {
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
export interface Node {
|
|
310
|
+
- type: 'rule' | 'keyframes' | 'declaration' | 'import' | 'media';
|
|
311
|
+
+ type: 'rule' | 'keyframes' | 'declaration' | 'import' | 'media' | 'layer';
|
|
312
|
+
position: Position;
|
|
313
|
+
}
|
|
314
|
+
export interface Declaration extends Node {
|
|
315
|
+
@@ -21,7 +21,7 @@ export interface Rule extends Node {
|
|
316
|
+
selectors: string[];
|
|
317
|
+
declarations: Declaration[];
|
|
318
|
+
}
|
|
319
|
+
-export type AtRule = KeyFrames | Media;
|
|
320
|
+
+export type AtRule = KeyFrames | Media | Layer;
|
|
321
|
+
export interface Keyframes extends Rule {
|
|
322
|
+
name: string;
|
|
323
|
+
vendor?: string;
|
|
324
|
+
@@ -35,6 +35,11 @@ export interface Media extends Node {
|
|
325
|
+
media: string;
|
|
326
|
+
rules: Array<Rule | AtRule>;
|
|
327
|
+
}
|
|
328
|
+
+export interface Layer extends Node {
|
|
329
|
+
+ /** Layer-name prelude — '' for an anonymous block. Statement form (`@layer a, b;`) leaves rules undefined. */
|
|
330
|
+
+ layer: string;
|
|
331
|
+
+ rules?: Array<Rule | AtRule>;
|
|
332
|
+
+}
|
|
333
|
+
export interface StyleSheet {
|
|
334
|
+
rules: Rule[];
|
|
335
|
+
}
|
|
336
|
+
diff --git a/ui/styling/css-selector.d.ts b/ui/styling/css-selector.d.ts
|
|
337
|
+
index 734d886..d90f0e4 100644
|
|
338
|
+
--- a/ui/styling/css-selector.d.ts
|
|
339
|
+
+++ b/ui/styling/css-selector.d.ts
|
|
340
|
+
@@ -222,6 +222,8 @@ export declare class RuleSet {
|
|
341
|
+
mediaQueryString: string | string[];
|
|
342
|
+
tag?: string | number;
|
|
343
|
+
scopedTag?: string;
|
|
344
|
+
+ /** Cascade-layer rank per nesting depth; undefined/shortest path sorts after every layer (unlayered styles win). */
|
|
345
|
+
+ layerPath?: number[];
|
|
346
|
+
constructor(selectors: SelectorCore[], declarations: Declaration[]);
|
|
347
|
+
toString(): string;
|
|
348
|
+
lookupSort(sorter: LookupSorter): void;
|
|
349
|
+
diff --git a/ui/styling/css-selector.js b/ui/styling/css-selector.js
|
|
350
|
+
index 702ca50..b6fa55a 100644
|
|
351
|
+
--- a/ui/styling/css-selector.js
|
|
352
|
+
+++ b/ui/styling/css-selector.js
|
|
353
|
+
@@ -1081,9 +1081,27 @@ export class StyleSheetSelectorScope extends SelectorScope {
|
|
354
|
+
return matchSelectorCandidates(node, this.collectCandidates(node));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
-/** Cascade order: specificity, then source order - (tier, position) across stylesheets. */
|
|
358
|
+
+/**
|
|
359
|
+
+ * Cascade layers order before specificity: rules in an earlier-declared layer lose
|
|
360
|
+
+ * to rules in a later-declared layer regardless of specificity, and unlayered
|
|
361
|
+
+ * rules (absent rank at a depth) beat every layered rule.
|
|
362
|
+
+ */
|
|
363
|
+
+function compareLayerPaths(a, b) {
|
|
364
|
+
+ const aLength = a?.length ?? 0;
|
|
365
|
+
+ const bLength = b?.length ?? 0;
|
|
366
|
+
+ const length = Math.max(aLength, bLength);
|
|
367
|
+
+ for (let i = 0; i < length; i++) {
|
|
368
|
+
+ const rankA = i < aLength ? a[i] : Number.POSITIVE_INFINITY;
|
|
369
|
+
+ const rankB = i < bLength ? b[i] : Number.POSITIVE_INFINITY;
|
|
370
|
+
+ if (rankA !== rankB) {
|
|
371
|
+
+ return rankA - rankB;
|
|
372
|
+
+ }
|
|
373
|
+
+ }
|
|
374
|
+
+ return 0;
|
|
375
|
+
+}
|
|
376
|
+
+/** Cascade order: cascade layer, then specificity, then source order - (tier, position) across stylesheets. */
|
|
377
|
+
function compareSelectors(a, b) {
|
|
378
|
+
- return a.specificity - b.specificity || a.tier - b.tier || a.pos - b.pos;
|
|
379
|
+
+ return compareLayerPaths(a.ruleset?.layerPath, b.ruleset?.layerPath) || a.specificity - b.specificity || a.tier - b.tier || a.pos - b.pos;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Resolve collected candidates against a node, compacting the array in place.
|
|
383
|
+
diff --git a/ui/styling/style-scope.d.ts b/ui/styling/style-scope.d.ts
|
|
384
|
+
index d6f0266..321217d 100644
|
|
385
|
+
--- a/ui/styling/style-scope.d.ts
|
|
386
|
+
+++ b/ui/styling/style-scope.d.ts
|
|
387
|
+
@@ -8,7 +8,7 @@ import { Keyframes, KeyframeAnimationInfo, KeyframeAnimation } from '../animatio
|
|
388
|
+
*/
|
|
389
|
+
export declare function mergeCssSelectors(): void;
|
|
390
|
+
export declare function mergeCssKeyframes(): void;
|
|
391
|
+
-export declare function _populateRules(nodes: ReworkCSS.Node[], rulesets: RuleSet[], keyframes: Keyframes[], mediaQueryString?: string | string[]): void;
|
|
392
|
+
+export declare function _populateRules(nodes: ReworkCSS.Node[], rulesets: RuleSet[], keyframes: Keyframes[], mediaQueryString?: string | string[], layerPath?: number[], layerPrefix?: string): void;
|
|
393
|
+
export declare function removeTaggedAdditionalCSS(tag: string | number): boolean;
|
|
394
|
+
export declare function addTaggedAdditionalCSS(cssText: string, tag?: string | number): boolean;
|
|
395
|
+
export declare const loadAppCSS: any;
|
|
396
|
+
diff --git a/ui/styling/style-scope.js b/ui/styling/style-scope.js
|
|
397
|
+
index e75c7c8..7cf9244 100644
|
|
398
|
+
--- a/ui/styling/style-scope.js
|
|
399
|
+
+++ b/ui/styling/style-scope.js
|
|
400
|
+
@@ -344,6 +344,33 @@ __decorate([
|
|
401
|
+
__metadata("design:paramtypes", [String, String]),
|
|
402
|
+
__metadata("design:returntype", String)
|
|
403
|
+
], CSSSource, "resolveCSSPathFromURL", null);
|
|
404
|
+
+// Cascade-layer registry: qualified name -> first-seen ordinal, shared across
|
|
405
|
+
+// every stylesheet feeding the cascade (layer order is document-wide per spec).
|
|
406
|
+
+// '~' synthesizes names for anonymous layers, which authors cannot reference —
|
|
407
|
+
+// the char is illegal in a CSS ident, so they never collide with named layers.
|
|
408
|
+
+const cascadeLayerOrdinals = new Map();
|
|
409
|
+
+let cascadeLayerCount = 0;
|
|
410
|
+
+let anonymousLayerCount = 0;
|
|
411
|
+
+function layerOrdinal(name) {
|
|
412
|
+
+ let ordinal = cascadeLayerOrdinals.get(name);
|
|
413
|
+
+ if (ordinal === undefined) {
|
|
414
|
+
+ ordinal = cascadeLayerCount++;
|
|
415
|
+
+ cascadeLayerOrdinals.set(name, ordinal);
|
|
416
|
+
+ }
|
|
417
|
+
+ return ordinal;
|
|
418
|
+
+}
|
|
419
|
+
+// Rank path for `name` nested under the layer at prefixPath/prefixName. Dotted
|
|
420
|
+
+// names register each ancestor on the way down, so a top-level `@layer a.b`
|
|
421
|
+
+// refers to the same layer as `@layer a { @layer b }`.
|
|
422
|
+
+function layerPathFor(name, prefixPath, prefixName) {
|
|
423
|
+
+ const path = [...prefixPath];
|
|
424
|
+
+ let qualified = prefixName;
|
|
425
|
+
+ for (const segment of name.split('.')) {
|
|
426
|
+
+ qualified = qualified ? `${qualified}.${segment}` : segment;
|
|
427
|
+
+ path.push(layerOrdinal(qualified));
|
|
428
|
+
+ }
|
|
429
|
+
+ return path;
|
|
430
|
+
+}
|
|
431
|
+
function populateRulesFromImports(nodes, rulesets, keyframes) {
|
|
432
|
+
const imports = nodes.filter((r) => r.type === 'import');
|
|
433
|
+
if (!imports.length) {
|
|
434
|
+
@@ -354,6 +381,11 @@ function populateRulesFromImports(nodes, rulesets, keyframes) {
|
|
435
|
+
const urlMatch = importItem && importItem.match(pattern);
|
|
436
|
+
return urlMatch && urlMatch[2];
|
|
437
|
+
};
|
|
438
|
+
+ const layerFromImportObject = (importObject) => {
|
|
439
|
+
+ const importItem = importObject['import'];
|
|
440
|
+
+ const layerMatch = importItem && importItem.match(/\blayer\b(?:\(\s*([^)]*?)\s*\))?/);
|
|
441
|
+
+ return layerMatch ? layerMatch[1] ?? '' : null;
|
|
442
|
+
+ };
|
|
443
|
+
const sourceFromImportObject = (importObject) => importObject['position'] && importObject['position']['source'];
|
|
444
|
+
const toUrlSourcePair = (importObject) => ({
|
|
445
|
+
url: urlFromImportObject(importObject),
|
|
446
|
+
@@ -361,17 +393,25 @@ function populateRulesFromImports(nodes, rulesets, keyframes) {
|
|
447
|
+
});
|
|
448
|
+
const getCssFile = ({ url, source }) => (source ? CSSSource.fromFileImport(url, source) : CSSSource.fromURI(url));
|
|
449
|
+
const cssFiles = imports
|
|
450
|
+
- .map(toUrlSourcePair)
|
|
451
|
+
+ .map((importObject) => ({ ...toUrlSourcePair(importObject), layer: layerFromImportObject(importObject) }))
|
|
452
|
+
.filter(({ url }) => !!url)
|
|
453
|
+
- .map(getCssFile);
|
|
454
|
+
- for (const cssFile of cssFiles) {
|
|
455
|
+
+ .map(({ url, source, layer }) => ({ cssFile: getCssFile({ url, source }), layer }));
|
|
456
|
+
+ for (const { cssFile, layer } of cssFiles) {
|
|
457
|
+
if (cssFile) {
|
|
458
|
+
+ if (layer !== null) {
|
|
459
|
+
+ // @import "x.css" layer(name) — the imported sheet joins the named
|
|
460
|
+
+ // layer; its own layers become nested sublayers of it.
|
|
461
|
+
+ const importPath = [layerOrdinal(layer.trim() || `~import-${anonymousLayerCount++}`)];
|
|
462
|
+
+ for (const ruleset of cssFile.selectors) {
|
|
463
|
+
+ ruleset.layerPath = [...importPath, ...(ruleset.layerPath ?? [])];
|
|
464
|
+
+ }
|
|
465
|
+
+ }
|
|
466
|
+
rulesets.push(...cssFile.selectors);
|
|
467
|
+
keyframes.push(...cssFile.keyframes);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
-export function _populateRules(nodes, rulesets, keyframes, mediaQueryString) {
|
|
472
|
+
+export function _populateRules(nodes, rulesets, keyframes, mediaQueryString, layerPath, layerPrefix) {
|
|
473
|
+
for (const node of nodes) {
|
|
474
|
+
if (isKeyframe(node)) {
|
|
475
|
+
const keyframeRule = {
|
|
476
|
+
@@ -396,11 +436,32 @@ export function _populateRules(nodes, rulesets, keyframes, mediaQueryString) {
|
|
477
|
+
else {
|
|
478
|
+
compositeMediaQuery = node.media;
|
|
479
|
+
}
|
|
480
|
+
- _populateRules(node.rules, rulesets, keyframes, compositeMediaQuery);
|
|
481
|
+
+ _populateRules(node.rules, rulesets, keyframes, compositeMediaQuery, layerPath, layerPrefix);
|
|
482
|
+
+ }
|
|
483
|
+
+ else if (isLayer(node)) {
|
|
484
|
+
+ const name = (node.layer ?? '').trim();
|
|
485
|
+
+ if (node.rules) {
|
|
486
|
+
+ // Block at-rule: a single name, or anonymous when the prelude is empty.
|
|
487
|
+
+ const qualified = name ? (layerPrefix ? `${layerPrefix}.${name}` : name) : `${layerPrefix ?? ''}~${anonymousLayerCount++}`;
|
|
488
|
+
+ const childPath = name ? layerPathFor(name, layerPath ?? [], layerPrefix ?? '') : [...(layerPath ?? []), layerOrdinal(qualified)];
|
|
489
|
+
+ _populateRules(node.rules, rulesets, keyframes, mediaQueryString, childPath, qualified);
|
|
490
|
+
+ }
|
|
491
|
+
+ else {
|
|
492
|
+
+ // Statement at-rule: `@layer a, b;` pre-declares order only.
|
|
493
|
+
+ for (const declared of name.split(',')) {
|
|
494
|
+
+ const declaredName = declared.trim();
|
|
495
|
+
+ if (declaredName) {
|
|
496
|
+
+ layerPathFor(declaredName, layerPath ?? [], layerPrefix ?? '');
|
|
497
|
+
+ }
|
|
498
|
+
+ }
|
|
499
|
+
+ }
|
|
500
|
+
}
|
|
501
|
+
else if (isRule(node)) {
|
|
502
|
+
const ruleset = fromAstNode(node);
|
|
503
|
+
ruleset.mediaQueryString = mediaQueryString;
|
|
504
|
+
+ if (layerPath?.length) {
|
|
505
|
+
+ ruleset.layerPath = layerPath;
|
|
506
|
+
+ }
|
|
507
|
+
rulesets.push(ruleset);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
@@ -1263,6 +1324,9 @@ function isMedia(node) {
|
|
511
|
+
function isKeyframe(node) {
|
|
512
|
+
return node.type === 'keyframes';
|
|
513
|
+
}
|
|
514
|
+
+function isLayer(node) {
|
|
515
|
+
+ return node.type === 'layer';
|
|
516
|
+
+}
|
|
517
|
+
function isRule(node) {
|
|
518
|
+
return node.type === 'rule';
|
|
519
|
+
}
|
|
520
|
+
diff --git a/ui/core/view-base/index.d.ts b/ui/core/view-base/index.d.ts
|
|
521
|
+
index e60308a..1aee75a 100644
|
|
522
|
+
--- a/ui/core/view-base/index.d.ts
|
|
523
|
+
+++ b/ui/core/view-base/index.d.ts
|
|
524
|
+
@@ -497,6 +497,12 @@ export declare abstract class ViewBase extends Observable {
|
|
525
|
+
* @param view to unload.
|
|
526
|
+
*/
|
|
527
|
+
unloadView(view: ViewBase): void;
|
|
528
|
+
+ /**
|
|
529
|
+
+ * Whether a child is the one currently shown. Containers that keep several children
|
|
530
|
+
+ * alive but show one at a time (a TabView's items) override it for the hidden ones.
|
|
531
|
+
+ * @param child A direct child of this view.
|
|
532
|
+
+ */
|
|
533
|
+
+ _isChildPresented(child: ViewBase): boolean;
|
|
534
|
+
/**
|
|
535
|
+
* Core logic for removing a child view from this instance. Used by the framework to handle lifecycle events more centralized. Do not use outside the UI Stack implementation.
|
|
536
|
+
*/
|
|
537
|
+
diff --git a/ui/core/view-base/index.js b/ui/core/view-base/index.js
|
|
538
|
+
index 0c25671..524dfd7 100644
|
|
539
|
+
--- a/ui/core/view-base/index.js
|
|
540
|
+
+++ b/ui/core/view-base/index.js
|
|
541
|
+
@@ -633,6 +633,14 @@ export class ViewBase extends Observable {
|
|
542
|
+
view.callUnloaded();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
+ /**
|
|
546
|
+
+ * Whether a child is the one currently shown. Containers that keep several children
|
|
547
|
+
+ * alive but show one at a time (a TabView's items) override it for the hidden ones.
|
|
548
|
+
+ * @param child A direct child of this view.
|
|
549
|
+
+ */
|
|
550
|
+
+ _isChildPresented(child) {
|
|
551
|
+
+ return true;
|
|
552
|
+
+ }
|
|
553
|
+
/**
|
|
554
|
+
* Core logic for removing a child view from this instance. Used by the framework to handle lifecycle events more centralized. Do not use outside the UI Stack implementation.
|
|
555
|
+
*/
|
|
556
|
+
diff --git a/ui/frame/frame-common.js b/ui/frame/frame-common.js
|
|
557
|
+
index f678538..28b261e 100644
|
|
558
|
+
--- a/ui/frame/frame-common.js
|
|
559
|
+
+++ b/ui/frame/frame-common.js
|
|
560
|
+
@@ -3,7 +3,7 @@ import { NavigationType } from './frame-interfaces';
|
|
561
|
+
import { CustomLayoutView, CSSType } from '../core/view';
|
|
562
|
+
import { Property } from '../core/properties';
|
|
563
|
+
import { Trace } from '../../trace';
|
|
564
|
+
-import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack, _isFrameStackEmpty } from './frame-stack';
|
|
565
|
+
+import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _insertAtFrameStackBottom, _popFromFrameStack, _removeFromFrameStack, _isFrameStackEmpty } from './frame-stack';
|
|
566
|
+
import { viewMatchesModuleContext } from '../core/view/view-common';
|
|
567
|
+
import { getAncestor } from '../core/view-base';
|
|
568
|
+
import { Builder } from '../builder';
|
|
569
|
+
@@ -12,6 +12,19 @@ import { profile } from '../../profiling';
|
|
570
|
+
import { FRAME_SYMBOL } from './frame-helpers';
|
|
571
|
+
import { SharedTransition } from '../transition/shared-transition';
|
|
572
|
+
export { NavigationType } from './frame-interfaces';
|
|
573
|
+
+/**
|
|
574
|
+
+ * A view is presented while every ancestor shows the child leading to it; a frame in an
|
|
575
|
+
+ * unselected tab is loaded on Android but must never outrank the tab that is shown.
|
|
576
|
+
+ */
|
|
577
|
+
+function isPresented(view) {
|
|
578
|
+
+ while (view.parent) {
|
|
579
|
+
+ if (!view.parent._isChildPresented(view)) {
|
|
580
|
+
+ return false;
|
|
581
|
+
+ }
|
|
582
|
+
+ view = view.parent;
|
|
583
|
+
+ }
|
|
584
|
+
+ return true;
|
|
585
|
+
+}
|
|
586
|
+
function buildEntryFromArgs(arg) {
|
|
587
|
+
let entry;
|
|
588
|
+
if (typeof arg === 'string') {
|
|
589
|
+
@@ -466,7 +479,12 @@ let FrameBase = FrameBase_1 = class FrameBase extends CustomLayoutView {
|
|
590
|
+
return _isFrameStackEmpty();
|
|
591
|
+
}
|
|
592
|
+
_pushInFrameStack() {
|
|
593
|
+
- _pushInFrameStack(this);
|
|
594
|
+
+ if (isPresented(this)) {
|
|
595
|
+
+ _pushInFrameStack(this);
|
|
596
|
+
+ }
|
|
597
|
+
+ else {
|
|
598
|
+
+ _insertAtFrameStackBottom(this);
|
|
599
|
+
+ }
|
|
600
|
+
}
|
|
601
|
+
_popFromFrameStack() {
|
|
602
|
+
_popFromFrameStack(this);
|
|
603
|
+
diff --git a/ui/frame/frame-stack.d.ts b/ui/frame/frame-stack.d.ts
|
|
604
|
+
index 8387e62..0c87416 100644
|
|
605
|
+
--- a/ui/frame/frame-stack.d.ts
|
|
606
|
+
+++ b/ui/frame/frame-stack.d.ts
|
|
607
|
+
@@ -19,5 +19,6 @@ export declare function topmostForWindow(window: WindowBase): FrameBase | undefi
|
|
608
|
+
export declare function topmost(window?: WindowBase): FrameBase;
|
|
609
|
+
export declare function _isFrameStackEmpty(): boolean;
|
|
610
|
+
export declare function _pushInFrameStack(frame: FrameBase): void;
|
|
611
|
+
+export declare function _insertAtFrameStackBottom(frame: FrameBase): void;
|
|
612
|
+
export declare function _popFromFrameStack(frame: FrameBase): void;
|
|
613
|
+
export declare function _removeFromFrameStack(frame: FrameBase): void;
|
|
614
|
+
diff --git a/ui/frame/frame-stack.js b/ui/frame/frame-stack.js
|
|
615
|
+
index 9ded225..9a9f5d5 100644
|
|
616
|
+
--- a/ui/frame/frame-stack.js
|
|
617
|
+
+++ b/ui/frame/frame-stack.js
|
|
618
|
+
@@ -46,6 +46,17 @@ export function _pushInFrameStack(frame) {
|
|
619
|
+
frameStack.push(frame);
|
|
620
|
+
frame._isInFrameStack = true;
|
|
621
|
+
}
|
|
622
|
+
+/**
|
|
623
|
+
+ * Keeps a frame in the stack without making it topmost: the frame stays findable by id,
|
|
624
|
+
+ * while every frame that is actually shown outranks it.
|
|
625
|
+
+ */
|
|
626
|
+
+export function _insertAtFrameStackBottom(frame) {
|
|
627
|
+
+ if (frame._isInFrameStack) {
|
|
628
|
+
+ return;
|
|
629
|
+
+ }
|
|
630
|
+
+ frameStack.unshift(frame);
|
|
631
|
+
+ frame._isInFrameStack = true;
|
|
632
|
+
+}
|
|
633
|
+
export function _popFromFrameStack(frame) {
|
|
634
|
+
if (!frame._isInFrameStack) {
|
|
635
|
+
return;
|
|
636
|
+
diff --git a/ui/tab-view/index.android.js b/ui/tab-view/index.android.js
|
|
637
|
+
index e195b75..7df6fcb 100644
|
|
638
|
+
--- a/ui/tab-view/index.android.js
|
|
639
|
+
+++ b/ui/tab-view/index.android.js
|
|
640
|
+
@@ -368,7 +368,10 @@ export class TabView extends TabViewBase {
|
|
641
|
+
onItemsChanged(oldItems, newItems) {
|
|
642
|
+
super.onItemsChanged(oldItems, newItems);
|
|
643
|
+
if (oldItems) {
|
|
644
|
+
- oldItems.forEach((item, i, arr) => {
|
|
645
|
+
+ oldItems.forEach((item) => {
|
|
646
|
+
+ if (newItems && newItems.indexOf(item) >= 0) {
|
|
647
|
+
+ return;
|
|
648
|
+
+ }
|
|
649
|
+
item.index = 0;
|
|
650
|
+
item.tabItemSpec = null;
|
|
651
|
+
item.setNativeView(null);
|
|
652
|
+
diff --git a/ui/tab-view/index.ios.js b/ui/tab-view/index.ios.js
|
|
653
|
+
index c25ff0c..e162b5d 100644
|
|
654
|
+
--- a/ui/tab-view/index.ios.js
|
|
655
|
+
+++ b/ui/tab-view/index.ios.js
|
|
656
|
+
@@ -622,6 +622,11 @@ export class TabView extends TabViewBase {
|
|
657
|
+
[itemsProperty.setNative](value) {
|
|
658
|
+
this.setViewControllers(value);
|
|
659
|
+
selectedIndexProperty.coerce(this);
|
|
660
|
+
+ // Items added to a loaded TabView were loaded before their controllers existed.
|
|
661
|
+
+ const selectedItem = value === null || value === void 0 ? void 0 : value[this.selectedIndex];
|
|
662
|
+
+ if (selectedItem) {
|
|
663
|
+
+ selectedItem.loadView(selectedItem.view);
|
|
664
|
+
+ }
|
|
665
|
+
}
|
|
666
|
+
[tabTextFontSizeProperty.getDefault]() {
|
|
667
|
+
return null;
|
|
668
|
+
diff --git a/ui/tab-view/tab-view-common.d.ts b/ui/tab-view/tab-view-common.d.ts
|
|
669
|
+
index 35d1ba1..e96b308 100644
|
|
670
|
+
--- a/ui/tab-view/tab-view-common.d.ts
|
|
671
|
+
+++ b/ui/tab-view/tab-view-common.d.ts
|
|
672
|
+
@@ -66,6 +66,7 @@ export declare class TabViewBase extends View implements TabViewDefinition, AddC
|
|
673
|
+
eachChild(callback: (child: ViewBase) => boolean): void;
|
|
674
|
+
eachChildView(callback: (child: View) => boolean): void;
|
|
675
|
+
onItemsChanged(oldItems: TabViewItemDefinition[], newItems: TabViewItemDefinition[]): void;
|
|
676
|
+
+ _isChildPresented(child: ViewBase): boolean;
|
|
677
|
+
onSelectedIndexChanged(oldIndex: number, newIndex: number): void;
|
|
678
|
+
}
|
|
679
|
+
export interface TabViewBase {
|
|
680
|
+
diff --git a/ui/tab-view/tab-view-common.js b/ui/tab-view/tab-view-common.js
|
|
681
|
+
index 66672f1..1eb3676 100644
|
|
682
|
+
--- a/ui/tab-view/tab-view-common.js
|
|
683
|
+
+++ b/ui/tab-view/tab-view-common.js
|
|
684
|
+
@@ -144,21 +144,33 @@ let TabViewBase = TabViewBase_1 = class TabViewBase extends View {
|
|
685
|
+
}
|
|
686
|
+
onItemsChanged(oldItems, newItems) {
|
|
687
|
+
if (oldItems) {
|
|
688
|
+
- oldItems.forEach((item) => this._removeView(item));
|
|
689
|
+
+ oldItems.forEach((item) => {
|
|
690
|
+
+ if (!newItems || newItems.indexOf(item) < 0) {
|
|
691
|
+
+ this._removeView(item);
|
|
692
|
+
+ }
|
|
693
|
+
+ });
|
|
694
|
+
}
|
|
695
|
+
if (newItems) {
|
|
696
|
+
newItems.forEach((item) => {
|
|
697
|
+
if (!item.view) {
|
|
698
|
+
throw new Error(`TabViewItem must have a view.`);
|
|
699
|
+
}
|
|
700
|
+
+ // An item kept across the change stays loaded; tearing it down would unload its view.
|
|
701
|
+
+ if (item.parent === this) {
|
|
702
|
+
+ return;
|
|
703
|
+
+ }
|
|
704
|
+
// A view can only have one parent, and _addView throws if it already has one.
|
|
705
|
+
- if (item.parent && item.parent !== this) {
|
|
706
|
+
+ if (item.parent) {
|
|
707
|
+
item.parent._removeView(item);
|
|
708
|
+
}
|
|
709
|
+
this._addView(item);
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
+ _isChildPresented(child) {
|
|
714
|
+
+ const items = this.items;
|
|
715
|
+
+ return !items || items[this.selectedIndex] === child;
|
|
716
|
+
+ }
|
|
717
|
+
onSelectedIndexChanged(oldIndex, newIndex) {
|
|
718
|
+
// to be overridden in platform specific files
|
|
719
|
+
this.notify({
|
|
720
|
+
diff --git a/ui/layouts/flexbox-layout/index.ios.js b/ui/layouts/flexbox-layout/index.ios.js
|
|
721
|
+
index d391d4213240cab9a5088f9d7937fab17e7c7e54..112a3dc9ca31471c7cbceb94f6ee22bc86181955 100644
|
|
722
|
+
--- a/ui/layouts/flexbox-layout/index.ios.js
|
|
723
|
+
+++ b/ui/layouts/flexbox-layout/index.ios.js
|
|
724
|
+
@@ -222,8 +222,8 @@ export class FlexboxLayout extends FlexboxLayoutBase {
|
|
725
|
+
if (FlexBasisPercent.DEFAULT /*lp.flexBasisPercent*/ !== FlexBasisPercent.DEFAULT && widthMode === EXACTLY) {
|
|
726
|
+
childWidth = Math.round(widthSize * FlexBasisPercent.DEFAULT /*lp.flexBasisPercent*/);
|
|
727
|
+
}
|
|
728
|
+
- const childWidthMeasureSpec = FlexboxLayout.getChildMeasureSpec(widthMeasureSpec, lp.effectivePaddingLeft + lp.effectivePaddingRight + lp.effectiveMarginLeft + lp.effectiveMarginRight, childWidth < 0 ? WRAP_CONTENT : childWidth);
|
|
729
|
+
- const childHeightMeasureSpec = FlexboxLayout.getChildMeasureSpec(heightMeasureSpec, lp.effectivePaddingTop + lp.effectivePaddingBottom + lp.effectiveMarginTop + lp.effectiveMarginBottom, lp.effectiveHeight < 0 ? WRAP_CONTENT : lp.effectiveHeight);
|
|
730
|
+
+ const childWidthMeasureSpec = FlexboxLayout.getChildMeasureSpec(widthMeasureSpec, this.effectivePaddingLeft + this.effectivePaddingRight + lp.effectiveMarginLeft + lp.effectiveMarginRight, childWidth < 0 ? WRAP_CONTENT : childWidth);
|
|
731
|
+
+ const childHeightMeasureSpec = FlexboxLayout.getChildMeasureSpec(heightMeasureSpec, this.effectivePaddingTop + this.effectivePaddingBottom + lp.effectiveMarginTop + lp.effectiveMarginBottom, lp.effectiveHeight < 0 ? WRAP_CONTENT : lp.effectiveHeight);
|
|
732
|
+
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
|
|
733
|
+
this._checkSizeConstraints(child);
|
|
734
|
+
childState = View.combineMeasuredStates(childState, child.getMeasuredState());
|
|
735
|
+
@@ -547,7 +547,7 @@ export class FlexboxLayout extends FlexboxLayoutBase {
|
|
736
|
+
// might result in increased height e.g. text that could be shown on one line for larger
|
|
737
|
+
// width needs to be wrapped in two when width is reduced.
|
|
738
|
+
// As a result we cannot unconditionally measure with EXACTLY the current measured height
|
|
739
|
+
- const childHeightMeasureSpec = FlexboxLayout.getChildMeasureSpec(this._currentHeightMeasureSpec, lp.effectivePaddingTop + lp.effectivePaddingBottom + lp.effectiveMarginTop + lp.effectiveMarginBottom, lp.effectiveHeight < 0 ? WRAP_CONTENT : lp.effectiveHeight);
|
|
740
|
+
+ const childHeightMeasureSpec = FlexboxLayout.getChildMeasureSpec(this._currentHeightMeasureSpec, this.effectivePaddingTop + this.effectivePaddingBottom + lp.effectiveMarginTop + lp.effectiveMarginBottom, lp.effectiveHeight < 0 ? WRAP_CONTENT : lp.effectiveHeight);
|
|
741
|
+
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
|
|
742
|
+
child.effectiveMinWidth = minWidth;
|
|
743
|
+
// make sure crossSize is up-to-date as child calculated height might have increased
|
|
744
|
+
@@ -758,7 +758,7 @@ export class FlexboxLayout extends FlexboxLayoutBase {
|
|
745
|
+
let newHeight = crossSize - view.effectiveMarginTop - view.effectiveMarginBottom;
|
|
746
|
+
newHeight = Math.max(newHeight, 0);
|
|
747
|
+
const originalMeasuredWidth = view.getMeasuredWidth();
|
|
748
|
+
- let childWidthMeasureSpec = FlexboxLayout.getChildMeasureSpec(this._currentWidthMeasureSpec, view.effectivePaddingLeft + view.effectivePaddingRight + view.effectiveMarginLeft + view.effectiveMarginRight, view.effectiveWidth < 0 ? WRAP_CONTENT : Math.min(view.effectiveWidth, originalMeasuredWidth));
|
|
749
|
+
+ let childWidthMeasureSpec = FlexboxLayout.getChildMeasureSpec(this._currentWidthMeasureSpec, this.effectivePaddingLeft + this.effectivePaddingRight + view.effectiveMarginLeft + view.effectiveMarginRight, view.effectiveWidth < 0 ? WRAP_CONTENT : Math.min(view.effectiveWidth, originalMeasuredWidth));
|
|
750
|
+
view.measure(childWidthMeasureSpec, makeMeasureSpec(newHeight, EXACTLY));
|
|
751
|
+
if (originalMeasuredWidth > view.getMeasuredWidth()) {
|
|
752
|
+
childWidthMeasureSpec = makeMeasureSpec(originalMeasuredWidth, EXACTLY);
|