@roku-sdk/components 0.0.0-stage → 2.18.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 +84 -3
- package/src/__tests__/container.test.tsx +150 -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 +268 -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 +116 -0
- package/src/sg-nodes/effect.tsx +39 -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 +104 -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 +68 -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 +59 -0
- package/src/sg-nodes/standard-message-dialog.props.ts +56 -0
- package/src/sg-nodes/standard-message-dialog.tsx +48 -0
- package/src/sg-nodes/standard-pinpad-dialog.props.ts +64 -0
- package/src/sg-nodes/standard-pinpad-dialog.tsx +46 -0
- package/src/sg-nodes/standard-progress-dialog.props.ts +30 -0
- package/src/sg-nodes/standard-progress-dialog.tsx +43 -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 +573 -0
- package/src/sg-nodes/video.tsx +73 -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
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { IGroupProps } from "./group.props";
|
|
5
|
+
import type { LoadDisplayMode } from "./poster.props";
|
|
6
|
+
import type { EventHandler, ResType, Uri } from "./types";
|
|
7
|
+
|
|
8
|
+
/** Commands accepted by the AnimatedImage `control` field. */
|
|
9
|
+
export type AnimatedImageControl = "loop" | "pause" | "play" | "rewind";
|
|
10
|
+
|
|
11
|
+
/** Values reported by the AnimatedImage `state` field. */
|
|
12
|
+
export type AnimatedImageState = "downloading" | "init" | "first" | "decode" | "stop" | "error";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* MIME types accepted by the Roku OS AnimatedImage decoder.
|
|
16
|
+
* Matching is exact and case-sensitive; any other value fails with an
|
|
17
|
+
* unsupported-format error. `"video/lottie+json"` covers both Lottie JSON
|
|
18
|
+
* and zipped `.lottie` files.
|
|
19
|
+
*/
|
|
20
|
+
export type AnimatedImageMimeType = "video/mp4" | "video/webp" | "image/webp" | "video/lottie+json";
|
|
21
|
+
|
|
22
|
+
/** Properties for the native AnimatedImage node. */
|
|
23
|
+
export interface IAnimatedImageProps extends IGroupProps {
|
|
24
|
+
/** Width in local coordinates, or the decoded media width when zero. */
|
|
25
|
+
width?: ResType<number>;
|
|
26
|
+
/** Height in local coordinates, or the decoded media height when zero. */
|
|
27
|
+
height?: ResType<number>;
|
|
28
|
+
/** Requested media decode width; decode-time scaling depends on the format. */
|
|
29
|
+
loadWidth?: ResType<number>;
|
|
30
|
+
/** Requested media decode height; decode-time scaling depends on the format. */
|
|
31
|
+
loadHeight?: ResType<number>;
|
|
32
|
+
/** Scales decoded media within this node's dimensions. */
|
|
33
|
+
loadDisplayMode?: LoadDisplayMode;
|
|
34
|
+
/**
|
|
35
|
+
* MIME type used to select the decoder. Optional: when omitted, the firmware
|
|
36
|
+
* detects the format from the file header, so prefer leaving it unset over
|
|
37
|
+
* guessing. A wrong value is a hard decode error.
|
|
38
|
+
*/
|
|
39
|
+
mimeType?: AnimatedImageMimeType;
|
|
40
|
+
/** Playback command. `"stop"` is a reported state, not a command. */
|
|
41
|
+
control?: AnimatedImageControl;
|
|
42
|
+
/** Bitmap URI shown while media is downloading or initializing. */
|
|
43
|
+
loadingBitmapUri?: Uri;
|
|
44
|
+
/** Opacity for `loadingBitmapUri`. */
|
|
45
|
+
loadingBitmapOpacity?: number;
|
|
46
|
+
/** Bitmap URI shown when decoding fails. */
|
|
47
|
+
failedBitmapUri?: Uri;
|
|
48
|
+
/** Opacity for `failedBitmapUri`. */
|
|
49
|
+
failedBitmapOpacity?: number;
|
|
50
|
+
/** Text announced by the audio guide when this node has focus. */
|
|
51
|
+
audioGuideText?: string;
|
|
52
|
+
/** Fired when the read-only playback state changes. */
|
|
53
|
+
onState?: EventHandler<AnimatedImageState>;
|
|
54
|
+
/** Fired when the read-only error text changes. */
|
|
55
|
+
onError?: EventHandler<string>;
|
|
56
|
+
/** Fired when the decoded media width changes. */
|
|
57
|
+
onMediaWidth?: EventHandler<number>;
|
|
58
|
+
/** Fired when the decoded media height changes. */
|
|
59
|
+
onMediaHeight?: EventHandler<number>;
|
|
60
|
+
/**
|
|
61
|
+
* Media URI. Direct intrinsic users must set this after configuration and handlers.
|
|
62
|
+
*/
|
|
63
|
+
uri?: Uri;
|
|
64
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { IAnimatedImageProps } from "./animated-image.props";
|
|
5
|
+
import { Component, splitProps } from "./types";
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
AnimatedImageControl,
|
|
9
|
+
AnimatedImageMimeType,
|
|
10
|
+
AnimatedImageState,
|
|
11
|
+
IAnimatedImageProps
|
|
12
|
+
} from "./animated-image.props";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Thin binding for the native AnimatedImage node.
|
|
16
|
+
*
|
|
17
|
+
* Native qualification is pending; observe `onState` and `onError` for decode results.
|
|
18
|
+
* Installs configuration and handlers before `uri`, which starts loading.
|
|
19
|
+
*/
|
|
20
|
+
export const AnimatedImage: Component<IAnimatedImageProps> = props => {
|
|
21
|
+
const [local, rest] = splitProps(props, ["uri"]);
|
|
22
|
+
return <animatedImage {...rest} uri={local.uri}></animatedImage>;
|
|
23
|
+
};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { ComponentRef } from "@roku-sdk/interfaces";
|
|
5
|
+
|
|
6
|
+
import type { INodeProps } from "./node";
|
|
7
|
+
import type { EventHandler, Vector2D } from "./types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Interpolator functions available for Animation easing.
|
|
11
|
+
*
|
|
12
|
+
* Controls how the animation progresses over time. Functions are divided into
|
|
13
|
+
* ease-in (beginning) and ease-out (end) portions. The `piecewise` option allows
|
|
14
|
+
* custom control over the ease-in/ease-out split via `easeInPercent` and `easeOutPercent`.
|
|
15
|
+
*
|
|
16
|
+
* @see {@link https://developer.roku.com/dev/docs/animation}
|
|
17
|
+
*/
|
|
18
|
+
export type EaseFunction =
|
|
19
|
+
| "linear"
|
|
20
|
+
| "inQuad"
|
|
21
|
+
| "inCubic"
|
|
22
|
+
| "inQuartic"
|
|
23
|
+
| "inQuintic"
|
|
24
|
+
| "inExpo"
|
|
25
|
+
| "outQuad"
|
|
26
|
+
| "outCubic"
|
|
27
|
+
| "outQuartic"
|
|
28
|
+
| "outQuintic"
|
|
29
|
+
| "outExpo"
|
|
30
|
+
| "inOutQuad"
|
|
31
|
+
| "inOutCubic"
|
|
32
|
+
| "inOutQuartic"
|
|
33
|
+
| "inOutQuintic"
|
|
34
|
+
| "inOutExpo"
|
|
35
|
+
| "piecewise";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Animation control commands.
|
|
39
|
+
*
|
|
40
|
+
* - `none`: Initial state with no associated action.
|
|
41
|
+
* - `start`: Always plays the animation from the beginning.
|
|
42
|
+
* - `stop`: Stops the animation in its current state.
|
|
43
|
+
* - `pause`: Pauses the animation in its current state.
|
|
44
|
+
* - `resume`: If paused, resumes from the current state; otherwise plays from the beginning.
|
|
45
|
+
* - `finish`: Jumps to the end of the animation, setting all fields to final values.
|
|
46
|
+
*
|
|
47
|
+
* @see {@link https://developer.roku.com/dev/docs/animationbase}
|
|
48
|
+
*/
|
|
49
|
+
export type AnimationControl = "none" | "start" | "stop" | "pause" | "resume" | "finish";
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Animation state values reported by the `state` field.
|
|
53
|
+
*
|
|
54
|
+
* @see {@link https://developer.roku.com/dev/docs/animationbase}
|
|
55
|
+
*/
|
|
56
|
+
export type AnimationState = "running" | "paused" | "stopped";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Properties shared by Animation, SequentialAnimation, and ParallelAnimation.
|
|
60
|
+
*
|
|
61
|
+
* AnimationBase provides basic functionality for animating screen elements such as
|
|
62
|
+
* moving them across the display, fading them in and out, or changing their color.
|
|
63
|
+
*
|
|
64
|
+
* @see {@link https://developer.roku.com/dev/docs/animationbase}
|
|
65
|
+
*/
|
|
66
|
+
export interface IAnimationBaseProps extends INodeProps {
|
|
67
|
+
/** Controls the animation. @default "none" */
|
|
68
|
+
control?: AnimationControl;
|
|
69
|
+
/** Controls whether the animation stops when it finishes (false) or repeats from the beginning (true). @default false */
|
|
70
|
+
repeat?: boolean;
|
|
71
|
+
/** Delays the start of the animation by the specified number of seconds. @default 0 */
|
|
72
|
+
delay?: number;
|
|
73
|
+
/** Fired when the animation state changes. Values: "running", "paused", "stopped". */
|
|
74
|
+
onState?: EventHandler<AnimationState>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Properties for the Animation node.
|
|
79
|
+
*
|
|
80
|
+
* Animation provides animations of renderable nodes by applying interpolator functions
|
|
81
|
+
* to the values in specified renderable node fields. Requires child field interpolator nodes
|
|
82
|
+
* (FloatFieldInterpolator, Vector2DFieldInterpolator, ColorFieldInterpolator) for each
|
|
83
|
+
* animated field.
|
|
84
|
+
*
|
|
85
|
+
* @see {@link https://developer.roku.com/dev/docs/animation}
|
|
86
|
+
*/
|
|
87
|
+
export interface IAnimationProps extends IAnimationBaseProps {
|
|
88
|
+
/** Duration of the animation in seconds. @default 0 */
|
|
89
|
+
duration?: number;
|
|
90
|
+
/** Interpolator function used for the animation. @default "outCubic" */
|
|
91
|
+
easeFunction?: EaseFunction;
|
|
92
|
+
/**
|
|
93
|
+
* If easeFunction is set to "piecewise", sets the percentage of the animation duration
|
|
94
|
+
* during which ease-in is applied. The sum of easeInPercent and easeOutPercent must be <= 1.
|
|
95
|
+
* Ignored for all other easeFunction values.
|
|
96
|
+
* @default 0.5
|
|
97
|
+
*/
|
|
98
|
+
easeInPercent?: number;
|
|
99
|
+
/**
|
|
100
|
+
* If easeFunction is set to "piecewise", sets the percentage of the animation duration
|
|
101
|
+
* during which ease-out is applied. The sum of easeInPercent and easeOutPercent must be <= 1.
|
|
102
|
+
* Ignored for all other easeFunction values.
|
|
103
|
+
* @default 0.5
|
|
104
|
+
*/
|
|
105
|
+
easeOutPercent?: number;
|
|
106
|
+
/**
|
|
107
|
+
* Set to true to skip animations on lower-performing Roku devices. When skipped,
|
|
108
|
+
* setting control to "start" causes state to transition running -> finished immediately,
|
|
109
|
+
* allowing any state observers to be properly called.
|
|
110
|
+
* @default false
|
|
111
|
+
*/
|
|
112
|
+
optional?: boolean;
|
|
113
|
+
/** Fired when the willBeSkipped status changes. Indicates whether the animation will be skipped (rendered in its final state). */
|
|
114
|
+
onWillBeSkipped?: EventHandler<boolean>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Properties shared by all field interpolator nodes.
|
|
119
|
+
*
|
|
120
|
+
* Field interpolators define keyframe animation sequences that are applied to
|
|
121
|
+
* specific fields of SceneGraph nodes. They are used as children of Animation nodes.
|
|
122
|
+
*
|
|
123
|
+
* @see {@link https://developer.roku.com/dev/docs/floatfieldinterpolator}
|
|
124
|
+
*/
|
|
125
|
+
export interface IFieldInterpolatorPropsBase extends INodeProps {
|
|
126
|
+
/**
|
|
127
|
+
* Specifies the field to interpolate. The string should contain the ID of a node and
|
|
128
|
+
* the name of a field, separated by a dot (e.g. "myPoster.opacity").
|
|
129
|
+
* @default ""
|
|
130
|
+
*/
|
|
131
|
+
fieldToInterp?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Keyframe positions as an array of floats from 0 to 1 indicating the percentage of
|
|
134
|
+
* the animation where the corresponding keyValue should occur. Must match the length of keyValue.
|
|
135
|
+
* @default []
|
|
136
|
+
*/
|
|
137
|
+
key?: number[];
|
|
138
|
+
/**
|
|
139
|
+
* Specifies the current interpolation progress (0 to 1) used to compute the field value.
|
|
140
|
+
* Typically driven by a parent Animation node.
|
|
141
|
+
* @default 0.0
|
|
142
|
+
*/
|
|
143
|
+
fraction?: number;
|
|
144
|
+
/** Enables animation to be played in reverse. @default false */
|
|
145
|
+
reverse?: boolean;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Properties for the FloatFieldInterpolator node.
|
|
150
|
+
*
|
|
151
|
+
* Specifies a keyframe animation sequence to be applied to a floating point field
|
|
152
|
+
* of a node (such as opacity, width, or height).
|
|
153
|
+
*
|
|
154
|
+
* @see {@link https://developer.roku.com/dev/docs/floatfieldinterpolator}
|
|
155
|
+
*/
|
|
156
|
+
export interface IFloatFieldInterpolatorProps extends IFieldInterpolatorPropsBase {
|
|
157
|
+
/**
|
|
158
|
+
* Float values at each keyframe. Each value corresponds to a value in the key array.
|
|
159
|
+
* Must match the length of key.
|
|
160
|
+
* @default []
|
|
161
|
+
*/
|
|
162
|
+
keyValue?: number[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Properties for the ColorFieldInterpolator node.
|
|
167
|
+
*
|
|
168
|
+
* Specifies a keyframe animation sequence to be applied to a color field of a node.
|
|
169
|
+
* Interpolation is performed in HSV color space for visually pleasing results.
|
|
170
|
+
*
|
|
171
|
+
* @see {@link https://developer.roku.com/dev/docs/colorfieldinterpolator}
|
|
172
|
+
*/
|
|
173
|
+
export interface IColorFieldInterpolatorProps extends IFieldInterpolatorPropsBase {
|
|
174
|
+
/**
|
|
175
|
+
* Color values at each keyframe (e.g. "0xFF0000FF" for red). Each value corresponds
|
|
176
|
+
* to a value in the key array. Must match the length of key.
|
|
177
|
+
* @default []
|
|
178
|
+
*/
|
|
179
|
+
keyValue?: string[];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Properties for the Vector2DFieldInterpolator node.
|
|
184
|
+
*
|
|
185
|
+
* Specifies a keyframe animation sequence to be applied to a Vector2D field of a node,
|
|
186
|
+
* most commonly used to animate translation coordinates.
|
|
187
|
+
*
|
|
188
|
+
* @see {@link https://developer.roku.com/dev/docs/vector2dfieldinterpolator}
|
|
189
|
+
*/
|
|
190
|
+
export interface IVector2DFieldInterpolatorProps extends IFieldInterpolatorPropsBase {
|
|
191
|
+
/**
|
|
192
|
+
* Vector2D values at each keyframe. Each value corresponds to a value in the key array.
|
|
193
|
+
* Must match the length of key.
|
|
194
|
+
* @default []
|
|
195
|
+
*/
|
|
196
|
+
keyValue?: Vector2D[];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Handle for imperative control of an {@link Animation} or {@link SequentialAnimation}
|
|
201
|
+
* component, obtained via the `ref` prop.
|
|
202
|
+
*
|
|
203
|
+
* Extends {@link ComponentRef} with convenience methods that map to
|
|
204
|
+
* {@link AnimationControl} values, so you can drive animation playback without
|
|
205
|
+
* manually setting the `control` prop.
|
|
206
|
+
*
|
|
207
|
+
* Uses {@link IAnimationBaseProps} as the base so it works with both
|
|
208
|
+
* `Animation` and `SequentialAnimation`.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```tsx
|
|
212
|
+
* import { Animation, AnimationRef, FloatFieldInterpolator, Button } from "@roku-sdk/components";
|
|
213
|
+
*
|
|
214
|
+
* const FadeDemo = () => {
|
|
215
|
+
* let animRef!: AnimationRef;
|
|
216
|
+
*
|
|
217
|
+
* return (
|
|
218
|
+
* <>
|
|
219
|
+
* <Button text="Start" onButtonSelected={() => animRef.start()} />
|
|
220
|
+
* <Button text="Pause" onButtonSelected={() => animRef.pause()} />
|
|
221
|
+
* <Animation ref={animRef} duration={2} easeFunction="linear">
|
|
222
|
+
* <FloatFieldInterpolator
|
|
223
|
+
* key={[0, 1]}
|
|
224
|
+
* keyValue={[1, 0]}
|
|
225
|
+
* fieldToInterp="myPoster.opacity"
|
|
226
|
+
* />
|
|
227
|
+
* </Animation>
|
|
228
|
+
* </>
|
|
229
|
+
* );
|
|
230
|
+
* };
|
|
231
|
+
* ```
|
|
232
|
+
*
|
|
233
|
+
* @see {@link https://developer.roku.com/dev/docs/animationbase}
|
|
234
|
+
*/
|
|
235
|
+
export interface AnimationRef extends ComponentRef<IAnimationBaseProps> {
|
|
236
|
+
start: () => void;
|
|
237
|
+
stop: () => void;
|
|
238
|
+
pause: () => void;
|
|
239
|
+
resume: () => void;
|
|
240
|
+
finish: () => void;
|
|
241
|
+
/**
|
|
242
|
+
* Writes `duration` immediately instead of through the reactive `duration` prop.
|
|
243
|
+
*
|
|
244
|
+
* Re-targeting a running animation requires the new duration to reach the node
|
|
245
|
+
* before `stop()`. A reactive prop write is deferred to after the current effect
|
|
246
|
+
* body, so it would land after the following `start()` and the animation would
|
|
247
|
+
* run its first frames against the previous duration.
|
|
248
|
+
*/
|
|
249
|
+
setDuration: (duration: number) => void;
|
|
250
|
+
/** Writes `easeFunction` immediately. See {@link AnimationRef.setDuration}. */
|
|
251
|
+
setEaseFunction: (easeFunction: EaseFunction) => void;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Handle for imperative control of a {@link FloatFieldInterpolator}, obtained via the `ref` prop.
|
|
256
|
+
*
|
|
257
|
+
* `setKeyValue` writes `keyValue` immediately instead of through the reactive prop.
|
|
258
|
+
* Re-targeting an animation requires `stop`, the new `keyValue`, and `start` to land
|
|
259
|
+
* in that order; a deferred prop write reorders it to `stop`, `start`, `keyValue`,
|
|
260
|
+
* leaving the restarted animation interpolating toward the previous target.
|
|
261
|
+
*/
|
|
262
|
+
export interface FloatFieldInterpolatorRef extends ComponentRef<IFloatFieldInterpolatorProps> {
|
|
263
|
+
setKeyValue: (keyValue: number[]) => void;
|
|
264
|
+
/**
|
|
265
|
+
* Writes `fieldToInterp` immediately. Needed when the interpolator is re-pointed at a
|
|
266
|
+
* new node in the same batch that starts the animation, since a zero-duration start
|
|
267
|
+
* applies the final `keyValue` to whichever node `fieldToInterp` names at that moment.
|
|
268
|
+
*/
|
|
269
|
+
setFieldToInterp: (fieldToInterp: string) => void;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Handle for imperative control of a {@link ColorFieldInterpolator}, obtained via the `ref` prop.
|
|
274
|
+
*
|
|
275
|
+
* See {@link FloatFieldInterpolatorRef} for why `setKeyValue` bypasses the reactive prop.
|
|
276
|
+
*/
|
|
277
|
+
export interface ColorFieldInterpolatorRef extends ComponentRef<IColorFieldInterpolatorProps> {
|
|
278
|
+
setKeyValue: (keyValue: string[]) => void;
|
|
279
|
+
/** Writes `fieldToInterp` immediately. See {@link FloatFieldInterpolatorRef.setFieldToInterp}. */
|
|
280
|
+
setFieldToInterp: (fieldToInterp: string) => void;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Handle for imperative control of a {@link Vector2DFieldInterpolator}, obtained via the `ref` prop.
|
|
285
|
+
*
|
|
286
|
+
* See {@link FloatFieldInterpolatorRef} for why `setKeyValue` bypasses the reactive prop.
|
|
287
|
+
*/
|
|
288
|
+
export interface Vector2DFieldInterpolatorRef extends ComponentRef<IVector2DFieldInterpolatorProps> {
|
|
289
|
+
setKeyValue: (keyValue: Vector2D[]) => void;
|
|
290
|
+
/** Writes `fieldToInterp` immediately. See {@link FloatFieldInterpolatorRef.setFieldToInterp}. */
|
|
291
|
+
setFieldToInterp: (fieldToInterp: string) => void;
|
|
292
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import { registerImperativeMethods, setPropMethod } from "@roku-sdk/solid";
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
IAnimationBaseProps,
|
|
8
|
+
IAnimationProps,
|
|
9
|
+
IColorFieldInterpolatorProps,
|
|
10
|
+
IFloatFieldInterpolatorProps,
|
|
11
|
+
IVector2DFieldInterpolatorProps
|
|
12
|
+
} from "./animation.props";
|
|
13
|
+
export type {
|
|
14
|
+
EaseFunction,
|
|
15
|
+
AnimationControl,
|
|
16
|
+
AnimationState,
|
|
17
|
+
IAnimationBaseProps,
|
|
18
|
+
IAnimationProps,
|
|
19
|
+
IFieldInterpolatorPropsBase,
|
|
20
|
+
IFloatFieldInterpolatorProps,
|
|
21
|
+
IColorFieldInterpolatorProps,
|
|
22
|
+
IVector2DFieldInterpolatorProps,
|
|
23
|
+
AnimationRef,
|
|
24
|
+
FloatFieldInterpolatorRef,
|
|
25
|
+
ColorFieldInterpolatorRef,
|
|
26
|
+
Vector2DFieldInterpolatorRef
|
|
27
|
+
} from "./animation.props";
|
|
28
|
+
|
|
29
|
+
import { Component } from "./types";
|
|
30
|
+
|
|
31
|
+
const animationMethods = {
|
|
32
|
+
start: setPropMethod("control", "start"),
|
|
33
|
+
stop: setPropMethod("control", "stop"),
|
|
34
|
+
pause: setPropMethod("control", "pause"),
|
|
35
|
+
resume: setPropMethod("control", "resume"),
|
|
36
|
+
finish: setPropMethod("control", "finish"),
|
|
37
|
+
setDuration: setPropMethod("duration"),
|
|
38
|
+
setEaseFunction: setPropMethod("easeFunction")
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Re-targeting a running animation has to deliver duration, easeFunction and keyValue
|
|
42
|
+
// alongside the surrounding stop/start in a single batch, in that order. Reactive prop
|
|
43
|
+
// writes are deferred to after the current effect body, which reorders them after the
|
|
44
|
+
// trailing start.
|
|
45
|
+
//
|
|
46
|
+
// `fieldToInterp` needs the same treatment whenever the interpolator is re-pointed at a
|
|
47
|
+
// different node in the same batch that starts the animation: a zero-duration start
|
|
48
|
+
// applies the final keyValue to whatever `fieldToInterp` currently names, so a deferred
|
|
49
|
+
// write would snap the previous target instead of the new one.
|
|
50
|
+
const fieldInterpolatorMethods = {
|
|
51
|
+
setKeyValue: setPropMethod("keyValue"),
|
|
52
|
+
setFieldToInterp: setPropMethod("fieldToInterp")
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
registerImperativeMethods("animation", animationMethods);
|
|
56
|
+
registerImperativeMethods("sequentialAnimation", animationMethods);
|
|
57
|
+
registerImperativeMethods("floatFieldInterpolator", fieldInterpolatorMethods);
|
|
58
|
+
registerImperativeMethods("colorFieldInterpolator", fieldInterpolatorMethods);
|
|
59
|
+
registerImperativeMethods("vector2dFieldInterpolator", fieldInterpolatorMethods);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Animation component for animating renderable node fields using interpolator functions.
|
|
63
|
+
*
|
|
64
|
+
* Requires child field interpolator nodes (FloatFieldInterpolator, Vector2DFieldInterpolator,
|
|
65
|
+
* ColorFieldInterpolator) to define which fields to animate. Supports various easing functions
|
|
66
|
+
* to control the animation's speed curve.
|
|
67
|
+
*
|
|
68
|
+
* Playback can be controlled declaratively via the `control` prop or imperatively
|
|
69
|
+
* via an {@link AnimationRef} obtained through the `ref` prop.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```tsx
|
|
73
|
+
* <animation duration={1} repeat={true} easeFunction="linear" control="start">
|
|
74
|
+
* <vector2dFieldInterpolator
|
|
75
|
+
* key={[0.0, 0.5, 1.0]}
|
|
76
|
+
* keyValue={[[0, 0], [500, 0], [500, 200]]}
|
|
77
|
+
* fieldToInterp="myPoster.translation"
|
|
78
|
+
* />
|
|
79
|
+
* </animation>
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @see {@link https://developer.roku.com/dev/docs/animation}
|
|
83
|
+
*/
|
|
84
|
+
export const Animation: Component<IAnimationProps> = props => {
|
|
85
|
+
return <animation {...props}></animation>;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* FloatFieldInterpolator animates a floating point field of a node through a series of keyframes.
|
|
90
|
+
*
|
|
91
|
+
* Typically used as a child of an Animation node to animate fields like opacity, width, or height.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* <animation duration={10} repeat={true} control="start" easeFunction="linear">
|
|
96
|
+
* <floatFieldInterpolator
|
|
97
|
+
* key={[0.0, 0.5, 1.0]}
|
|
98
|
+
* keyValue={[1.0, 0.0, 1.0]}
|
|
99
|
+
* fieldToInterp="myPoster.opacity"
|
|
100
|
+
* />
|
|
101
|
+
* </animation>
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @see {@link https://developer.roku.com/dev/docs/floatfieldinterpolator}
|
|
105
|
+
*/
|
|
106
|
+
export const FloatFieldInterpolator: Component<IFloatFieldInterpolatorProps> = props => {
|
|
107
|
+
return <floatFieldInterpolator {...props}></floatFieldInterpolator>;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* ColorFieldInterpolator animates a color field of a node through a series of keyframes.
|
|
112
|
+
*
|
|
113
|
+
* Interpolation is performed in HSV color space for visually pleasing transitions.
|
|
114
|
+
* Typically used as a child of an Animation node.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```tsx
|
|
118
|
+
* <animation duration={10} repeat={true} control="start" easeFunction="linear">
|
|
119
|
+
* <colorFieldInterpolator
|
|
120
|
+
* key={[0.0, 0.5, 1.0]}
|
|
121
|
+
* keyValue={["0x0000CCFF", "0x00000FFF", "0x0000CCFF"]}
|
|
122
|
+
* fieldToInterp="myRectangle.color"
|
|
123
|
+
* />
|
|
124
|
+
* </animation>
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* @see {@link https://developer.roku.com/dev/docs/colorfieldinterpolator}
|
|
128
|
+
*/
|
|
129
|
+
export const ColorFieldInterpolator: Component<IColorFieldInterpolatorProps> = props => {
|
|
130
|
+
return <colorFieldInterpolator {...props}></colorFieldInterpolator>;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Vector2DFieldInterpolator animates a Vector2D field of a node through a series of keyframes.
|
|
135
|
+
*
|
|
136
|
+
* Most commonly used to animate translation coordinates for moving nodes across the screen.
|
|
137
|
+
* Typically used as a child of an Animation node.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```tsx
|
|
141
|
+
* <animation duration={10} repeat={true} control="start" easeFunction="linear">
|
|
142
|
+
* <vector2dFieldInterpolator
|
|
143
|
+
* key={[0.0, 0.5, 1.0]}
|
|
144
|
+
* keyValue={[[318, 8], [656, 8], [318, 8]]}
|
|
145
|
+
* fieldToInterp="myLabel.translation"
|
|
146
|
+
* />
|
|
147
|
+
* </animation>
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @see {@link https://developer.roku.com/dev/docs/vector2dfieldinterpolator}
|
|
151
|
+
*/
|
|
152
|
+
export const Vector2DFieldInterpolator: Component<IVector2DFieldInterpolatorProps> = props => {
|
|
153
|
+
return <vector2dFieldInterpolator {...props}></vector2dFieldInterpolator>;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* SequentialAnimation runs a set of child animations one after another.
|
|
158
|
+
*
|
|
159
|
+
* The state field is set to "running" while any child animation is in progress.
|
|
160
|
+
* Once all children have completed, the state becomes "stopped". Child animation
|
|
161
|
+
* `delay` fields can be used to add gaps between sequential animations.
|
|
162
|
+
*
|
|
163
|
+
* Playback can be controlled declaratively via the `control` prop or imperatively
|
|
164
|
+
* via an {@link AnimationRef} obtained through the `ref` prop.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```tsx
|
|
168
|
+
* <sequentialAnimation repeat={true} control="start">
|
|
169
|
+
* <animation duration={2} easeFunction="linear">
|
|
170
|
+
* <floatFieldInterpolator
|
|
171
|
+
* key={[0, 0.5, 1]}
|
|
172
|
+
* keyValue={[0.2, 1, 0.2]}
|
|
173
|
+
* fieldToInterp="rect1.opacity"
|
|
174
|
+
* />
|
|
175
|
+
* </animation>
|
|
176
|
+
* <animation duration={2} easeFunction="linear">
|
|
177
|
+
* <floatFieldInterpolator
|
|
178
|
+
* key={[0, 0.5, 1]}
|
|
179
|
+
* keyValue={[0.2, 1, 0.2]}
|
|
180
|
+
* fieldToInterp="rect2.opacity"
|
|
181
|
+
* />
|
|
182
|
+
* </animation>
|
|
183
|
+
* </sequentialAnimation>
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @see {@link https://developer.roku.com/dev/docs/sequentialanimation}
|
|
187
|
+
*/
|
|
188
|
+
export const SequentialAnimation: Component<IAnimationBaseProps> = props => {
|
|
189
|
+
return <sequentialAnimation {...props}></sequentialAnimation>;
|
|
190
|
+
};
|