@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
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { ComponentRef } from "@roku-sdk/interfaces";
|
|
5
|
+
|
|
6
|
+
import type { MediaControl, MediaState } from "../types";
|
|
7
|
+
import type { IGroupProps } from "./group.props";
|
|
8
|
+
import type { EventHandler, JSX } from "./types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Buffering progress information from the Audio node.
|
|
12
|
+
*
|
|
13
|
+
* Valid only while buffering is in progress, both at stream startup or when
|
|
14
|
+
* re-buffering is required.
|
|
15
|
+
*
|
|
16
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
17
|
+
*/
|
|
18
|
+
export interface IAudioBufferingStatus {
|
|
19
|
+
/** Percent buffering complete as an integer. */
|
|
20
|
+
buffering: number;
|
|
21
|
+
/** Percent buffering complete as an integer. */
|
|
22
|
+
percent: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Alias for {@link MediaControl} used by the Audio node `control` field. */
|
|
26
|
+
export type AudioControl = MediaControl;
|
|
27
|
+
|
|
28
|
+
/** Alias for {@link MediaState} from the Audio node `state` field. */
|
|
29
|
+
export type AudioState = MediaState;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Audio codec format reported by the Audio node `audioFormat` field.
|
|
33
|
+
*
|
|
34
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
35
|
+
*/
|
|
36
|
+
export type AudioFormat =
|
|
37
|
+
| ""
|
|
38
|
+
| "aac"
|
|
39
|
+
| "aac_adif"
|
|
40
|
+
| "aac_adts"
|
|
41
|
+
| "aac_latm"
|
|
42
|
+
| "ac3"
|
|
43
|
+
| "alac"
|
|
44
|
+
| "dts"
|
|
45
|
+
| "eac3"
|
|
46
|
+
| "flac"
|
|
47
|
+
| "mp2"
|
|
48
|
+
| "mp3"
|
|
49
|
+
| "none"
|
|
50
|
+
| "pcm"
|
|
51
|
+
| "unknown"
|
|
52
|
+
| "vorbis"
|
|
53
|
+
| "wma"
|
|
54
|
+
| "wmapro";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* HDR mode reported in `streamingSegment` data.
|
|
58
|
+
*
|
|
59
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
60
|
+
*/
|
|
61
|
+
export type HDRmode = "invalid" | "unknown" | "none" | "hdr10" | "dolby_vision" | "hlg10" | "sl-hdr2";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Information about the audio segment currently streaming (HLS/DASH).
|
|
65
|
+
*
|
|
66
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
67
|
+
*/
|
|
68
|
+
export interface IAudioStreamingSegment {
|
|
69
|
+
/** Bitrate of the segment in bits per second. */
|
|
70
|
+
segBitrateBps: number;
|
|
71
|
+
/** The sequence number of the segment in the audio. */
|
|
72
|
+
segSequence: number;
|
|
73
|
+
/** The start time of the segment from the start of the audio, in seconds. */
|
|
74
|
+
segStart: number;
|
|
75
|
+
/** URL of the segment. */
|
|
76
|
+
segUrl: string;
|
|
77
|
+
/** The type of data in the segment: "unknown", "mux", "audio", "video", or "captions". */
|
|
78
|
+
segTypeStr: string;
|
|
79
|
+
/** The HDR format of the content. */
|
|
80
|
+
hdrModeStr: HDRmode;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Information about the audio stream currently playing or buffering.
|
|
85
|
+
*
|
|
86
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
87
|
+
*/
|
|
88
|
+
export interface IAudioStreamInfo {
|
|
89
|
+
/** If true, the stream was downloaded due to an underrun. */
|
|
90
|
+
isUnderrun: boolean;
|
|
91
|
+
/** If true, playback was resumed after trickplay. */
|
|
92
|
+
isResumed: boolean;
|
|
93
|
+
/** The measured bitrate (bps) of the network when the stream was selected. */
|
|
94
|
+
measuredBitrate: number;
|
|
95
|
+
/** The bitrate of the stream. */
|
|
96
|
+
streamBitrate: number;
|
|
97
|
+
/** The URL of the stream. */
|
|
98
|
+
streamUrl: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Properties for the Audio node.
|
|
103
|
+
*
|
|
104
|
+
* The Audio node plays streaming audio. It has no built-in visual UI, but you can build
|
|
105
|
+
* your own UI for the node, including trick play, or showing an album cover or similar
|
|
106
|
+
* graphical image for each song selected by a user.
|
|
107
|
+
*
|
|
108
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
109
|
+
*/
|
|
110
|
+
export interface IAudioProps extends IGroupProps {
|
|
111
|
+
/**
|
|
112
|
+
* The ContentNode with the content metadata for the audio or audio playlist to be played.
|
|
113
|
+
* If an audio playlist is to be played, the ContentNode must include complete child
|
|
114
|
+
* ContentNodes for each audio in the playlist.
|
|
115
|
+
* @default null
|
|
116
|
+
*/
|
|
117
|
+
content?: JSX.Element;
|
|
118
|
+
/**
|
|
119
|
+
* When true, enables audio playlists. The ContentNode set in `content` must have children
|
|
120
|
+
* ContentNodes for each audio in the playlist. All audios will be played in sequence.
|
|
121
|
+
* @default false
|
|
122
|
+
*/
|
|
123
|
+
contentIsPlaylist?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Sets the index of the next audio in the playlist to be played. Takes effect when the
|
|
126
|
+
* current audio is completed or skipped. Resets to -1 after the specified audio begins.
|
|
127
|
+
* @default -1
|
|
128
|
+
*/
|
|
129
|
+
nextContentIndex?: number;
|
|
130
|
+
/**
|
|
131
|
+
* When true, the audio or audio playlist will be restarted from the beginning after
|
|
132
|
+
* the end is reached.
|
|
133
|
+
* @default false
|
|
134
|
+
*/
|
|
135
|
+
loop?: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Fired when buffering progress or status changes. Valid only while buffering is in
|
|
138
|
+
* progress, both at stream startup or when re-buffering is required.
|
|
139
|
+
*/
|
|
140
|
+
onBufferingStatus?: EventHandler<IAudioBufferingStatus>;
|
|
141
|
+
/**
|
|
142
|
+
* Sets the desired play state for the audio (e.g. "play", "pause", "stop", "prebuffer").
|
|
143
|
+
* To dynamically monitor the actual state, observe the `onState` event.
|
|
144
|
+
* @default "none"
|
|
145
|
+
*/
|
|
146
|
+
control?: AudioControl;
|
|
147
|
+
/**
|
|
148
|
+
* The interval between position event notifications, in seconds.
|
|
149
|
+
* If 0, no notifications are delivered.
|
|
150
|
+
* @default 0.5
|
|
151
|
+
*/
|
|
152
|
+
notificationInterval?: number;
|
|
153
|
+
/**
|
|
154
|
+
* If the audio stream contains timed metadata such as ID3 tags, any metadata with a
|
|
155
|
+
* key matching an entry in this array will be reported via `onTimedMetaData`. If any
|
|
156
|
+
* entry is "*", all timed metadata will be selected.
|
|
157
|
+
* @default []
|
|
158
|
+
*/
|
|
159
|
+
timedMetaDataSelectionKeys?: string[];
|
|
160
|
+
/**
|
|
161
|
+
* Sets the current position in the audio in seconds from the beginning of the stream (write-only).
|
|
162
|
+
*/
|
|
163
|
+
seek?: number;
|
|
164
|
+
/**
|
|
165
|
+
* Fired when the current playlist content index changes. Set to 0 for single audio
|
|
166
|
+
* when playlists are not enabled.
|
|
167
|
+
*/
|
|
168
|
+
onContentIndex?: EventHandler<number>;
|
|
169
|
+
/**
|
|
170
|
+
* Fired when timed metadata (e.g. ID3 tags) matching {@link IAudioProps.timedMetaDataSelectionKeys}
|
|
171
|
+
* is decoded from the audio stream.
|
|
172
|
+
*/
|
|
173
|
+
onTimedMetaData?: EventHandler<{ [key: string]: unknown }>;
|
|
174
|
+
/**
|
|
175
|
+
* Fired when the audio play state changes.
|
|
176
|
+
* Possible values: "none", "buffering", "playing", "paused", "stopped", "finished", "error".
|
|
177
|
+
*/
|
|
178
|
+
onState?: EventHandler<AudioState>;
|
|
179
|
+
/** Fired on position updates at the interval specified by {@link IAudioProps.notificationInterval}. */
|
|
180
|
+
onPosition?: EventHandler<number>;
|
|
181
|
+
/**
|
|
182
|
+
* Fired when the audio duration is known or changes.
|
|
183
|
+
* Value is in seconds; becomes valid when playback begins.
|
|
184
|
+
*/
|
|
185
|
+
onDuration?: EventHandler<number>;
|
|
186
|
+
/** Fired when an error code is set after the state changes to "error". */
|
|
187
|
+
onErrorCode?: EventHandler<number>;
|
|
188
|
+
/** Fired when an error message is set describing an audio play error. */
|
|
189
|
+
onErrorMsg?: EventHandler<string>;
|
|
190
|
+
/**
|
|
191
|
+
* Fired when the format of the currently playing audio changes.
|
|
192
|
+
* @see {@link AudioFormat}
|
|
193
|
+
*/
|
|
194
|
+
onAudioFormat?: EventHandler<AudioFormat>;
|
|
195
|
+
/**
|
|
196
|
+
* Fired with information about the audio segment currently streaming.
|
|
197
|
+
* Only meaningful for segmented transports (DASH, HLS).
|
|
198
|
+
*/
|
|
199
|
+
onStreamingSegment?: EventHandler<IAudioStreamingSegment>;
|
|
200
|
+
/**
|
|
201
|
+
* When false, disables auto-play after rebuffering following a seek.
|
|
202
|
+
* @default true
|
|
203
|
+
*/
|
|
204
|
+
autoplayAfterSeek?: boolean;
|
|
205
|
+
/**
|
|
206
|
+
* Set to true to mute the audio currently playing. Set to false to restore audio.
|
|
207
|
+
* @default false
|
|
208
|
+
*/
|
|
209
|
+
mute?: boolean;
|
|
210
|
+
/** Fired when stream info for the currently playing or buffering audio is updated. */
|
|
211
|
+
onStreamInfo?: EventHandler<IAudioStreamInfo>;
|
|
212
|
+
/**
|
|
213
|
+
* Fired when the time-to-start-streaming value is available. Value is the time in seconds
|
|
214
|
+
* from playback being started until audio actually began playing (minimum 0.001s).
|
|
215
|
+
* Only valid when state is "playing".
|
|
216
|
+
*/
|
|
217
|
+
onTimeToStartStreaming?: EventHandler<number>;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Handle for imperative control of an {@link Audio} component, obtained via the `ref` prop.
|
|
222
|
+
*
|
|
223
|
+
* Extends {@link ComponentRef} with convenience methods that map to
|
|
224
|
+
* {@link AudioControl} values, so you can drive playback without manually
|
|
225
|
+
* setting the `control` prop.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```tsx
|
|
229
|
+
* import { Audio, AudioRef, Button } from "@roku-sdk/components";
|
|
230
|
+
*
|
|
231
|
+
* const MusicPlayer = () => {
|
|
232
|
+
* let audioRef!: AudioRef;
|
|
233
|
+
*
|
|
234
|
+
* return (
|
|
235
|
+
* <>
|
|
236
|
+
* <Button text="Play" onButtonSelected={() => audioRef.play()} />
|
|
237
|
+
* <Audio
|
|
238
|
+
* ref={audioRef}
|
|
239
|
+
* content={<ContentNode url="https://example.com/song.mp3" streamFormat="mp3" />}
|
|
240
|
+
* />
|
|
241
|
+
* </>
|
|
242
|
+
* );
|
|
243
|
+
* };
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
247
|
+
*/
|
|
248
|
+
export interface AudioRef extends ComponentRef<IAudioProps> {
|
|
249
|
+
/**
|
|
250
|
+
* Begin playback of the audio specified by `content`. Equivalent to setting
|
|
251
|
+
* `control="play"`. The node transitions through `buffering` to `playing`,
|
|
252
|
+
* which is observable via {@link IAudioProps.onState}.
|
|
253
|
+
*/
|
|
254
|
+
play: () => void;
|
|
255
|
+
/**
|
|
256
|
+
* Pause playback while preserving the current position. Equivalent to setting
|
|
257
|
+
* `control="pause"`. Use {@link AudioRef.resume} to continue from where it
|
|
258
|
+
* paused. Has no effect when not currently `playing`.
|
|
259
|
+
*/
|
|
260
|
+
pause: () => void;
|
|
261
|
+
/**
|
|
262
|
+
* Stop playback and release the audio stream. Equivalent to setting
|
|
263
|
+
* `control="stop"`. Position is reset; a subsequent {@link AudioRef.play}
|
|
264
|
+
* starts from the beginning.
|
|
265
|
+
*/
|
|
266
|
+
stop: () => void;
|
|
267
|
+
/**
|
|
268
|
+
* Resume playback after a {@link AudioRef.pause}. Equivalent to setting
|
|
269
|
+
* `control="resume"`. Has no effect when the node is not in the `paused`
|
|
270
|
+
* state.
|
|
271
|
+
*/
|
|
272
|
+
resume: () => void;
|
|
273
|
+
/**
|
|
274
|
+
* Restart the current audio (or playlist) from the beginning. Equivalent to
|
|
275
|
+
* setting `control="replay"`.
|
|
276
|
+
*/
|
|
277
|
+
replay: () => void;
|
|
278
|
+
/**
|
|
279
|
+
* Pre-buffer the audio without starting playback. Equivalent to setting
|
|
280
|
+
* `control="prebuffer"`. Useful to reduce time-to-first-sample when playback
|
|
281
|
+
* is started later via {@link AudioRef.play}.
|
|
282
|
+
*/
|
|
283
|
+
prebuffer: () => void;
|
|
284
|
+
/**
|
|
285
|
+
* Skip to the next item in a playlist. Equivalent to setting
|
|
286
|
+
* `control="skipcontent"`. Only meaningful when
|
|
287
|
+
* {@link IAudioProps.contentIsPlaylist} is true.
|
|
288
|
+
*/
|
|
289
|
+
skipcontent: () => void;
|
|
290
|
+
/**
|
|
291
|
+
* Seek to a specific position in the current audio. Equivalent to setting
|
|
292
|
+
* the {@link IAudioProps.seek} prop.
|
|
293
|
+
*
|
|
294
|
+
* @param position Target position in seconds from the start of the stream.
|
|
295
|
+
*/
|
|
296
|
+
seekTo: (position: number) => void;
|
|
297
|
+
/**
|
|
298
|
+
* Set the index of the next audio in the playlist to be played.
|
|
299
|
+
* @param index The index of the next audio in the playlist.
|
|
300
|
+
*/
|
|
301
|
+
setNextContentIndex: (index: number) => void;
|
|
302
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import { registerImperativeMethods } from "@roku-sdk/solid";
|
|
5
|
+
|
|
6
|
+
import type { IAudioProps } from "./audio.props";
|
|
7
|
+
export type {
|
|
8
|
+
IAudioBufferingStatus,
|
|
9
|
+
AudioControl,
|
|
10
|
+
AudioState,
|
|
11
|
+
AudioFormat,
|
|
12
|
+
HDRmode,
|
|
13
|
+
IAudioStreamingSegment,
|
|
14
|
+
IAudioStreamInfo,
|
|
15
|
+
IAudioProps,
|
|
16
|
+
AudioRef
|
|
17
|
+
} from "./audio.props";
|
|
18
|
+
|
|
19
|
+
import { mediaPlaybackMethods } from "./media-playback-methods";
|
|
20
|
+
import { Component } from "./types";
|
|
21
|
+
|
|
22
|
+
registerImperativeMethods("audio", mediaPlaybackMethods);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Audio plays streaming audio content. It has no built-in visual UI, but you can build your
|
|
26
|
+
* own UI including trick play, album art, or any graphical elements for user interaction.
|
|
27
|
+
*
|
|
28
|
+
* Audio playlists are supported by setting `contentIsPlaylist` to true and providing a
|
|
29
|
+
* ContentNode with child ContentNodes for each audio in the playlist.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <Audio
|
|
34
|
+
* content={<contentNode url="https://example.com/song.mp3" streamFormat="mp3" />}
|
|
35
|
+
* control="play"
|
|
36
|
+
* onState={(state) => console.log("Audio state:", state)}
|
|
37
|
+
* onPosition={(pos) => console.log("Position:", pos)}
|
|
38
|
+
* />
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* // Audio playlist
|
|
44
|
+
* <Audio
|
|
45
|
+
* content={playlist}
|
|
46
|
+
* contentIsPlaylist={true}
|
|
47
|
+
* loop={true}
|
|
48
|
+
* control="play"
|
|
49
|
+
* onContentIndex={(idx) => console.log("Now playing track:", idx)}
|
|
50
|
+
* />
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @see {@link https://developer.roku.com/dev/docs/audio}
|
|
54
|
+
*/
|
|
55
|
+
export const Audio: Component<IAudioProps> = props => {
|
|
56
|
+
return <audio {...props}>{props.content}</audio>;
|
|
57
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { IGroupProps } from "./group.props";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Properties for the BusySpinner node.
|
|
8
|
+
*
|
|
9
|
+
* BusySpinner is a widget that displays a continuously rotating bitmap to indicate loading
|
|
10
|
+
* or processing state. It uses an internal Poster node instance to display the spinner bitmap.
|
|
11
|
+
*
|
|
12
|
+
* **Note:** Not all Roku Player hardware versions support arbitrary rotations. Some hardware
|
|
13
|
+
* versions only support 90-degree rotation increments. In those cases, the icon will step through
|
|
14
|
+
* 90°, 180°, 270°, and back to 0° rotations rather than spin smoothly.
|
|
15
|
+
*
|
|
16
|
+
* @extends Group
|
|
17
|
+
* @see {@link https://developer.roku.com/dev/docs/busyspinner}
|
|
18
|
+
*/
|
|
19
|
+
export interface IBusySpinnerProps extends IGroupProps {
|
|
20
|
+
/**
|
|
21
|
+
* Sets the URI field of the internal Poster node to select the bitmap for the busy spinner.
|
|
22
|
+
* The Poster node instance is created internally and can be accessed to set the spinner image.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Using a theme resource
|
|
26
|
+
* uri="theme:ActionHudSpinner"
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Using a package resource
|
|
30
|
+
* uri="pkg:/images/spinner.png"
|
|
31
|
+
*/
|
|
32
|
+
uri?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sets the operational state of the busy spinner.
|
|
36
|
+
*
|
|
37
|
+
* - `"none"`: No operational state set. The busy spinner will run if not set to "stop"
|
|
38
|
+
* - `"start"`: Starts the busy spinner if not running
|
|
39
|
+
* - `"stop"`: Stops the busy spinner if running
|
|
40
|
+
*
|
|
41
|
+
* @default "none"
|
|
42
|
+
*/
|
|
43
|
+
control?: "none" | "start" | "stop";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Specifies whether the bitmap rotates in a clockwise or counterclockwise direction.
|
|
47
|
+
*
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
clockwise?: boolean;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The number of seconds to complete a 360-degree rotation of the spinner image.
|
|
54
|
+
* A value of 0 will cause the spinner to remain stationary and not spin.
|
|
55
|
+
*
|
|
56
|
+
* @default 2
|
|
57
|
+
*/
|
|
58
|
+
spinInterval?: number;
|
|
59
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { IBusySpinnerProps } from "./busy-spinner.props";
|
|
5
|
+
import { Component, ResObject } from "./types";
|
|
6
|
+
export type { IBusySpinnerProps } from "./busy-spinner.props";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Standard dimensions for the default BusySpinner (theme:ActionHudSpinner).
|
|
10
|
+
* These dimensions are resolution-aware and scale appropriately for HD and FHD displays.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { getBusySpinnerWidth, getBusySpinnerHeight } from "@roku-sdk/components";
|
|
15
|
+
* import { useDeviceInfoEvents } from "@roku-sdk/rsg-ts/runtime";
|
|
16
|
+
*
|
|
17
|
+
* // Get current resolution and select appropriate dimension
|
|
18
|
+
* const { uiResolution } = useDeviceInfoEvents();
|
|
19
|
+
* const width = getBusySpinnerWidth(uiResolution()?.res);
|
|
20
|
+
* const height = getBusySpinnerHeight(uiResolution()?.res);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const BUSY_SPINNER_DIMENSIONS = {
|
|
24
|
+
/** Width of ActionHudSpinner: 80px in HD, 120px in FHD */
|
|
25
|
+
width: { hd: 80, fhd: 120 } as ResObject<number>,
|
|
26
|
+
/** Height of ActionHudSpinner: 80px in HD, 120px in FHD */
|
|
27
|
+
height: { hd: 80, fhd: 120 } as ResObject<number>
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get the width of the BusySpinner for the given UI resolution.
|
|
32
|
+
* Defaults to FHD (120px) if resolution is not provided.
|
|
33
|
+
*
|
|
34
|
+
* @param res - The resolution key ("hd", "fhd" or undefined)
|
|
35
|
+
* @returns Width in pixels (80 for HD, 120 for FHD)
|
|
36
|
+
*/
|
|
37
|
+
export function getBusySpinnerWidth(res?: "hd" | "fhd"): number {
|
|
38
|
+
return BUSY_SPINNER_DIMENSIONS.width[res ?? "fhd"];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get the height of the BusySpinner for the given UI resolution.
|
|
43
|
+
* Defaults to FHD (120px) if resolution is not provided.
|
|
44
|
+
*
|
|
45
|
+
* @param res - The resolution key ("hd", "fhd" or undefined)
|
|
46
|
+
* @returns Height in pixels (80 for HD, 120 for FHD)
|
|
47
|
+
*/
|
|
48
|
+
export function getBusySpinnerHeight(res?: "hd" | "fhd"): number {
|
|
49
|
+
return BUSY_SPINNER_DIMENSIONS.height[res ?? "fhd"];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* BusySpinner component - displays an animated spinning indicator.
|
|
54
|
+
*
|
|
55
|
+
* The BusySpinner is commonly used to provide visual feedback during loading operations
|
|
56
|
+
* or to indicate that processing is in progress.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* <busySpinner
|
|
61
|
+
* uri="theme:ActionHudSpinner"
|
|
62
|
+
* control="start"
|
|
63
|
+
* clockwise={true}
|
|
64
|
+
* spinInterval={2}
|
|
65
|
+
* />
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* // Controlling the spinner with state
|
|
71
|
+
* const [isLoading, setIsLoading] = createSignal(true);
|
|
72
|
+
*
|
|
73
|
+
* <busySpinner
|
|
74
|
+
* uri="pkg:/images/custom-spinner.png"
|
|
75
|
+
* control={isLoading() ? "start" : "stop"}
|
|
76
|
+
* visible={isLoading()}
|
|
77
|
+
* />
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @see {@link https://developer.roku.com/dev/docs/busyspinner}
|
|
81
|
+
*/
|
|
82
|
+
export const BusySpinner: Component<IBusySpinnerProps> = (props: IBusySpinnerProps) => {
|
|
83
|
+
return <busySpinner {...props}></busySpinner>;
|
|
84
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { IGroupProps } from "./group.props";
|
|
5
|
+
import type { ILayoutGroupProps } from "./layout-group.props";
|
|
6
|
+
import type { EventHandler, Uri } from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Properties shared across ButtonGroup-like components.
|
|
10
|
+
*
|
|
11
|
+
* Provides the visual customization fields for button groups, including text styling,
|
|
12
|
+
* icon bitmaps, sizing, and button creation via string arrays.
|
|
13
|
+
*
|
|
14
|
+
* @see {@link https://developer.roku.com/dev/docs/buttongroup}
|
|
15
|
+
*/
|
|
16
|
+
export interface IButtonGroupDataProps {
|
|
17
|
+
id?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Specifies the button label color for all unfocused Button nodes in the group.
|
|
20
|
+
* Only set to override the system default.
|
|
21
|
+
* @default 0xffffffff
|
|
22
|
+
*/
|
|
23
|
+
textColor?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Specifies the button label color for the Button node that has focus, if any.
|
|
26
|
+
* Only set to override the system default.
|
|
27
|
+
* @default 0xffffffff
|
|
28
|
+
*/
|
|
29
|
+
focusedTextColor?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Specifies the font for all unfocused Button nodes in the group.
|
|
32
|
+
* Only set to override the system default.
|
|
33
|
+
* @default "font:MediumSystemFont"
|
|
34
|
+
*/
|
|
35
|
+
textFont?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Specifies the font for the Button node that has focus, if any.
|
|
38
|
+
* Only set to override the system default.
|
|
39
|
+
* @default "font:MediumBoldSystemFont"
|
|
40
|
+
*/
|
|
41
|
+
focusedTextFont?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Specifies the bitmap to be used as the focus footprint background when focus is not
|
|
44
|
+
* on the ButtonGroup node. The focus footprint is a visual indicator of the button that
|
|
45
|
+
* will take focus when focus moves back onto the ButtonGroup.
|
|
46
|
+
*/
|
|
47
|
+
focusFootprintBitmapUri?: Uri;
|
|
48
|
+
/**
|
|
49
|
+
* Specifies the bitmap to be used as the background for the Button node that has focus.
|
|
50
|
+
* Only set to override the system default.
|
|
51
|
+
*/
|
|
52
|
+
focusBitmapUri?: Uri;
|
|
53
|
+
/**
|
|
54
|
+
* Specifies the bitmap for the button icon for all unfocused Button nodes in the group.
|
|
55
|
+
* Only set to override the system default.
|
|
56
|
+
*/
|
|
57
|
+
iconUri?: Uri;
|
|
58
|
+
/**
|
|
59
|
+
* Specifies the bitmap for the focused button icon.
|
|
60
|
+
* Only set to override the system default.
|
|
61
|
+
*/
|
|
62
|
+
focusedIconUri?: Uri;
|
|
63
|
+
/**
|
|
64
|
+
* Specifies the height of each Button node in the group.
|
|
65
|
+
* Only set to override the system default.
|
|
66
|
+
* @default 0
|
|
67
|
+
*/
|
|
68
|
+
height?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Specifies the minimum width for the Button nodes in the group.
|
|
71
|
+
* Must be less than or equal to {@link IButtonGroupDataProps.maxWidth | maxWidth}.
|
|
72
|
+
* @default 0
|
|
73
|
+
*/
|
|
74
|
+
minWidth?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Specifies the maximum width for the Button nodes in the group.
|
|
77
|
+
* Must be greater than or equal to {@link IButtonGroupDataProps.minWidth | minWidth}.
|
|
78
|
+
* @default 32767
|
|
79
|
+
*/
|
|
80
|
+
maxWidth?: number;
|
|
81
|
+
width?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Controls whether the focus footprint bitmap is displayed when the ButtonGroup
|
|
84
|
+
* does not have the key focus.
|
|
85
|
+
* @default false
|
|
86
|
+
*/
|
|
87
|
+
showFocusFootprint?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Specifies the height of each Button node in the group.
|
|
90
|
+
* Only set to override the system default.
|
|
91
|
+
* @default 0
|
|
92
|
+
*/
|
|
93
|
+
buttonHeight?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Specifies whether the button labels and icons should be right- or left-justified.
|
|
96
|
+
* When right-justified and there is an icon, it appears to the right of the button label.
|
|
97
|
+
* @default false
|
|
98
|
+
*/
|
|
99
|
+
rightJustify?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Causes the button with the specified index to receive the focus when the ButtonGroup
|
|
102
|
+
* has the key focus. If the ButtonGroup does not have the key focus when set, the
|
|
103
|
+
* specified button will display the focus footprint as its background.
|
|
104
|
+
* @default 0
|
|
105
|
+
*/
|
|
106
|
+
focusButton?: number;
|
|
107
|
+
/**
|
|
108
|
+
* Allows a set of Button nodes to be easily created by providing an array of button labels.
|
|
109
|
+
* Each string in the array results in a Button node added to the ButtonGroup, using the
|
|
110
|
+
* string as the button label.
|
|
111
|
+
* @default []
|
|
112
|
+
*/
|
|
113
|
+
buttons?: string[];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Properties for the ButtonGroup node.
|
|
118
|
+
*
|
|
119
|
+
* ButtonGroup manages the layout, visual attributes, and focus management of a vertical
|
|
120
|
+
* list of Button nodes. When the ButtonGroup has focus, it sets the key focus on a single
|
|
121
|
+
* one of its child Button nodes.
|
|
122
|
+
*
|
|
123
|
+
* @see {@link https://developer.roku.com/dev/docs/buttongroup}
|
|
124
|
+
*/
|
|
125
|
+
export interface IButtonGroupProps extends IButtonGroupDataProps, IGroupProps, ILayoutGroupProps {
|
|
126
|
+
/**
|
|
127
|
+
* Specifies the text for a single button. Typically `buttons` (string array) is preferred
|
|
128
|
+
* for creating multiple buttons at once.
|
|
129
|
+
*/
|
|
130
|
+
text?: string;
|
|
131
|
+
/**
|
|
132
|
+
* Fired when a button in the group is selected by the user. The handler receives the
|
|
133
|
+
* zero-based index of the selected button.
|
|
134
|
+
*/
|
|
135
|
+
onButtonSelected?: EventHandler<number>;
|
|
136
|
+
/**
|
|
137
|
+
* Fired when a button in the group receives the key focus. The handler receives the
|
|
138
|
+
* zero-based index of the focused button.
|
|
139
|
+
*/
|
|
140
|
+
onButtonFocused?: EventHandler<number>;
|
|
141
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
import type { IButtonGroupProps } from "./button-group.props";
|
|
5
|
+
import { Component } from "./types";
|
|
6
|
+
export type { IButtonGroupDataProps, IButtonGroupProps } from "./button-group.props";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ButtonGroup manages the layout, visual attributes, and focus management of a vertical
|
|
10
|
+
* list of Button nodes. Buttons can be created by setting the `buttons` field to an array
|
|
11
|
+
* of label strings. A single observer on `onButtonSelected` can watch for any button in
|
|
12
|
+
* the group to be selected.
|
|
13
|
+
*
|
|
14
|
+
* By default, Button nodes added to the group will have the default button appearance.
|
|
15
|
+
* Several fields allow you to override the appearance of all buttons at once.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <buttonGroup
|
|
20
|
+
* buttons={["Play", "Pause", "Stop"]}
|
|
21
|
+
* onButtonSelected={(index) => console.log("Selected button:", index)}
|
|
22
|
+
* onButtonFocused={(index) => console.log("Focused button:", index)}
|
|
23
|
+
* />
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see {@link https://developer.roku.com/dev/docs/buttongroup}
|
|
27
|
+
*/
|
|
28
|
+
export const ButtonGroup: Component<IButtonGroupProps> = props => {
|
|
29
|
+
return <buttonGroup {...props}></buttonGroup>;
|
|
30
|
+
};
|