@getuserfeedback/protocol 3.0.12 → 3.0.13
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/host/widget-layout-transition-plan.d.ts +34 -1
- package/dist/host/widget-layout-transition-plan.d.ts.map +1 -1
- package/dist/host/widget-layout-transition-plan.js +37 -9
- package/package.json +2 -2
- package/src/host/widget-layout-transition-plan.test.ts +88 -0
- package/src/host/widget-layout-transition-plan.ts +115 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WidgetLayoutSize, WidgetLayoutSizeUpdate, WidgetLayoutTimedSizeUpdate } from "./widget-layout-size-update.js";
|
|
1
|
+
import type { WidgetLayoutCubicBezier, WidgetLayoutSize, WidgetLayoutSizeUpdate, WidgetLayoutTimedSizeUpdate } from "./widget-layout-size-update.js";
|
|
2
2
|
export type WidgetLayoutComparableSizeUpdate = WidgetLayoutSizeUpdate | WidgetLayoutTimedSizeUpdate;
|
|
3
3
|
export type WidgetLayoutUnchangedTransitionPlan<Update extends WidgetLayoutComparableSizeUpdate = WidgetLayoutComparableSizeUpdate> = {
|
|
4
4
|
kind: "unchanged";
|
|
@@ -23,8 +23,37 @@ export type WidgetLayoutTimedTransitionPlan = {
|
|
|
23
23
|
export type WidgetLayoutInstantOnlyTransitionPlan = WidgetLayoutUnchangedTransitionPlan<WidgetLayoutSizeUpdate> | WidgetLayoutInstantTransitionPlan;
|
|
24
24
|
export type WidgetLayoutTransitionPlan = WidgetLayoutInstantOnlyTransitionPlan;
|
|
25
25
|
export type WidgetLayoutTimedCapableTransitionPlan = WidgetLayoutUnchangedTransitionPlan | WidgetLayoutInstantTransitionPlan | WidgetLayoutTimedTransitionPlan;
|
|
26
|
+
export type WidgetLayoutInstantTransitionPolicy = {
|
|
27
|
+
kind: "instant";
|
|
28
|
+
durationMs: 0;
|
|
29
|
+
};
|
|
30
|
+
export type WidgetLayoutTimedTransitionPolicy = {
|
|
31
|
+
kind: "timed";
|
|
32
|
+
durationMs: number;
|
|
33
|
+
easing: WidgetLayoutCubicBezier;
|
|
34
|
+
};
|
|
35
|
+
export type WidgetLayoutTransitionPolicy = WidgetLayoutInstantTransitionPolicy | WidgetLayoutTimedTransitionPolicy;
|
|
36
|
+
export declare const INSTANT_WIDGET_LAYOUT_TRANSITION_POLICY: WidgetLayoutInstantTransitionPolicy;
|
|
26
37
|
export declare function getWidgetLayoutPlanNowMs(): number;
|
|
27
38
|
export declare function areWidgetLayoutSizeUpdatesEqual(left: WidgetLayoutComparableSizeUpdate | undefined, right: WidgetLayoutComparableSizeUpdate): boolean;
|
|
39
|
+
type WidgetLayoutInstantPolicyPlanInput = {
|
|
40
|
+
previous?: WidgetLayoutComparableSizeUpdate;
|
|
41
|
+
size: WidgetLayoutSize;
|
|
42
|
+
nowMs: number;
|
|
43
|
+
transitionPolicy?: WidgetLayoutInstantTransitionPolicy;
|
|
44
|
+
};
|
|
45
|
+
type WidgetLayoutTimedCapablePolicyPlanInput = {
|
|
46
|
+
previous?: WidgetLayoutComparableSizeUpdate;
|
|
47
|
+
size: WidgetLayoutSize;
|
|
48
|
+
nowMs: number;
|
|
49
|
+
transitionPolicy: WidgetLayoutTimedTransitionPolicy;
|
|
50
|
+
};
|
|
51
|
+
type WidgetLayoutOptionalPolicyPlanInput = {
|
|
52
|
+
previous?: WidgetLayoutComparableSizeUpdate;
|
|
53
|
+
size: WidgetLayoutSize;
|
|
54
|
+
nowMs: number;
|
|
55
|
+
transitionPolicy?: WidgetLayoutTransitionPolicy;
|
|
56
|
+
};
|
|
28
57
|
export declare function planWidgetLayoutTransition(input: {
|
|
29
58
|
previous?: WidgetLayoutSizeUpdate;
|
|
30
59
|
next: WidgetLayoutSizeUpdate;
|
|
@@ -35,4 +64,8 @@ export declare function planWidgetLayoutTransition(input: {
|
|
|
35
64
|
next: WidgetLayoutComparableSizeUpdate;
|
|
36
65
|
nowMs: number;
|
|
37
66
|
}): WidgetLayoutTimedCapableTransitionPlan;
|
|
67
|
+
export declare function planWidgetLayoutTransition(input: WidgetLayoutInstantPolicyPlanInput): WidgetLayoutInstantOnlyTransitionPlan;
|
|
68
|
+
export declare function planWidgetLayoutTransition(input: WidgetLayoutTimedCapablePolicyPlanInput): WidgetLayoutTimedCapableTransitionPlan;
|
|
69
|
+
export declare function planWidgetLayoutTransition(input: WidgetLayoutOptionalPolicyPlanInput): WidgetLayoutTimedCapableTransitionPlan;
|
|
70
|
+
export {};
|
|
38
71
|
//# sourceMappingURL=widget-layout-transition-plan.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-layout-transition-plan.d.ts","sourceRoot":"","sources":["../../src/host/widget-layout-transition-plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,gBAAgB,EAChB,sBAAsB,EACtB,2BAA2B,EAC3B,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"widget-layout-transition-plan.d.ts","sourceRoot":"","sources":["../../src/host/widget-layout-transition-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { toInstantWidgetLayoutSizeUpdate, toTimedWidgetLayoutSizeUpdate, } from "./widget-layout-size-update.js";
|
|
2
|
+
export const INSTANT_WIDGET_LAYOUT_TRANSITION_POLICY = {
|
|
3
|
+
kind: "instant",
|
|
4
|
+
durationMs: 0,
|
|
5
|
+
};
|
|
1
6
|
export function getWidgetLayoutPlanNowMs() {
|
|
2
7
|
const performanceNow = typeof globalThis.performance?.now === "function"
|
|
3
8
|
? globalThis.performance.now()
|
|
@@ -53,31 +58,54 @@ function cloneWidgetLayoutSizeUpdate(update) {
|
|
|
53
58
|
function isWidgetLayoutTimedSizeUpdate(update) {
|
|
54
59
|
return update.transition.kind === "timed";
|
|
55
60
|
}
|
|
61
|
+
function toWidgetLayoutSizeUpdateForPolicy(input) {
|
|
62
|
+
const policy = input.policy ?? INSTANT_WIDGET_LAYOUT_TRANSITION_POLICY;
|
|
63
|
+
switch (policy.kind) {
|
|
64
|
+
case "instant":
|
|
65
|
+
return toInstantWidgetLayoutSizeUpdate(input.size);
|
|
66
|
+
case "timed":
|
|
67
|
+
return toTimedWidgetLayoutSizeUpdate({
|
|
68
|
+
size: input.size,
|
|
69
|
+
durationMs: policy.durationMs,
|
|
70
|
+
easing: policy.easing,
|
|
71
|
+
});
|
|
72
|
+
default: {
|
|
73
|
+
const unreachable = policy;
|
|
74
|
+
throw new Error(`Unsupported widget layout transition policy: ${JSON.stringify(unreachable)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
56
78
|
export function planWidgetLayoutTransition(input) {
|
|
57
79
|
if (!Number.isFinite(input.nowMs)) {
|
|
58
80
|
throw new RangeError("Widget layout transition plans require finite nowMs");
|
|
59
81
|
}
|
|
60
|
-
|
|
82
|
+
const next = "next" in input
|
|
83
|
+
? input.next
|
|
84
|
+
: toWidgetLayoutSizeUpdateForPolicy({
|
|
85
|
+
size: input.size,
|
|
86
|
+
policy: input.transitionPolicy,
|
|
87
|
+
});
|
|
88
|
+
if (areWidgetLayoutSizeUpdatesEqual(input.previous, next)) {
|
|
61
89
|
return {
|
|
62
90
|
kind: "unchanged",
|
|
63
|
-
size: { ...
|
|
64
|
-
update: cloneWidgetLayoutSizeUpdate(
|
|
91
|
+
size: { ...next.size },
|
|
92
|
+
update: cloneWidgetLayoutSizeUpdate(next),
|
|
65
93
|
observedAtMs: input.nowMs,
|
|
66
94
|
};
|
|
67
95
|
}
|
|
68
|
-
if (isWidgetLayoutTimedSizeUpdate(
|
|
96
|
+
if (isWidgetLayoutTimedSizeUpdate(next)) {
|
|
69
97
|
return {
|
|
70
98
|
kind: "timed",
|
|
71
|
-
size: { ...
|
|
72
|
-
update: cloneWidgetLayoutSizeUpdate(
|
|
99
|
+
size: { ...next.size },
|
|
100
|
+
update: cloneWidgetLayoutSizeUpdate(next),
|
|
73
101
|
startedAtMs: input.nowMs,
|
|
74
|
-
settledAtMs: input.nowMs +
|
|
102
|
+
settledAtMs: input.nowMs + next.transition.durationMs,
|
|
75
103
|
};
|
|
76
104
|
}
|
|
77
105
|
return {
|
|
78
106
|
kind: "instant",
|
|
79
|
-
size: { ...
|
|
80
|
-
update: cloneWidgetLayoutSizeUpdate(
|
|
107
|
+
size: { ...next.size },
|
|
108
|
+
update: cloneWidgetLayoutSizeUpdate(next),
|
|
81
109
|
startedAtMs: input.nowMs,
|
|
82
110
|
settledAtMs: input.nowMs,
|
|
83
111
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/protocol",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.13",
|
|
4
4
|
"description": "getuserfeedback widget protocol — host surface and (later) wire protocol",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"getuserfeedback",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
"scripts": {
|
|
135
|
-
"lint": "
|
|
135
|
+
"lint": "biome check .",
|
|
136
136
|
"build": "bun ../../scripts/clean-build-output.ts dist tsconfig.tsbuildinfo && tsc -b tsconfig.json && bun scripts/verify-dist-js.ts",
|
|
137
137
|
"types": "bun ../../scripts/clean-build-output.ts dist tsconfig.tsbuildinfo && tsc -b tsconfig.json --emitDeclarationOnly",
|
|
138
138
|
"typecheck": "tsc -b tsconfig.json",
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
areWidgetLayoutSizeUpdatesEqual,
|
|
8
8
|
getWidgetLayoutPlanNowMs,
|
|
9
|
+
INSTANT_WIDGET_LAYOUT_TRANSITION_POLICY,
|
|
9
10
|
planWidgetLayoutTransition,
|
|
10
11
|
type WidgetLayoutInstantOnlyTransitionPlan,
|
|
12
|
+
type WidgetLayoutTimedCapableTransitionPlan,
|
|
11
13
|
type WidgetLayoutTransitionPlan,
|
|
12
14
|
} from "./widget-layout-transition-plan.js";
|
|
13
15
|
|
|
@@ -18,6 +20,9 @@ describe("widget layout transition plan", () => {
|
|
|
18
20
|
function expectLegacyTransitionPlan(
|
|
19
21
|
_plan: WidgetLayoutTransitionPlan,
|
|
20
22
|
): void {}
|
|
23
|
+
function expectTimedCapablePlan(
|
|
24
|
+
_plan: WidgetLayoutTimedCapableTransitionPlan,
|
|
25
|
+
): void {}
|
|
21
26
|
|
|
22
27
|
test("uses the browser monotonic clock for layout plan timestamps", () => {
|
|
23
28
|
const originalPerformanceDescriptor = Object.getOwnPropertyDescriptor(
|
|
@@ -134,6 +139,44 @@ describe("widget layout transition plan", () => {
|
|
|
134
139
|
expect(areWidgetLayoutSizeUpdatesEqual(previous, next)).toBe(true);
|
|
135
140
|
});
|
|
136
141
|
|
|
142
|
+
test("plans size input as instant by default", () => {
|
|
143
|
+
const previous = toInstantWidgetLayoutSizeUpdate({
|
|
144
|
+
width: 320,
|
|
145
|
+
height: 240,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const plan = planWidgetLayoutTransition({
|
|
149
|
+
previous,
|
|
150
|
+
size: { width: 320, height: 360 },
|
|
151
|
+
nowMs: 1_500,
|
|
152
|
+
});
|
|
153
|
+
expectInstantOnlyPlan(plan);
|
|
154
|
+
expect(plan).toEqual({
|
|
155
|
+
kind: "instant",
|
|
156
|
+
size: { width: 320, height: 360 },
|
|
157
|
+
update: {
|
|
158
|
+
size: { width: 320, height: 360 },
|
|
159
|
+
transition: {
|
|
160
|
+
kind: "instant",
|
|
161
|
+
durationMs: 0,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
startedAtMs: 1_500,
|
|
165
|
+
settledAtMs: 1_500,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("keeps explicit instant transition policy instant-only", () => {
|
|
170
|
+
const plan = planWidgetLayoutTransition({
|
|
171
|
+
size: { width: 320, height: 360 },
|
|
172
|
+
nowMs: 1_500,
|
|
173
|
+
transitionPolicy: INSTANT_WIDGET_LAYOUT_TRANSITION_POLICY,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expectInstantOnlyPlan(plan);
|
|
177
|
+
expect(plan.kind).toBe("instant");
|
|
178
|
+
});
|
|
179
|
+
|
|
137
180
|
test("compares the full timed transition shape before it is live", () => {
|
|
138
181
|
const base = {
|
|
139
182
|
size: { width: 320, height: 240 },
|
|
@@ -206,6 +249,51 @@ describe("widget layout transition plan", () => {
|
|
|
206
249
|
});
|
|
207
250
|
});
|
|
208
251
|
|
|
252
|
+
test("plans timed transition policy from size input", () => {
|
|
253
|
+
const plan = planWidgetLayoutTransition({
|
|
254
|
+
size: { width: 320, height: 360 },
|
|
255
|
+
nowMs: 1_500,
|
|
256
|
+
transitionPolicy: {
|
|
257
|
+
kind: "timed",
|
|
258
|
+
durationMs: 180,
|
|
259
|
+
easing: [0.2, 0, 0, 1],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
expectTimedCapablePlan(plan);
|
|
263
|
+
|
|
264
|
+
expect(plan).toEqual({
|
|
265
|
+
kind: "timed",
|
|
266
|
+
size: { width: 320, height: 360 },
|
|
267
|
+
update: {
|
|
268
|
+
size: { width: 320, height: 360 },
|
|
269
|
+
transition: {
|
|
270
|
+
kind: "timed",
|
|
271
|
+
durationMs: 180,
|
|
272
|
+
easing: {
|
|
273
|
+
kind: "cubic-bezier",
|
|
274
|
+
points: [0.2, 0, 0, 1],
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
startedAtMs: 1_500,
|
|
279
|
+
settledAtMs: 1_680,
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("rejects invalid timed transition policy through protocol helpers", () => {
|
|
284
|
+
expect(() =>
|
|
285
|
+
planWidgetLayoutTransition({
|
|
286
|
+
size: { width: 320, height: 360 },
|
|
287
|
+
nowMs: 1_500,
|
|
288
|
+
transitionPolicy: {
|
|
289
|
+
kind: "timed",
|
|
290
|
+
durationMs: 0,
|
|
291
|
+
easing: [0.2, 0, 0, 1],
|
|
292
|
+
},
|
|
293
|
+
}),
|
|
294
|
+
).toThrow(RangeError);
|
|
295
|
+
});
|
|
296
|
+
|
|
209
297
|
test("does not leak mutable references from the next update", () => {
|
|
210
298
|
const next = {
|
|
211
299
|
size: { width: 320, height: 240 },
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
WidgetLayoutCubicBezier,
|
|
2
3
|
WidgetLayoutSize,
|
|
3
4
|
WidgetLayoutSizeUpdate,
|
|
4
5
|
WidgetLayoutTimedSizeUpdate,
|
|
5
6
|
} from "./widget-layout-size-update.js";
|
|
7
|
+
import {
|
|
8
|
+
toInstantWidgetLayoutSizeUpdate,
|
|
9
|
+
toTimedWidgetLayoutSizeUpdate,
|
|
10
|
+
} from "./widget-layout-size-update.js";
|
|
6
11
|
|
|
7
12
|
export type WidgetLayoutComparableSizeUpdate =
|
|
8
13
|
| WidgetLayoutSizeUpdate
|
|
@@ -45,6 +50,27 @@ export type WidgetLayoutTimedCapableTransitionPlan =
|
|
|
45
50
|
| WidgetLayoutInstantTransitionPlan
|
|
46
51
|
| WidgetLayoutTimedTransitionPlan;
|
|
47
52
|
|
|
53
|
+
export type WidgetLayoutInstantTransitionPolicy = {
|
|
54
|
+
kind: "instant";
|
|
55
|
+
durationMs: 0;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type WidgetLayoutTimedTransitionPolicy = {
|
|
59
|
+
kind: "timed";
|
|
60
|
+
durationMs: number;
|
|
61
|
+
easing: WidgetLayoutCubicBezier;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type WidgetLayoutTransitionPolicy =
|
|
65
|
+
| WidgetLayoutInstantTransitionPolicy
|
|
66
|
+
| WidgetLayoutTimedTransitionPolicy;
|
|
67
|
+
|
|
68
|
+
export const INSTANT_WIDGET_LAYOUT_TRANSITION_POLICY: WidgetLayoutInstantTransitionPolicy =
|
|
69
|
+
{
|
|
70
|
+
kind: "instant",
|
|
71
|
+
durationMs: 0,
|
|
72
|
+
};
|
|
73
|
+
|
|
48
74
|
export function getWidgetLayoutPlanNowMs(): number {
|
|
49
75
|
const performanceNow =
|
|
50
76
|
typeof globalThis.performance?.now === "function"
|
|
@@ -129,45 +155,120 @@ function isWidgetLayoutTimedSizeUpdate(
|
|
|
129
155
|
return update.transition.kind === "timed";
|
|
130
156
|
}
|
|
131
157
|
|
|
132
|
-
|
|
158
|
+
function toWidgetLayoutSizeUpdateForPolicy(input: {
|
|
159
|
+
policy: WidgetLayoutTransitionPolicy | undefined;
|
|
160
|
+
size: WidgetLayoutSize;
|
|
161
|
+
}): WidgetLayoutComparableSizeUpdate {
|
|
162
|
+
const policy = input.policy ?? INSTANT_WIDGET_LAYOUT_TRANSITION_POLICY;
|
|
163
|
+
switch (policy.kind) {
|
|
164
|
+
case "instant":
|
|
165
|
+
return toInstantWidgetLayoutSizeUpdate(input.size);
|
|
166
|
+
case "timed":
|
|
167
|
+
return toTimedWidgetLayoutSizeUpdate({
|
|
168
|
+
size: input.size,
|
|
169
|
+
durationMs: policy.durationMs,
|
|
170
|
+
easing: policy.easing,
|
|
171
|
+
});
|
|
172
|
+
default: {
|
|
173
|
+
const unreachable: never = policy;
|
|
174
|
+
throw new Error(
|
|
175
|
+
`Unsupported widget layout transition policy: ${JSON.stringify(unreachable)}`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
type WidgetLayoutInstantUpdatePlanInput = {
|
|
133
182
|
previous?: WidgetLayoutSizeUpdate;
|
|
134
183
|
next: WidgetLayoutSizeUpdate;
|
|
135
184
|
nowMs: number;
|
|
136
|
-
}
|
|
137
|
-
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
type WidgetLayoutTimedCapableUpdatePlanInput = {
|
|
138
188
|
previous?: WidgetLayoutComparableSizeUpdate;
|
|
139
189
|
next: WidgetLayoutComparableSizeUpdate;
|
|
140
190
|
nowMs: number;
|
|
141
|
-
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
type WidgetLayoutInstantPolicyPlanInput = {
|
|
194
|
+
previous?: WidgetLayoutComparableSizeUpdate;
|
|
195
|
+
size: WidgetLayoutSize;
|
|
196
|
+
nowMs: number;
|
|
197
|
+
transitionPolicy?: WidgetLayoutInstantTransitionPolicy;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
type WidgetLayoutTimedCapablePolicyPlanInput = {
|
|
201
|
+
previous?: WidgetLayoutComparableSizeUpdate;
|
|
202
|
+
size: WidgetLayoutSize;
|
|
203
|
+
nowMs: number;
|
|
204
|
+
transitionPolicy: WidgetLayoutTimedTransitionPolicy;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
type WidgetLayoutOptionalPolicyPlanInput = {
|
|
208
|
+
previous?: WidgetLayoutComparableSizeUpdate;
|
|
209
|
+
size: WidgetLayoutSize;
|
|
210
|
+
nowMs: number;
|
|
211
|
+
transitionPolicy?: WidgetLayoutTransitionPolicy;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export function planWidgetLayoutTransition(input: {
|
|
215
|
+
previous?: WidgetLayoutSizeUpdate;
|
|
216
|
+
next: WidgetLayoutSizeUpdate;
|
|
217
|
+
nowMs: number;
|
|
218
|
+
}): WidgetLayoutInstantOnlyTransitionPlan;
|
|
142
219
|
export function planWidgetLayoutTransition(input: {
|
|
143
220
|
previous?: WidgetLayoutComparableSizeUpdate;
|
|
144
221
|
next: WidgetLayoutComparableSizeUpdate;
|
|
145
222
|
nowMs: number;
|
|
146
|
-
}): WidgetLayoutTimedCapableTransitionPlan
|
|
223
|
+
}): WidgetLayoutTimedCapableTransitionPlan;
|
|
224
|
+
export function planWidgetLayoutTransition(
|
|
225
|
+
input: WidgetLayoutInstantPolicyPlanInput,
|
|
226
|
+
): WidgetLayoutInstantOnlyTransitionPlan;
|
|
227
|
+
export function planWidgetLayoutTransition(
|
|
228
|
+
input: WidgetLayoutTimedCapablePolicyPlanInput,
|
|
229
|
+
): WidgetLayoutTimedCapableTransitionPlan;
|
|
230
|
+
export function planWidgetLayoutTransition(
|
|
231
|
+
input: WidgetLayoutOptionalPolicyPlanInput,
|
|
232
|
+
): WidgetLayoutTimedCapableTransitionPlan;
|
|
233
|
+
export function planWidgetLayoutTransition(
|
|
234
|
+
input:
|
|
235
|
+
| WidgetLayoutInstantUpdatePlanInput
|
|
236
|
+
| WidgetLayoutTimedCapableUpdatePlanInput
|
|
237
|
+
| WidgetLayoutInstantPolicyPlanInput
|
|
238
|
+
| WidgetLayoutTimedCapablePolicyPlanInput
|
|
239
|
+
| WidgetLayoutOptionalPolicyPlanInput,
|
|
240
|
+
): WidgetLayoutTimedCapableTransitionPlan {
|
|
147
241
|
if (!Number.isFinite(input.nowMs)) {
|
|
148
242
|
throw new RangeError("Widget layout transition plans require finite nowMs");
|
|
149
243
|
}
|
|
150
|
-
|
|
244
|
+
const next =
|
|
245
|
+
"next" in input
|
|
246
|
+
? input.next
|
|
247
|
+
: toWidgetLayoutSizeUpdateForPolicy({
|
|
248
|
+
size: input.size,
|
|
249
|
+
policy: input.transitionPolicy,
|
|
250
|
+
});
|
|
251
|
+
if (areWidgetLayoutSizeUpdatesEqual(input.previous, next)) {
|
|
151
252
|
return {
|
|
152
253
|
kind: "unchanged",
|
|
153
|
-
size: { ...
|
|
154
|
-
update: cloneWidgetLayoutSizeUpdate(
|
|
254
|
+
size: { ...next.size },
|
|
255
|
+
update: cloneWidgetLayoutSizeUpdate(next),
|
|
155
256
|
observedAtMs: input.nowMs,
|
|
156
257
|
};
|
|
157
258
|
}
|
|
158
|
-
if (isWidgetLayoutTimedSizeUpdate(
|
|
259
|
+
if (isWidgetLayoutTimedSizeUpdate(next)) {
|
|
159
260
|
return {
|
|
160
261
|
kind: "timed",
|
|
161
|
-
size: { ...
|
|
162
|
-
update: cloneWidgetLayoutSizeUpdate(
|
|
262
|
+
size: { ...next.size },
|
|
263
|
+
update: cloneWidgetLayoutSizeUpdate(next),
|
|
163
264
|
startedAtMs: input.nowMs,
|
|
164
|
-
settledAtMs: input.nowMs +
|
|
265
|
+
settledAtMs: input.nowMs + next.transition.durationMs,
|
|
165
266
|
};
|
|
166
267
|
}
|
|
167
268
|
return {
|
|
168
269
|
kind: "instant",
|
|
169
|
-
size: { ...
|
|
170
|
-
update: cloneWidgetLayoutSizeUpdate(
|
|
270
|
+
size: { ...next.size },
|
|
271
|
+
update: cloneWidgetLayoutSizeUpdate(next),
|
|
171
272
|
startedAtMs: input.nowMs,
|
|
172
273
|
settledAtMs: input.nowMs,
|
|
173
274
|
};
|