@roku-sdk/components 0.0.0-stage → 2.19.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/BREAKING_CHANGES.md +84 -0
- package/LICENSE +21 -0
- package/README.md +19 -2
- package/core-jsx.d.ts +53 -0
- package/eslint.config.mjs +3 -0
- package/jsx.d.ts +273 -0
- package/package.json +85 -3
- package/src/__tests__/container.test.tsx +150 -0
- package/src/__tests__/effect-gradient-validation.test.tsx +133 -0
- package/src/__tests__/glyphs.test.ts +202 -0
- package/src/__tests__/modal-boundary-export.test.ts +12 -0
- package/src/__tests__/native-modal.test.tsx +224 -0
- package/src/__tests__/tween-release.test.tsx +125 -0
- package/src/__tests__/tween-stops-shape.test.tsx +165 -0
- package/src/__tests__/tween-without-compat.test.tsx +64 -0
- package/src/__tests__/tween.test.tsx +195 -0
- package/src/blended-busy-spinner.props.ts +37 -0
- package/src/blended-busy-spinner.tsx +13 -0
- package/src/bounding-tracker.props.ts +18 -0
- package/src/bounding-tracker.tsx +13 -0
- package/src/button-row.tsx +126 -0
- package/src/channel-store.props.ts +425 -0
- package/src/channel-store.tsx +76 -0
- package/src/container.tsx +130 -0
- package/src/core/jsx-runtime.ts +39 -0
- package/src/focusable-group.props.ts +8 -0
- package/src/focusable-group.tsx +17 -0
- package/src/focusable.props.ts +14 -0
- package/src/focusable.tsx +17 -0
- package/src/glyphs.ts +116 -0
- package/src/helpers/index.tsx +4 -0
- package/src/helpers/resolution-context.tsx +68 -0
- package/src/helpers/scale-for-device-resolution.ts +31 -0
- package/src/index.tsx +70 -0
- package/src/jsx-props.ts +87 -0
- package/src/kdfs.d.ts +14 -0
- package/src/kdfs.ts +15 -0
- package/src/key-handler.props.ts +33 -0
- package/src/key-handler.tsx +24 -0
- package/src/modal-dialog.props.ts +38 -0
- package/src/modal-dialog.tsx +41 -0
- package/src/retain-asset.ts +24 -0
- package/src/sg-intrinsics.json +1153 -0
- package/src/sg-nodes/__tests__/__snapshots__/font.test.tsx.snap +13 -0
- package/src/sg-nodes/__tests__/__snapshots__/prop-forwarding.test.tsx.snap +4409 -0
- package/src/sg-nodes/__tests__/core-props.test.ts +301 -0
- package/src/sg-nodes/__tests__/font.test.tsx +141 -0
- package/src/sg-nodes/__tests__/imperative-methods.test.ts +276 -0
- package/src/sg-nodes/__tests__/prop-forwarding.test.tsx +1049 -0
- package/src/sg-nodes/__tests__/solid-integration.test.tsx +719 -0
- package/src/sg-nodes/animated-image.props.ts +64 -0
- package/src/sg-nodes/animated-image.tsx +23 -0
- package/src/sg-nodes/animation.props.ts +292 -0
- package/src/sg-nodes/animation.tsx +190 -0
- package/src/sg-nodes/array-grid.ts +602 -0
- package/src/sg-nodes/audio.props.ts +302 -0
- package/src/sg-nodes/audio.tsx +57 -0
- package/src/sg-nodes/busy-spinner.props.ts +59 -0
- package/src/sg-nodes/busy-spinner.tsx +84 -0
- package/src/sg-nodes/button-group.props.ts +141 -0
- package/src/sg-nodes/button-group.tsx +30 -0
- package/src/sg-nodes/button.props.ts +119 -0
- package/src/sg-nodes/button.tsx +31 -0
- package/src/sg-nodes/check-list.props.ts +71 -0
- package/src/sg-nodes/check-list.tsx +55 -0
- package/src/sg-nodes/content-node.props.ts +1316 -0
- package/src/sg-nodes/content-node.tsx +153 -0
- package/src/sg-nodes/core-props.ts +197 -0
- package/src/sg-nodes/custom-keyboard.props.ts +108 -0
- package/src/sg-nodes/custom-keyboard.tsx +48 -0
- package/src/sg-nodes/dialog.props.ts +159 -0
- package/src/sg-nodes/dialog.tsx +36 -0
- package/src/sg-nodes/dynamic-key-grid.props.ts +22 -0
- package/src/sg-nodes/dynamic-key-grid.tsx +16 -0
- package/src/sg-nodes/dynamic-keyboard.props.ts +30 -0
- package/src/sg-nodes/dynamic-keyboard.tsx +18 -0
- package/src/sg-nodes/dynamic-mini-keyboard.props.ts +22 -0
- package/src/sg-nodes/dynamic-mini-keyboard.tsx +18 -0
- package/src/sg-nodes/dynamic-pinpad.props.ts +57 -0
- package/src/sg-nodes/dynamic-pinpad.tsx +18 -0
- package/src/sg-nodes/effect.props.ts +119 -0
- package/src/sg-nodes/effect.tsx +167 -0
- package/src/sg-nodes/font.props.ts +45 -0
- package/src/sg-nodes/font.tsx +80 -0
- package/src/sg-nodes/group.props.ts +105 -0
- package/src/sg-nodes/group.tsx +30 -0
- package/src/sg-nodes/label-list.props.ts +86 -0
- package/src/sg-nodes/label-list.tsx +64 -0
- package/src/sg-nodes/label.props.ts +207 -0
- package/src/sg-nodes/label.tsx +56 -0
- package/src/sg-nodes/layout-group.props.ts +69 -0
- package/src/sg-nodes/layout-group.tsx +75 -0
- package/src/sg-nodes/list-panel.props.ts +103 -0
- package/src/sg-nodes/list-panel.tsx +49 -0
- package/src/sg-nodes/markup-grid.props.ts +78 -0
- package/src/sg-nodes/markup-grid.tsx +38 -0
- package/src/sg-nodes/markup-list.props.ts +66 -0
- package/src/sg-nodes/markup-list.tsx +38 -0
- package/src/sg-nodes/mask-group.props.ts +78 -0
- package/src/sg-nodes/mask-group.tsx +59 -0
- package/src/sg-nodes/media-playback-methods.ts +20 -0
- package/src/sg-nodes/node.ts +77 -0
- package/src/sg-nodes/overhang.props.ts +169 -0
- package/src/sg-nodes/overhang.tsx +29 -0
- package/src/sg-nodes/panel-set.props.ts +71 -0
- package/src/sg-nodes/panel-set.tsx +53 -0
- package/src/sg-nodes/panel.props.ts +176 -0
- package/src/sg-nodes/panel.tsx +56 -0
- package/src/sg-nodes/poster-grid.props.ts +277 -0
- package/src/sg-nodes/poster-grid.tsx +45 -0
- package/src/sg-nodes/poster.props.ts +125 -0
- package/src/sg-nodes/poster.tsx +44 -0
- package/src/sg-nodes/radio-button-list.props.ts +59 -0
- package/src/sg-nodes/radio-button-list.tsx +68 -0
- package/src/sg-nodes/rectangle.props.ts +29 -0
- package/src/sg-nodes/rectangle.tsx +27 -0
- package/src/sg-nodes/row-list.props.ts +288 -0
- package/src/sg-nodes/row-list.tsx +47 -0
- package/src/sg-nodes/scene.props.ts +65 -0
- package/src/sg-nodes/scene.tsx +15 -0
- package/src/sg-nodes/scrollable-text.props.ts +133 -0
- package/src/sg-nodes/scrollable-text.tsx +64 -0
- package/src/sg-nodes/scrolling-label.props.ts +106 -0
- package/src/sg-nodes/scrolling-label.tsx +42 -0
- package/src/sg-nodes/simple-label.props.ts +156 -0
- package/src/sg-nodes/simple-label.tsx +59 -0
- package/src/sg-nodes/sound-effect.props.ts +130 -0
- package/src/sg-nodes/sound-effect.tsx +65 -0
- package/src/sg-nodes/standard-dialog.props.ts +520 -0
- package/src/sg-nodes/standard-dialog.tsx +428 -0
- package/src/sg-nodes/standard-keyboard-dialog.props.ts +84 -0
- package/src/sg-nodes/standard-keyboard-dialog.tsx +58 -0
- package/src/sg-nodes/standard-message-dialog.props.ts +56 -0
- package/src/sg-nodes/standard-message-dialog.tsx +47 -0
- package/src/sg-nodes/standard-pinpad-dialog.props.ts +64 -0
- package/src/sg-nodes/standard-pinpad-dialog.tsx +44 -0
- package/src/sg-nodes/standard-progress-dialog.props.ts +30 -0
- package/src/sg-nodes/standard-progress-dialog.tsx +40 -0
- package/src/sg-nodes/target-group.props.ts +278 -0
- package/src/sg-nodes/target-group.tsx +63 -0
- package/src/sg-nodes/target-list.props.ts +128 -0
- package/src/sg-nodes/target-list.tsx +66 -0
- package/src/sg-nodes/target-set.props.ts +63 -0
- package/src/sg-nodes/target-set.tsx +42 -0
- package/src/sg-nodes/time-grid.props.ts +646 -0
- package/src/sg-nodes/time-grid.tsx +87 -0
- package/src/sg-nodes/tween.props.ts +115 -0
- package/src/sg-nodes/types.ts +150 -0
- package/src/sg-nodes/video.props.ts +642 -0
- package/src/sg-nodes/video.tsx +75 -0
- package/src/sg-nodes/voice-text-edit-box.props.ts +91 -0
- package/src/sg-nodes/voice-text-edit-box.tsx +15 -0
- package/src/sg-nodes/zoom-row-list.props.ts +416 -0
- package/src/sg-nodes/zoom-row-list.tsx +42 -0
- package/src/standard-dialog-next/__tests__/standard-dialog-next.test.tsx +186 -0
- package/src/standard-dialog-next/index.ts +5 -0
- package/src/standard-dialog-next/standard-dialog-next.props.ts +80 -0
- package/src/standard-dialog-next/standard-dialog-next.tsx +326 -0
- package/src/test-helpers/tween-test-setup.ts +78 -0
- package/src/tween.tsx +173 -0
- package/src/types.ts +8 -0
- package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.brs +13 -0
- package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml +10 -0
- package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml.d.ts +42 -0
- package/src/xml-components/bounding-tracker/BoundingTracker.brs +26 -0
- package/src/xml-components/bounding-tracker/BoundingTracker.xml +14 -0
- package/src/xml-components/bounding-tracker/BoundingTracker.xml.d.ts +42 -0
- package/src/xml-components/channel-store/channelStoreExt.brs +159 -0
- package/src/xml-components/channel-store/channelStoreExt.xml +29 -0
- package/src/xml-components/channel-store/channelStoreExt.xml.d.ts +57 -0
- package/src/xml-components/focus/focusable-group.brs +251 -0
- package/src/xml-components/focus/focusable-group.xml +15 -0
- package/src/xml-components/focus/focusable-group.xml.d.ts +42 -0
- package/src/xml-components/focus/focusable.xml +18 -0
- package/src/xml-components/focus/focusable.xml.d.ts +48 -0
- package/src/xml-components/keyboards/CustomAddressKDF.keyboard.json +547 -0
- package/src/xml-components/keyboards/CustomKeyboard.brs +322 -0
- package/src/xml-components/keyboards/CustomKeyboard.xml +45 -0
- package/src/xml-components/keyboards/CustomKeyboard.xml.d.ts +73 -0
- package/src/xml-components/keyboards/KeyGridExt.xml +4 -0
- package/src/xml-components/keyboards/KeyGridExt.xml.d.ts +37 -0
- package/src/xml-components/keyboards/KeyboardExt.xml +4 -0
- package/src/xml-components/keyboards/KeyboardExt.xml.d.ts +37 -0
- package/src/xml-components/keyboards/MOPPinPadCustomKDF.keyboard.json +151 -0
- package/src/xml-components/keyboards/MiniKeyboardExt.xml +4 -0
- package/src/xml-components/keyboards/MiniKeyboardExt.xml.d.ts +37 -0
- package/src/xml-components/keyboards/PhonePadCustomKDF.keyboard.json +42 -0
- package/src/xml-components/keyboards/PinPadExt.brs +102 -0
- package/src/xml-components/keyboards/PinPadExt.xml +17 -0
- package/src/xml-components/keyboards/PinPadExt.xml.d.ts +49 -0
- package/src/xml-components/keyboards/WifiCustomKDF.keyboard.json +1242 -0
- package/src/xml-components/keyhandler/KeyHandler.brs +30 -0
- package/src/xml-components/keyhandler/KeyHandler.xml +16 -0
- package/src/xml-components/keyhandler/KeyHandler.xml.d.ts +45 -0
- package/src/xml-components/managed-list-panel/ManagedListPanel.brs +137 -0
- package/src/xml-components/managed-list-panel/ManagedListPanel.xml +10 -0
- package/src/xml-components/managed-list-panel/ManagedListPanel.xml.d.ts +37 -0
- package/src/xml-components/modal/Modal.brs +105 -0
- package/src/xml-components/modal/Modal.xml +22 -0
- package/src/xml-components/modal/Modal.xml.d.ts +54 -0
- package/src/xml-components/modal/StandardDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardKeyboardDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardKeyboardDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardMessageDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardMessageDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardPinPadDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardPinPadDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardProgressDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardProgressDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/tests/Modal.test.brs +195 -0
- package/tsconfig.json +10 -0
package/src/tween.tsx
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import { type DomNode } from "@roku-sdk/interfaces";
|
|
5
|
+
import { type Component, createEffect, onCleanup, tmTransaction, untrack } from "@roku-sdk/solid";
|
|
6
|
+
|
|
7
|
+
import type { AnimationState } from "./sg-nodes/animation.props";
|
|
8
|
+
import type { ITweenProps } from "./sg-nodes/tween.props";
|
|
9
|
+
import type { Vector2D } from "./sg-nodes/types";
|
|
10
|
+
|
|
11
|
+
declare const __DEV__: boolean;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Unified `<tween>` primitive.
|
|
15
|
+
*
|
|
16
|
+
* Animate a target node using the unified native field contract.
|
|
17
|
+
*
|
|
18
|
+
* One component presenting the unified tween field contract, replacing the four earlier
|
|
19
|
+
* tween wrappers (`Tween`/GridTween, `TweenFloat`, `FloatTween`, `ScaleTween`). On device
|
|
20
|
+
* it renders a `<tween>` element, which is the firmware's native node when
|
|
21
|
+
* `roku.hasFeature("tween")` reports support. Otherwise `@roku-sdk/compat` aliases the tag
|
|
22
|
+
* to its `RsgTweenShim`; without compat creating it throws an error naming the missing feature. A renderer that
|
|
23
|
+
* provides its own tween implementation aliases this module to it at build time.
|
|
24
|
+
*/
|
|
25
|
+
export interface TweenProps extends Omit<ITweenProps, "targetNode" | "onAnimationState"> {
|
|
26
|
+
/** Node reference to animate, or a getter returning it. Takes precedence over targetRef. */
|
|
27
|
+
targetNode?: unknown | (() => unknown);
|
|
28
|
+
/** @deprecated Use targetNode. Retained for one minor release. */
|
|
29
|
+
targetRef?: unknown | (() => unknown);
|
|
30
|
+
/** Called with separate state and value arguments, unlike the intrinsic's tuple event. */
|
|
31
|
+
onAnimationState?: (state: AnimationState, value: number | Vector2D) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let tweenIdCounter = 0;
|
|
35
|
+
|
|
36
|
+
/** Fields the dev-only `proposedStops` shape check knows to be vector2d. */
|
|
37
|
+
const VECTOR_FIELDS: ReadonlySet<string> = new Set(["translation", "scale", "scaleRotateCenter"]);
|
|
38
|
+
/** Fields the dev-only `proposedStops` shape check knows to be float. */
|
|
39
|
+
const FLOAT_FIELDS: ReadonlySet<string> = new Set(["opacity", "width", "height", "rotation"]);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Dev-only: describe each way `proposedStops` (and `axis`) does not match the shape the field
|
|
43
|
+
* and axis call for. Returns `[key, message]` pairs, where `key` names the distinct problem so a
|
|
44
|
+
* caller can report each one once. A field outside the known lists is not checked.
|
|
45
|
+
*
|
|
46
|
+
* The expected shapes: float fields, and `axis` `"x"` / `"y"` on a vector field, take
|
|
47
|
+
* `number[]`; `axis` `"all"` (the default) on a vector field takes `[number, number][]`; `axis`
|
|
48
|
+
* is ignored on float fields.
|
|
49
|
+
*/
|
|
50
|
+
function proposedStopsShapeProblems(
|
|
51
|
+
field: string,
|
|
52
|
+
axis: string | undefined,
|
|
53
|
+
stops: readonly unknown[] | undefined
|
|
54
|
+
): [key: string, message: string][] {
|
|
55
|
+
const problems: [string, string][] = [];
|
|
56
|
+
const list = Array.isArray(stops) ? stops : [];
|
|
57
|
+
if (VECTOR_FIELDS.has(field)) {
|
|
58
|
+
if (axis === "x" || axis === "y") {
|
|
59
|
+
if (list.some(stop => Array.isArray(stop))) {
|
|
60
|
+
problems.push([
|
|
61
|
+
`${axis}:array`,
|
|
62
|
+
`Tween: proposedStops on "${field}" with axis "${axis}" must be scalars (the ${axis} coordinate); got an array. Array entries are skipped.`
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
const shown = axis ?? "all";
|
|
67
|
+
if (list.some(stop => !Array.isArray(stop))) {
|
|
68
|
+
problems.push([
|
|
69
|
+
"all:scalar",
|
|
70
|
+
`Tween: proposedStops on "${field}" with axis "${shown}" must be [x, y] pairs; got a scalar. Scalar entries are skipped.`
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
const wrongLength = list.find(stop => Array.isArray(stop) && stop.length !== 2) as unknown[] | undefined;
|
|
74
|
+
if (wrongLength) {
|
|
75
|
+
problems.push([
|
|
76
|
+
"all:length",
|
|
77
|
+
`Tween: proposedStops on "${field}" with axis "${shown}" must be [x, y] pairs; got an array of length ${wrongLength.length}. Entries that are not pairs are skipped.`
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} else if (FLOAT_FIELDS.has(field)) {
|
|
82
|
+
if (axis !== undefined) {
|
|
83
|
+
problems.push(["float:axis", `Tween: axis "${axis}" is ignored on float field "${field}"; remove it.`]);
|
|
84
|
+
}
|
|
85
|
+
if (list.some(stop => Array.isArray(stop))) {
|
|
86
|
+
problems.push([
|
|
87
|
+
"float:array",
|
|
88
|
+
`Tween: proposedStops on float field "${field}" must be numbers; got an array. Array entries are skipped.`
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return problems;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const resolveTarget = (targetRef: unknown | (() => unknown)): unknown =>
|
|
96
|
+
typeof targetRef === "function" ? (targetRef as () => unknown)() : targetRef;
|
|
97
|
+
|
|
98
|
+
export const Tween: Component<TweenProps> = props => {
|
|
99
|
+
const animationId = `tweenAnim_${props.id ?? ""}_${tweenIdCounter++}`;
|
|
100
|
+
const field = props.field ?? "translation";
|
|
101
|
+
|
|
102
|
+
let tweenRef: unknown;
|
|
103
|
+
let boundTarget: unknown;
|
|
104
|
+
|
|
105
|
+
const bindTarget = (target: unknown): void => {
|
|
106
|
+
if (tweenRef == null || target == null || target === boundTarget) return;
|
|
107
|
+
boundTarget = target;
|
|
108
|
+
tmTransaction.setProperty(tweenRef as DomNode, "targetNode", target as DomNode, undefined);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// A trigger fires on whatever node is bound when it lands, so a target switched in the
|
|
112
|
+
// same update as a trigger has to be written first, whichever effect runs first.
|
|
113
|
+
const withTarget = <V,>(value: V): V => {
|
|
114
|
+
bindTarget(untrack(() => resolveTarget(props.targetNode ?? props.targetRef)));
|
|
115
|
+
return value;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Keep the node's targetNode bound to the current ref (parents may populate a ref after
|
|
119
|
+
// mount, and pooled nodes are recycled onto new cells).
|
|
120
|
+
createEffect(function syncTargetNodeEffect() {
|
|
121
|
+
bindTarget(resolveTarget(props.targetNode ?? props.targetRef));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// The node and its target reference each other on the render thread, so a disposed tween
|
|
125
|
+
// still holding the target keeps its whole subtree alive. `prev` stays undefined: a node
|
|
126
|
+
// passed as `prev` is queued for removal.
|
|
127
|
+
onCleanup(() => {
|
|
128
|
+
if (tweenRef == null) return;
|
|
129
|
+
tmTransaction.setProperty(tweenRef as DomNode, "targetNode", null as unknown as DomNode, undefined);
|
|
130
|
+
tweenRef = undefined;
|
|
131
|
+
boundTarget = undefined;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Dev-only: warn when proposedStops (or axis) does not have the shape the field and axis
|
|
135
|
+
// call for, once per distinct problem per instance. DCE'd when __DEV__ is false.
|
|
136
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
137
|
+
const warned = new Set<string>();
|
|
138
|
+
createEffect(function checkProposedStopsShapeEffect() {
|
|
139
|
+
for (const [key, message] of proposedStopsShapeProblems(field, props.axis, props.proposedStops)) {
|
|
140
|
+
if (warned.has(key)) continue;
|
|
141
|
+
warned.add(key);
|
|
142
|
+
console.warn(message);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const bindTween = (el: unknown): void => {
|
|
148
|
+
tweenRef = el;
|
|
149
|
+
bindTarget(resolveTarget(props.targetNode ?? props.targetRef));
|
|
150
|
+
};
|
|
151
|
+
const onAnimationState = ([state, value]: [AnimationState, number | Vector2D]): void => {
|
|
152
|
+
props.onAnimationState?.(state, value);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<tween
|
|
157
|
+
ref={bindTween}
|
|
158
|
+
id={`tween-${animationId}`}
|
|
159
|
+
field={field}
|
|
160
|
+
duration={props.duration ?? 0.333}
|
|
161
|
+
easeFunction={props.easeFunction ?? "outCubic"}
|
|
162
|
+
axis={props.axis ?? "all"}
|
|
163
|
+
preserveOther={props.preserveOther ?? 0}
|
|
164
|
+
targetValue={withTarget(props.targetValue)}
|
|
165
|
+
targetVector={withTarget(props.targetVector)}
|
|
166
|
+
proposedStops={withTarget(props.proposedStops)}
|
|
167
|
+
control={withTarget(props.control)}
|
|
168
|
+
onAnimationStart={props.onAnimationStart}
|
|
169
|
+
onAnimationStop={props.onAnimationStop}
|
|
170
|
+
onAnimationState={props.onAnimationState ? onAnimationState : undefined}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
export * from "./sg-nodes/types";
|
|
5
|
+
|
|
6
|
+
export type MediaControl = "none" | "play" | "pause" | "stop" | "resume" | "replay" | "prebuffer" | "skipcontent";
|
|
7
|
+
|
|
8
|
+
export type MediaState = "none" | "buffering" | "playing" | "paused" | "stopping" | "stopped" | "finished" | "error";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
|
|
3
|
+
<component name="BlendedBusySpinner" extends="BusySpinner">
|
|
4
|
+
<script type="text/brightscript" uri="BlendedBusySpinner.brs" />
|
|
5
|
+
<interface>
|
|
6
|
+
<field id="uri" type="string" onChange="onUriChanged" />
|
|
7
|
+
<field id="blendColor" type="string" onChange="onBlendColorChanged" />
|
|
8
|
+
</interface>
|
|
9
|
+
|
|
10
|
+
</component>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Auto-generated TypeScript declarations for XML component import.
|
|
2
|
+
// DO NOT EDIT — this file is regenerated during build.
|
|
3
|
+
//
|
|
4
|
+
// Source component: BlendedBusySpinner
|
|
5
|
+
// Extends: BusySpinner
|
|
6
|
+
|
|
7
|
+
import type { IBusySpinnerProps } from "@roku-sdk/components";
|
|
8
|
+
import type { TypedXmlComponent } from "@roku-sdk/interfaces";
|
|
9
|
+
|
|
10
|
+
interface BlendedBusySpinnerFields {
|
|
11
|
+
uri: string;
|
|
12
|
+
blendColor: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IBlendedBusySpinnerProps extends IBusySpinnerProps, Partial<BlendedBusySpinnerFields> {}
|
|
16
|
+
|
|
17
|
+
declare const _default: TypedXmlComponent<"BlendedBusySpinner", BlendedBusySpinnerFields>;
|
|
18
|
+
export default _default;
|
|
19
|
+
|
|
20
|
+
declare module "@roku-sdk/solid" {
|
|
21
|
+
namespace JSX {
|
|
22
|
+
interface IntrinsicElements {
|
|
23
|
+
blendedBusySpinner: IBlendedBusySpinnerProps;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare module "@roku-sdk/solid/jsx-runtime" {
|
|
29
|
+
namespace JSX {
|
|
30
|
+
interface IntrinsicElements {
|
|
31
|
+
blendedBusySpinner: IBlendedBusySpinnerProps;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare module "solid-js" {
|
|
37
|
+
namespace JSX {
|
|
38
|
+
interface IntrinsicElements {
|
|
39
|
+
blendedBusySpinner: IBlendedBusySpinnerProps;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
' Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
' SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
sub init()
|
|
5
|
+
m.top.BoundsChange = m.top.boundingRect()
|
|
6
|
+
m.top.observeField("change", "onChange")
|
|
7
|
+
m.top.observeField("doMeasure", "doMeasure")
|
|
8
|
+
end sub
|
|
9
|
+
|
|
10
|
+
sub onChange()
|
|
11
|
+
setBoundingRect()
|
|
12
|
+
end sub
|
|
13
|
+
|
|
14
|
+
sub doMeasure(msg as Object)
|
|
15
|
+
data = msg.getData()
|
|
16
|
+
if data = true
|
|
17
|
+
setBoundingRect()
|
|
18
|
+
end if
|
|
19
|
+
end sub
|
|
20
|
+
|
|
21
|
+
sub setBoundingRect()
|
|
22
|
+
rect = m.top.boundingRect()
|
|
23
|
+
if rect.x <> m.top.BoundsChange.x or rect.y <> m.top.BoundsChange.y or rect.width <> m.top.BoundsChange.width or rect.height <> m.top.BoundsChange.height then
|
|
24
|
+
m.top.BoundsChange = rect
|
|
25
|
+
end if
|
|
26
|
+
end sub
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Copyright (c) 2026 Roku, Inc.
|
|
4
|
+
SPDX-License-Identifier: MIT
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
<component name="bounding-tracker" extends="Group" focusable="true">
|
|
9
|
+
<script type="text/brightscript" uri="BoundingTracker.brs" />
|
|
10
|
+
<interface>
|
|
11
|
+
<field id="BoundsChange" type="assocarray" />
|
|
12
|
+
<field id="doMeasure" type="bool" />
|
|
13
|
+
</interface>
|
|
14
|
+
</component>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Auto-generated TypeScript declarations for XML component import.
|
|
2
|
+
// DO NOT EDIT — this file is regenerated during build.
|
|
3
|
+
//
|
|
4
|
+
// Source component: bounding-tracker
|
|
5
|
+
// Extends: Group
|
|
6
|
+
|
|
7
|
+
import type { IGroupProps } from "@roku-sdk/components";
|
|
8
|
+
import type { TypedXmlComponent } from "@roku-sdk/interfaces";
|
|
9
|
+
|
|
10
|
+
interface boundingTrackerFields {
|
|
11
|
+
BoundsChange: Record<string, unknown>;
|
|
12
|
+
doMeasure: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IboundingTrackerProps extends IGroupProps, Partial<boundingTrackerFields> {}
|
|
16
|
+
|
|
17
|
+
declare const _default: TypedXmlComponent<"bounding-tracker", boundingTrackerFields>;
|
|
18
|
+
export default _default;
|
|
19
|
+
|
|
20
|
+
declare module "@roku-sdk/solid" {
|
|
21
|
+
namespace JSX {
|
|
22
|
+
interface IntrinsicElements {
|
|
23
|
+
boundingTracker: IboundingTrackerProps;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare module "@roku-sdk/solid/jsx-runtime" {
|
|
29
|
+
namespace JSX {
|
|
30
|
+
interface IntrinsicElements {
|
|
31
|
+
boundingTracker: IboundingTrackerProps;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare module "solid-js" {
|
|
37
|
+
namespace JSX {
|
|
38
|
+
interface IntrinsicElements {
|
|
39
|
+
boundingTracker: IboundingTrackerProps;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
' Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
' SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
Sub init()
|
|
5
|
+
m.top.observeField("userData", "updateUserData")
|
|
6
|
+
m.top.observeField("userRegionData", "updateUserRegionData")
|
|
7
|
+
m.top.observeField("requestedUserDataInfoObject", "updateRequestedUserDataInfo")
|
|
8
|
+
m.top.observeField("storeChannelCredDataStatus", "updateChannelCredDataStatus")
|
|
9
|
+
m.top.observeField("channelCred", "updateChannelCred")
|
|
10
|
+
m.top.observeField("catalog", "updateCatalog")
|
|
11
|
+
m.top.observeField("order", "updateOrder")
|
|
12
|
+
m.top.observeField("orderStatus", "updateOrderStatus")
|
|
13
|
+
m.top.observeField("purchases", "updatePurchases")
|
|
14
|
+
m.top.observeField("requestPartnerOrderObject", "updateRequestPartnerOrder")
|
|
15
|
+
m.top.observeField("requestPartnerOrderStatus", "updateRequestPartnerOrderStatus")
|
|
16
|
+
m.top.observeField("confirmPartnerOrderObject", "updateConfirmPartnerOrder")
|
|
17
|
+
m.top.observeField("confirmPartnerOrderStatus", "updateConfirmPartnerOrderStatus")
|
|
18
|
+
m.top.observeField("deviceAttestationToken", "updateDeviceAttestationToken")
|
|
19
|
+
end Sub
|
|
20
|
+
|
|
21
|
+
sub updateUserData()
|
|
22
|
+
userData = m.top.userData
|
|
23
|
+
if userData <> invalid
|
|
24
|
+
m.top.userDataObject = m.top.userData.getFields()
|
|
25
|
+
else
|
|
26
|
+
m.top.userDataObject = {}
|
|
27
|
+
end if
|
|
28
|
+
end sub
|
|
29
|
+
|
|
30
|
+
sub updateUserRegionData()
|
|
31
|
+
userRegionData = m.top.userRegionData
|
|
32
|
+
if userRegionData <> invalid
|
|
33
|
+
m.top.userRegionDataObject = m.top.userRegionData.getFields()
|
|
34
|
+
else
|
|
35
|
+
m.top.userRegionDataObject = {}
|
|
36
|
+
end if
|
|
37
|
+
end sub
|
|
38
|
+
|
|
39
|
+
sub updateRequestedUserDataInfo()
|
|
40
|
+
cn = CreateObject("roSGNode","ContentNode")
|
|
41
|
+
cn.Update(m.top.requestedUserDataInfoObject, true)
|
|
42
|
+
m.top.requestedUserDataInfo = cn
|
|
43
|
+
end sub
|
|
44
|
+
|
|
45
|
+
sub updateChannelCredDataStatus()
|
|
46
|
+
storeChannelCredDataStatus = m.top.storeChannelCredDataStatus
|
|
47
|
+
if storeChannelCredDataStatus <> invalid
|
|
48
|
+
m.top.storeChannelCredDataStatusObject = m.top.storeChannelCredDataStatus.getFields()
|
|
49
|
+
else
|
|
50
|
+
m.top.storeChannelCredDataStatusObject = {}
|
|
51
|
+
end if
|
|
52
|
+
end sub
|
|
53
|
+
|
|
54
|
+
sub updateChannelCred()
|
|
55
|
+
channelCred = m.top.channelCred
|
|
56
|
+
if channelCred <> invalid
|
|
57
|
+
m.top.channelCredObject = m.top.channelCred.getFields()
|
|
58
|
+
else
|
|
59
|
+
m.top.channelCredObject = {}
|
|
60
|
+
end if
|
|
61
|
+
end sub
|
|
62
|
+
|
|
63
|
+
sub updateCatalog()
|
|
64
|
+
catalog = m.top.catalog
|
|
65
|
+
if catalog <> invalid
|
|
66
|
+
result = []
|
|
67
|
+
products = catalog.getChildren(-1, 0)
|
|
68
|
+
for each product in products
|
|
69
|
+
result.push(product.getFields())
|
|
70
|
+
end for
|
|
71
|
+
m.top.catalogObject = result
|
|
72
|
+
else
|
|
73
|
+
m.top.catalogObject = []
|
|
74
|
+
end if
|
|
75
|
+
end sub
|
|
76
|
+
|
|
77
|
+
sub updateOrder()
|
|
78
|
+
order = m.top.orderObject
|
|
79
|
+
cn = CreateObject("roSGNode","ContentNode")
|
|
80
|
+
for each item in order
|
|
81
|
+
itemCn = cn.appendChild("ContentNode")
|
|
82
|
+
itemCn.Update(item, true)
|
|
83
|
+
end for
|
|
84
|
+
m.top.order = cn
|
|
85
|
+
end sub
|
|
86
|
+
|
|
87
|
+
sub updateOrderStatus()
|
|
88
|
+
orderStatus = m.top.orderStatus
|
|
89
|
+
if orderStatus <> invalid
|
|
90
|
+
m.top.orderStatusObject = orderStatus.getFields()
|
|
91
|
+
|
|
92
|
+
orderItems = []
|
|
93
|
+
items = orderStatus.getChildren(-1, 0)
|
|
94
|
+
for each item in items
|
|
95
|
+
orderItems.push(item.getFields())
|
|
96
|
+
end for
|
|
97
|
+
m.top.orderItemsStatusObject = orderItems
|
|
98
|
+
else
|
|
99
|
+
m.top.orderItemsStatusObject = []
|
|
100
|
+
end if
|
|
101
|
+
end sub
|
|
102
|
+
|
|
103
|
+
sub updatePurchases()
|
|
104
|
+
purchases = m.top.purchases
|
|
105
|
+
if purchases <> invalid
|
|
106
|
+
m.top.purchasesStatusObject = purchases.getFields()
|
|
107
|
+
|
|
108
|
+
purchaseItems = []
|
|
109
|
+
items = purchases.getChildren(-1, 0)
|
|
110
|
+
for each item in items
|
|
111
|
+
purchaseItems.push(item.getFields())
|
|
112
|
+
end for
|
|
113
|
+
m.top.purchasesObject = purchaseItems
|
|
114
|
+
else
|
|
115
|
+
m.top.purchasesStatusObject = {}
|
|
116
|
+
m.top.purchasesObject = []
|
|
117
|
+
end if
|
|
118
|
+
end sub
|
|
119
|
+
|
|
120
|
+
sub updateRequestPartnerOrder()
|
|
121
|
+
partnerOrder = m.top.requestPartnerOrderObject
|
|
122
|
+
cn = CreateObject("roSGNode","ContentNode")
|
|
123
|
+
cn.Update(partnerOrder, true)
|
|
124
|
+
m.top.requestPartnerOrder = cn
|
|
125
|
+
end sub
|
|
126
|
+
|
|
127
|
+
sub updateRequestPartnerOrderStatus()
|
|
128
|
+
partnerOrderStatus = m.top.requestPartnerOrderStatus
|
|
129
|
+
if partnerOrderStatus <> invalid
|
|
130
|
+
m.top.requestPartnerOrderStatusObject = partnerOrderStatus.getFields()
|
|
131
|
+
else
|
|
132
|
+
m.top.requestPartnerOrderStatusObject = {}
|
|
133
|
+
end if
|
|
134
|
+
end sub
|
|
135
|
+
|
|
136
|
+
sub updateConfirmPartnerOrder()
|
|
137
|
+
partnerOrder = m.top.confirmPartnerOrderObject
|
|
138
|
+
cn = CreateObject("roSGNode","ContentNode")
|
|
139
|
+
cn.Update(partnerOrder, true)
|
|
140
|
+
m.top.confirmPartnerOrder = cn
|
|
141
|
+
end sub
|
|
142
|
+
|
|
143
|
+
sub updateConfirmPartnerOrderStatus()
|
|
144
|
+
partnerOrderStatus = m.top.confirmPartnerOrderStatus
|
|
145
|
+
if partnerOrderStatus <> invalid
|
|
146
|
+
m.top.confirmPartnerOrderStatusObject = partnerOrderStatus.getFields()
|
|
147
|
+
else
|
|
148
|
+
m.top.confirmPartnerOrderStatusObject = {}
|
|
149
|
+
end if
|
|
150
|
+
end sub
|
|
151
|
+
|
|
152
|
+
sub updateDeviceAttestationToken()
|
|
153
|
+
deviceAttestationToken = m.top.deviceAttestationToken
|
|
154
|
+
if deviceAttestationToken <> invalid
|
|
155
|
+
m.top.deviceAttestationTokenObject = deviceAttestationToken.getFields()
|
|
156
|
+
else
|
|
157
|
+
m.top.deviceAttestationTokenObject = {}
|
|
158
|
+
end if
|
|
159
|
+
end sub
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Copyright (c) 2026 Roku, Inc.
|
|
4
|
+
SPDX-License-Identifier: MIT
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
<component name="channelStoreExt" extends="ChannelStore" focusable="false">
|
|
9
|
+
<script type="text/brightscript" uri="channelStoreExt.brs" />
|
|
10
|
+
<interface>
|
|
11
|
+
<field id="userDataObject" type="assocarray" />
|
|
12
|
+
<field id="userRegionDataObject" type="assocarray" />
|
|
13
|
+
<field id="requestedUserDataInfoObject" type="assocarray" />
|
|
14
|
+
<field id="storeChannelCredDataStatusObject" type="assocarray" />
|
|
15
|
+
<field id="channelCredObject" type="assocarray" />
|
|
16
|
+
<field id="catalogObject" type="array" />
|
|
17
|
+
<field id="orderObject" type="array" />
|
|
18
|
+
<field id="deltaOrderObject" type="assocarray" />
|
|
19
|
+
<field id="orderStatusObject" type="assocarray" />
|
|
20
|
+
<field id="orderItemsStatusObject" type="array" />
|
|
21
|
+
<field id="purchasesStatusObject" type="assocarray" />
|
|
22
|
+
<field id="purchasesObject" type="array" />
|
|
23
|
+
<field id="requestPartnerOrderObject" type="assocarray" />
|
|
24
|
+
<field id="requestPartnerOrderStatusObject" type="assocarray" />
|
|
25
|
+
<field id="confirmPartnerOrderObject" type="assocarray" />
|
|
26
|
+
<field id="confirmPartnerOrderStatusObject" type="assocarray" />
|
|
27
|
+
<field id="deviceAttestationTokenObject" type="assocarray" />
|
|
28
|
+
</interface>
|
|
29
|
+
</component>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Auto-generated TypeScript declarations for XML component import.
|
|
2
|
+
// DO NOT EDIT — this file is regenerated during build.
|
|
3
|
+
//
|
|
4
|
+
// Source component: channelStoreExt
|
|
5
|
+
// Extends: ChannelStore
|
|
6
|
+
|
|
7
|
+
import type { IChannelStoreProps } from "@roku-sdk/components";
|
|
8
|
+
import type { TypedXmlComponent } from "@roku-sdk/interfaces";
|
|
9
|
+
|
|
10
|
+
interface channelStoreExtFields {
|
|
11
|
+
userDataObject: Record<string, unknown>;
|
|
12
|
+
userRegionDataObject: Record<string, unknown>;
|
|
13
|
+
requestedUserDataInfoObject: Record<string, unknown>;
|
|
14
|
+
storeChannelCredDataStatusObject: Record<string, unknown>;
|
|
15
|
+
channelCredObject: Record<string, unknown>;
|
|
16
|
+
catalogObject: unknown[];
|
|
17
|
+
orderObject: unknown[];
|
|
18
|
+
deltaOrderObject: Record<string, unknown>;
|
|
19
|
+
orderStatusObject: Record<string, unknown>;
|
|
20
|
+
orderItemsStatusObject: unknown[];
|
|
21
|
+
purchasesStatusObject: Record<string, unknown>;
|
|
22
|
+
purchasesObject: unknown[];
|
|
23
|
+
requestPartnerOrderObject: Record<string, unknown>;
|
|
24
|
+
requestPartnerOrderStatusObject: Record<string, unknown>;
|
|
25
|
+
confirmPartnerOrderObject: Record<string, unknown>;
|
|
26
|
+
confirmPartnerOrderStatusObject: Record<string, unknown>;
|
|
27
|
+
deviceAttestationTokenObject: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IchannelStoreExtProps extends IChannelStoreProps, Partial<channelStoreExtFields> {}
|
|
31
|
+
|
|
32
|
+
declare const _default: TypedXmlComponent<"channelStoreExt", channelStoreExtFields>;
|
|
33
|
+
export default _default;
|
|
34
|
+
|
|
35
|
+
declare module "@roku-sdk/solid" {
|
|
36
|
+
namespace JSX {
|
|
37
|
+
interface IntrinsicElements {
|
|
38
|
+
channelStoreExt: IchannelStoreExtProps;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare module "@roku-sdk/solid/jsx-runtime" {
|
|
44
|
+
namespace JSX {
|
|
45
|
+
interface IntrinsicElements {
|
|
46
|
+
channelStoreExt: IchannelStoreExtProps;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
declare module "solid-js" {
|
|
52
|
+
namespace JSX {
|
|
53
|
+
interface IntrinsicElements {
|
|
54
|
+
channelStoreExt: IchannelStoreExtProps;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|