@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.
Files changed (212) hide show
  1. package/BREAKING_CHANGES.md +84 -0
  2. package/LICENSE +21 -0
  3. package/README.md +19 -2
  4. package/core-jsx.d.ts +53 -0
  5. package/eslint.config.mjs +3 -0
  6. package/jsx.d.ts +273 -0
  7. package/package.json +85 -3
  8. package/src/__tests__/container.test.tsx +150 -0
  9. package/src/__tests__/effect-gradient-validation.test.tsx +133 -0
  10. package/src/__tests__/glyphs.test.ts +202 -0
  11. package/src/__tests__/modal-boundary-export.test.ts +12 -0
  12. package/src/__tests__/native-modal.test.tsx +224 -0
  13. package/src/__tests__/tween-release.test.tsx +125 -0
  14. package/src/__tests__/tween-stops-shape.test.tsx +165 -0
  15. package/src/__tests__/tween-without-compat.test.tsx +64 -0
  16. package/src/__tests__/tween.test.tsx +195 -0
  17. package/src/blended-busy-spinner.props.ts +37 -0
  18. package/src/blended-busy-spinner.tsx +13 -0
  19. package/src/bounding-tracker.props.ts +18 -0
  20. package/src/bounding-tracker.tsx +13 -0
  21. package/src/button-row.tsx +126 -0
  22. package/src/channel-store.props.ts +425 -0
  23. package/src/channel-store.tsx +76 -0
  24. package/src/container.tsx +130 -0
  25. package/src/core/jsx-runtime.ts +39 -0
  26. package/src/focusable-group.props.ts +8 -0
  27. package/src/focusable-group.tsx +17 -0
  28. package/src/focusable.props.ts +14 -0
  29. package/src/focusable.tsx +17 -0
  30. package/src/glyphs.ts +116 -0
  31. package/src/helpers/index.tsx +4 -0
  32. package/src/helpers/resolution-context.tsx +68 -0
  33. package/src/helpers/scale-for-device-resolution.ts +31 -0
  34. package/src/index.tsx +70 -0
  35. package/src/jsx-props.ts +87 -0
  36. package/src/kdfs.d.ts +14 -0
  37. package/src/kdfs.ts +15 -0
  38. package/src/key-handler.props.ts +33 -0
  39. package/src/key-handler.tsx +24 -0
  40. package/src/modal-dialog.props.ts +38 -0
  41. package/src/modal-dialog.tsx +41 -0
  42. package/src/retain-asset.ts +24 -0
  43. package/src/sg-intrinsics.json +1153 -0
  44. package/src/sg-nodes/__tests__/__snapshots__/font.test.tsx.snap +13 -0
  45. package/src/sg-nodes/__tests__/__snapshots__/prop-forwarding.test.tsx.snap +4409 -0
  46. package/src/sg-nodes/__tests__/core-props.test.ts +301 -0
  47. package/src/sg-nodes/__tests__/font.test.tsx +141 -0
  48. package/src/sg-nodes/__tests__/imperative-methods.test.ts +276 -0
  49. package/src/sg-nodes/__tests__/prop-forwarding.test.tsx +1049 -0
  50. package/src/sg-nodes/__tests__/solid-integration.test.tsx +719 -0
  51. package/src/sg-nodes/animated-image.props.ts +64 -0
  52. package/src/sg-nodes/animated-image.tsx +23 -0
  53. package/src/sg-nodes/animation.props.ts +292 -0
  54. package/src/sg-nodes/animation.tsx +190 -0
  55. package/src/sg-nodes/array-grid.ts +602 -0
  56. package/src/sg-nodes/audio.props.ts +302 -0
  57. package/src/sg-nodes/audio.tsx +57 -0
  58. package/src/sg-nodes/busy-spinner.props.ts +59 -0
  59. package/src/sg-nodes/busy-spinner.tsx +84 -0
  60. package/src/sg-nodes/button-group.props.ts +141 -0
  61. package/src/sg-nodes/button-group.tsx +30 -0
  62. package/src/sg-nodes/button.props.ts +119 -0
  63. package/src/sg-nodes/button.tsx +31 -0
  64. package/src/sg-nodes/check-list.props.ts +71 -0
  65. package/src/sg-nodes/check-list.tsx +55 -0
  66. package/src/sg-nodes/content-node.props.ts +1316 -0
  67. package/src/sg-nodes/content-node.tsx +153 -0
  68. package/src/sg-nodes/core-props.ts +197 -0
  69. package/src/sg-nodes/custom-keyboard.props.ts +108 -0
  70. package/src/sg-nodes/custom-keyboard.tsx +48 -0
  71. package/src/sg-nodes/dialog.props.ts +159 -0
  72. package/src/sg-nodes/dialog.tsx +36 -0
  73. package/src/sg-nodes/dynamic-key-grid.props.ts +22 -0
  74. package/src/sg-nodes/dynamic-key-grid.tsx +16 -0
  75. package/src/sg-nodes/dynamic-keyboard.props.ts +30 -0
  76. package/src/sg-nodes/dynamic-keyboard.tsx +18 -0
  77. package/src/sg-nodes/dynamic-mini-keyboard.props.ts +22 -0
  78. package/src/sg-nodes/dynamic-mini-keyboard.tsx +18 -0
  79. package/src/sg-nodes/dynamic-pinpad.props.ts +57 -0
  80. package/src/sg-nodes/dynamic-pinpad.tsx +18 -0
  81. package/src/sg-nodes/effect.props.ts +119 -0
  82. package/src/sg-nodes/effect.tsx +167 -0
  83. package/src/sg-nodes/font.props.ts +45 -0
  84. package/src/sg-nodes/font.tsx +80 -0
  85. package/src/sg-nodes/group.props.ts +105 -0
  86. package/src/sg-nodes/group.tsx +30 -0
  87. package/src/sg-nodes/label-list.props.ts +86 -0
  88. package/src/sg-nodes/label-list.tsx +64 -0
  89. package/src/sg-nodes/label.props.ts +207 -0
  90. package/src/sg-nodes/label.tsx +56 -0
  91. package/src/sg-nodes/layout-group.props.ts +69 -0
  92. package/src/sg-nodes/layout-group.tsx +75 -0
  93. package/src/sg-nodes/list-panel.props.ts +103 -0
  94. package/src/sg-nodes/list-panel.tsx +49 -0
  95. package/src/sg-nodes/markup-grid.props.ts +78 -0
  96. package/src/sg-nodes/markup-grid.tsx +38 -0
  97. package/src/sg-nodes/markup-list.props.ts +66 -0
  98. package/src/sg-nodes/markup-list.tsx +38 -0
  99. package/src/sg-nodes/mask-group.props.ts +78 -0
  100. package/src/sg-nodes/mask-group.tsx +59 -0
  101. package/src/sg-nodes/media-playback-methods.ts +20 -0
  102. package/src/sg-nodes/node.ts +77 -0
  103. package/src/sg-nodes/overhang.props.ts +169 -0
  104. package/src/sg-nodes/overhang.tsx +29 -0
  105. package/src/sg-nodes/panel-set.props.ts +71 -0
  106. package/src/sg-nodes/panel-set.tsx +53 -0
  107. package/src/sg-nodes/panel.props.ts +176 -0
  108. package/src/sg-nodes/panel.tsx +56 -0
  109. package/src/sg-nodes/poster-grid.props.ts +277 -0
  110. package/src/sg-nodes/poster-grid.tsx +45 -0
  111. package/src/sg-nodes/poster.props.ts +125 -0
  112. package/src/sg-nodes/poster.tsx +44 -0
  113. package/src/sg-nodes/radio-button-list.props.ts +59 -0
  114. package/src/sg-nodes/radio-button-list.tsx +68 -0
  115. package/src/sg-nodes/rectangle.props.ts +29 -0
  116. package/src/sg-nodes/rectangle.tsx +27 -0
  117. package/src/sg-nodes/row-list.props.ts +288 -0
  118. package/src/sg-nodes/row-list.tsx +47 -0
  119. package/src/sg-nodes/scene.props.ts +65 -0
  120. package/src/sg-nodes/scene.tsx +15 -0
  121. package/src/sg-nodes/scrollable-text.props.ts +133 -0
  122. package/src/sg-nodes/scrollable-text.tsx +64 -0
  123. package/src/sg-nodes/scrolling-label.props.ts +106 -0
  124. package/src/sg-nodes/scrolling-label.tsx +42 -0
  125. package/src/sg-nodes/simple-label.props.ts +156 -0
  126. package/src/sg-nodes/simple-label.tsx +59 -0
  127. package/src/sg-nodes/sound-effect.props.ts +130 -0
  128. package/src/sg-nodes/sound-effect.tsx +65 -0
  129. package/src/sg-nodes/standard-dialog.props.ts +520 -0
  130. package/src/sg-nodes/standard-dialog.tsx +428 -0
  131. package/src/sg-nodes/standard-keyboard-dialog.props.ts +84 -0
  132. package/src/sg-nodes/standard-keyboard-dialog.tsx +58 -0
  133. package/src/sg-nodes/standard-message-dialog.props.ts +56 -0
  134. package/src/sg-nodes/standard-message-dialog.tsx +47 -0
  135. package/src/sg-nodes/standard-pinpad-dialog.props.ts +64 -0
  136. package/src/sg-nodes/standard-pinpad-dialog.tsx +44 -0
  137. package/src/sg-nodes/standard-progress-dialog.props.ts +30 -0
  138. package/src/sg-nodes/standard-progress-dialog.tsx +40 -0
  139. package/src/sg-nodes/target-group.props.ts +278 -0
  140. package/src/sg-nodes/target-group.tsx +63 -0
  141. package/src/sg-nodes/target-list.props.ts +128 -0
  142. package/src/sg-nodes/target-list.tsx +66 -0
  143. package/src/sg-nodes/target-set.props.ts +63 -0
  144. package/src/sg-nodes/target-set.tsx +42 -0
  145. package/src/sg-nodes/time-grid.props.ts +646 -0
  146. package/src/sg-nodes/time-grid.tsx +87 -0
  147. package/src/sg-nodes/tween.props.ts +115 -0
  148. package/src/sg-nodes/types.ts +150 -0
  149. package/src/sg-nodes/video.props.ts +642 -0
  150. package/src/sg-nodes/video.tsx +75 -0
  151. package/src/sg-nodes/voice-text-edit-box.props.ts +91 -0
  152. package/src/sg-nodes/voice-text-edit-box.tsx +15 -0
  153. package/src/sg-nodes/zoom-row-list.props.ts +416 -0
  154. package/src/sg-nodes/zoom-row-list.tsx +42 -0
  155. package/src/standard-dialog-next/__tests__/standard-dialog-next.test.tsx +186 -0
  156. package/src/standard-dialog-next/index.ts +5 -0
  157. package/src/standard-dialog-next/standard-dialog-next.props.ts +80 -0
  158. package/src/standard-dialog-next/standard-dialog-next.tsx +326 -0
  159. package/src/test-helpers/tween-test-setup.ts +78 -0
  160. package/src/tween.tsx +173 -0
  161. package/src/types.ts +8 -0
  162. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.brs +13 -0
  163. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml +10 -0
  164. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml.d.ts +42 -0
  165. package/src/xml-components/bounding-tracker/BoundingTracker.brs +26 -0
  166. package/src/xml-components/bounding-tracker/BoundingTracker.xml +14 -0
  167. package/src/xml-components/bounding-tracker/BoundingTracker.xml.d.ts +42 -0
  168. package/src/xml-components/channel-store/channelStoreExt.brs +159 -0
  169. package/src/xml-components/channel-store/channelStoreExt.xml +29 -0
  170. package/src/xml-components/channel-store/channelStoreExt.xml.d.ts +57 -0
  171. package/src/xml-components/focus/focusable-group.brs +251 -0
  172. package/src/xml-components/focus/focusable-group.xml +15 -0
  173. package/src/xml-components/focus/focusable-group.xml.d.ts +42 -0
  174. package/src/xml-components/focus/focusable.xml +18 -0
  175. package/src/xml-components/focus/focusable.xml.d.ts +48 -0
  176. package/src/xml-components/keyboards/CustomAddressKDF.keyboard.json +547 -0
  177. package/src/xml-components/keyboards/CustomKeyboard.brs +322 -0
  178. package/src/xml-components/keyboards/CustomKeyboard.xml +45 -0
  179. package/src/xml-components/keyboards/CustomKeyboard.xml.d.ts +73 -0
  180. package/src/xml-components/keyboards/KeyGridExt.xml +4 -0
  181. package/src/xml-components/keyboards/KeyGridExt.xml.d.ts +37 -0
  182. package/src/xml-components/keyboards/KeyboardExt.xml +4 -0
  183. package/src/xml-components/keyboards/KeyboardExt.xml.d.ts +37 -0
  184. package/src/xml-components/keyboards/MOPPinPadCustomKDF.keyboard.json +151 -0
  185. package/src/xml-components/keyboards/MiniKeyboardExt.xml +4 -0
  186. package/src/xml-components/keyboards/MiniKeyboardExt.xml.d.ts +37 -0
  187. package/src/xml-components/keyboards/PhonePadCustomKDF.keyboard.json +42 -0
  188. package/src/xml-components/keyboards/PinPadExt.brs +102 -0
  189. package/src/xml-components/keyboards/PinPadExt.xml +17 -0
  190. package/src/xml-components/keyboards/PinPadExt.xml.d.ts +49 -0
  191. package/src/xml-components/keyboards/WifiCustomKDF.keyboard.json +1242 -0
  192. package/src/xml-components/keyhandler/KeyHandler.brs +30 -0
  193. package/src/xml-components/keyhandler/KeyHandler.xml +16 -0
  194. package/src/xml-components/keyhandler/KeyHandler.xml.d.ts +45 -0
  195. package/src/xml-components/managed-list-panel/ManagedListPanel.brs +137 -0
  196. package/src/xml-components/managed-list-panel/ManagedListPanel.xml +10 -0
  197. package/src/xml-components/managed-list-panel/ManagedListPanel.xml.d.ts +37 -0
  198. package/src/xml-components/modal/Modal.brs +105 -0
  199. package/src/xml-components/modal/Modal.xml +22 -0
  200. package/src/xml-components/modal/Modal.xml.d.ts +54 -0
  201. package/src/xml-components/modal/StandardDialogExt.xml +10 -0
  202. package/src/xml-components/modal/StandardDialogExt.xml.d.ts +37 -0
  203. package/src/xml-components/modal/StandardKeyboardDialogExt.xml +10 -0
  204. package/src/xml-components/modal/StandardKeyboardDialogExt.xml.d.ts +37 -0
  205. package/src/xml-components/modal/StandardMessageDialogExt.xml +10 -0
  206. package/src/xml-components/modal/StandardMessageDialogExt.xml.d.ts +37 -0
  207. package/src/xml-components/modal/StandardPinPadDialogExt.xml +10 -0
  208. package/src/xml-components/modal/StandardPinPadDialogExt.xml.d.ts +37 -0
  209. package/src/xml-components/modal/StandardProgressDialogExt.xml +10 -0
  210. package/src/xml-components/modal/StandardProgressDialogExt.xml.d.ts +37 -0
  211. package/src/xml-components/modal/tests/Modal.test.brs +195 -0
  212. package/tsconfig.json +10 -0
@@ -0,0 +1,77 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import { ComponentRef } from "@roku-sdk/interfaces";
5
+
6
+ import { EventHandler, JSX } from "./types";
7
+
8
+ /**
9
+ * Properties handled by the SolidJS framework layer.
10
+ * These are intercepted and never sent to SceneGraph as properties.
11
+ */
12
+ export interface IFrameworkProps {
13
+ /**
14
+ * Reference to the component for imperative operations like focus()
15
+ *
16
+ * @example
17
+ * // Example from focusable-group-basic.tsx
18
+ * let buttonRef: ComponentRef<IButtonProps> | undefined;
19
+ *
20
+ * <Button ref={buttonRef} ... />
21
+ *
22
+ * // Later, imperatively call focus
23
+ * buttonRef?.focus();
24
+ */
25
+ ref?: ComponentRef<this> | ((ref: ComponentRef<this>) => void);
26
+
27
+ /** Declarative focus control - triggers focus on render */
28
+ gainFocus?: boolean;
29
+
30
+ /** Child elements - handled by renderer's tree manipulation, not sent as a property */
31
+ children?: JSX.Element;
32
+ }
33
+
34
+ export interface IBaseNodeProps {
35
+ /**
36
+ * Allows the node to be identified by SceneGraph. The value is also supplied as the parameter
37
+ * to certain event handlers (like onFocusedChild).
38
+ */
39
+ id?: string;
40
+
41
+ /** Provides a hint as to whether or not this node can take the key focus. */
42
+ focusable?: boolean;
43
+
44
+ /**
45
+ * Event handler called when a child node gains or loses focus.
46
+ * When focus is gained, the parameter is the child's `id`, or `""` if the child has no id.
47
+ * When focus is lost, the parameter is `null`.
48
+ *
49
+ * @param focusedChildId - The id of the child that gained focus, `""` if it has no id, or `null` when focus is lost
50
+ *
51
+ * @example
52
+ * const handleButtonFocused = (focusedChildId: string | null) => {
53
+ * if (focusedChildId === null) {
54
+ * console.log("Button lost focus");
55
+ * } else if (focusedChildId === "") {
56
+ * console.log("Unidentified child gained focus");
57
+ * } else {
58
+ * console.log(`Button Focused Id: ${focusedChildId}`);
59
+ * }
60
+ * };
61
+ *
62
+ * <FocusableGroup onFocusedChild={handleButtonFocused}>
63
+ * <Button id="button0" text="Button 0" />
64
+ * </FocusableGroup>
65
+ */
66
+ onFocusedChild?: EventHandler<string | null>;
67
+ }
68
+
69
+ /**
70
+ * Base properties for all SceneGraph nodes.
71
+ *
72
+ * Node is the abstract base class for all SceneGraph nodes. It provides fundamental properties
73
+ * and functionality that all nodes inherit, including identification, focus management, and event handling.
74
+ *
75
+ * @see {@link https://developer.roku.com/dev/docs/node}
76
+ */
77
+ export interface INodeProps extends IFrameworkProps, IBaseNodeProps {}
@@ -0,0 +1,169 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { Uri } from "../types";
5
+ import type { IGroupProps } from "./group.props";
6
+
7
+ /**
8
+ * Properties for the Overhang node.
9
+ *
10
+ * The Overhang node provides an information bar that is displayed at the top of a screen
11
+ * in many Roku apps. The regions occupied by the overhang can be filled with either a
12
+ * solid color or a bitmap.
13
+ *
14
+ * @see {@link https://developer.roku.com/dev/docs/overhang}
15
+ *
16
+ * @example
17
+ * <Overhang
18
+ * title="My Channel"
19
+ * logoUri="pkg:/images/logo.png"
20
+ * showClock={true}
21
+ * showOptions={true}
22
+ * optionsAvailable={true}
23
+ * />
24
+ */
25
+ export interface IOverhangProps extends IGroupProps {
26
+ /**
27
+ * Fills the overhang area with the specified color.
28
+ * If both `color` and `backgroundUri` are set, the last one set is used.
29
+ * @default 0x232323ff
30
+ */
31
+ color?: string;
32
+
33
+ /**
34
+ * Specifies the URI of a bitmap to be used to fill the overhang area.
35
+ * If both `color` and `backgroundUri` are set, the last one set is used.
36
+ * @default ""
37
+ */
38
+ backgroundUri?: Uri;
39
+
40
+ /**
41
+ * Specifies the URI of a bitmap to be drawn on the left side of the overhang area.
42
+ * If both `logoUri` and `title` are set, the logo will appear to the left of the
43
+ * title, with an optional divider bitmap between them.
44
+ * @default ""
45
+ */
46
+ logoUri?: Uri;
47
+
48
+ /**
49
+ * Specifies an adjustment to the vertical position of the logo to allow
50
+ * alignment of the logo relative to the baseline of the options prompt.
51
+ * @default 0
52
+ */
53
+ logoBaselineOffset?: number;
54
+
55
+ /**
56
+ * Specifies a text string to be displayed on the left side of the overhang area,
57
+ * to the right of the logo if `logoUri` is also specified.
58
+ * @default ""
59
+ */
60
+ title?: string;
61
+
62
+ /**
63
+ * Specifies the color of the title text.
64
+ * @default 0xddddddff
65
+ */
66
+ titleColor?: string;
67
+
68
+ /**
69
+ * Controls whether or not a clock is displayed on the right side of the overhang.
70
+ * The default value is based on the device clock format setting.
71
+ */
72
+ showClock?: boolean;
73
+
74
+ /**
75
+ * Specifies the color of the clock text.
76
+ * @default 0xddddddff
77
+ */
78
+ clockColor?: string;
79
+
80
+ /**
81
+ * Specifies an alternate string to be displayed in the location of the clock.
82
+ * If set to a non-empty string, the specified string is displayed in place of
83
+ * the clock. If set to an empty string, the clock is restored.
84
+ * @default ""
85
+ */
86
+ clockText?: string;
87
+
88
+ /**
89
+ * Controls whether or not the options prompt is displayed on the right side
90
+ * of the overhang. Note that `optionsAvailable` must also be set to true for
91
+ * the options prompt to be displayed.
92
+ * @default false
93
+ */
94
+ showOptions?: boolean;
95
+
96
+ /**
97
+ * Specifies the color of the options indicator when options are available.
98
+ * @default 0xddddddff
99
+ */
100
+ optionsColor?: string;
101
+
102
+ /**
103
+ * Specifies the color of the options indicator when options are not available.
104
+ * @default 0xdddddd44
105
+ */
106
+ optionsDimColor?: string;
107
+
108
+ /**
109
+ * Specifies the tint color to be applied to the options icon when options are available.
110
+ * @default 0xffffffff
111
+ */
112
+ optionsIconColor?: string;
113
+
114
+ /**
115
+ * Specifies the tint color to be applied to the options icon when options are not available.
116
+ * @default 0xffffffff
117
+ */
118
+ optionsIconDimColor?: string;
119
+
120
+ /**
121
+ * Specifies whether or not the Options remote key is currently available.
122
+ * When true and `showOptions` is also true, the options prompt is displayed.
123
+ * @default false
124
+ */
125
+ optionsAvailable?: boolean;
126
+
127
+ /**
128
+ * Specifies the text to be displayed next to the Options (*) symbol.
129
+ * @default ""
130
+ */
131
+ optionsText?: string;
132
+
133
+ /**
134
+ * Specifies the maximum width to which the `optionsText` text will be ellipsized.
135
+ * A value of 0.0 indicates no width restriction.
136
+ * @default 0.0
137
+ */
138
+ optionsMaxWidth?: number;
139
+
140
+ /**
141
+ * Specifies the URI of a bitmap to be used as a divider between the logo and the title.
142
+ * @default ""
143
+ */
144
+ leftDividerUri?: Uri;
145
+
146
+ /**
147
+ * Specifies the vertical offset of the left divider position.
148
+ * @default 0
149
+ */
150
+ leftDividerVertOffset?: number;
151
+
152
+ /**
153
+ * Specifies the URI of a bitmap to be used as a divider between the clock and options prompt.
154
+ * @default ""
155
+ */
156
+ rightDividerUri?: Uri;
157
+
158
+ /**
159
+ * Specifies the vertical offset of the right divider position.
160
+ * @default 0
161
+ */
162
+ rightDividerVertOffset?: number;
163
+
164
+ /**
165
+ * Specifies the height of the Overhang region.
166
+ * @default 115
167
+ */
168
+ height?: number;
169
+ }
@@ -0,0 +1,29 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import { Component } from "../types";
5
+ import type { IOverhangProps } from "./overhang.props";
6
+ export type { IOverhangProps } from "./overhang.props";
7
+
8
+ /**
9
+ * The Overhang node provides an information bar that is displayed at the top of a screen
10
+ * in many Roku apps. The regions occupied by the overhang can be filled with either a
11
+ * solid color or a bitmap.
12
+ *
13
+ * The overhang typically includes a logo, title, clock, and options indicator, all of
14
+ * which can be customized or hidden as needed.
15
+ *
16
+ * @see {@link https://developer.roku.com/dev/docs/overhang}
17
+ *
18
+ * @example
19
+ * <overhang
20
+ * title="My Channel"
21
+ * logoUri="pkg:/images/logo.png"
22
+ * showClock={true}
23
+ * showOptions={true}
24
+ * optionsAvailable={true}
25
+ * />
26
+ */
27
+ export const Overhang: Component<IOverhangProps> = props => {
28
+ return <overhang {...props}></overhang>;
29
+ };
@@ -0,0 +1,71 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IGroupProps } from "./group.props";
5
+ import type { EventHandler } from "./types";
6
+
7
+ /**
8
+ * Properties for the PanelSet component.
9
+ *
10
+ * PanelSet extends Group and provides the panel sliding behavior seen in the Roku home screen.
11
+ * It manages the position of child Panel nodes and implements left/right panel sliding behavior.
12
+ * When a Left or Back key is pressed, the panels slide to the right, back towards their original
13
+ * home position. When a Right key is pressed, the panels slide left, and panels that slide off
14
+ * the right side of the display are automatically removed from the PanelSet.
15
+ *
16
+ * @see {@link https://developer.roku.com/dev/docs/panelset}
17
+ * @see {@link https://developer.roku.com/dev/docs/sliding-panels-nodes | Sliding Panels Overview}
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { PanelSet, Panel, Label } from "@roku-sdk/components";
22
+ *
23
+ * <PanelSet width={1280} height={605} slideDuration={500}>
24
+ * <Panel panelSize="narrow" leftOnly={true}>
25
+ * <Label text="Navigation Menu" translation={[50, 50]} />
26
+ * </Panel>
27
+ * <Panel panelSize="wide" isFullScreen={false}>
28
+ * <Label text="Content Panel" translation={[50, 50]} />
29
+ * </Panel>
30
+ * </PanelSet>
31
+ * ```
32
+ */
33
+ export interface IPanelSetProps extends IGroupProps {
34
+ /**
35
+ * Specifies the width of the PanelSet node in local coordinates.
36
+ * In most cases, this should be set to the display width (e.g., 1280 for HD, 1920 for FHD).
37
+ *
38
+ * @default 1280
39
+ */
40
+ width?: number;
41
+
42
+ /**
43
+ * Specifies the height of the PanelSet node in local coordinates.
44
+ * In most cases, this should be set to the display height minus the overhang height.
45
+ *
46
+ * @default 605
47
+ */
48
+ height?: number;
49
+
50
+ /**
51
+ * Specifies the duration of the slide transition in milliseconds.
52
+ *
53
+ * @default 500
54
+ */
55
+ slideDuration?: number;
56
+
57
+ /**
58
+ * Fired when the `isGoingBack` field changes.
59
+ * The value is `true` when the PanelSet panels are sliding right, back towards the original
60
+ * home position, as a result of a Left or Back key press. The value is `false` otherwise.
61
+ *
62
+ * @see **ListPanel, GridPanel Best Practices** at the internal RSG 2.0 SDK documentation (knowledge-base/list-grid-panel-best-practice.md) for handling slidingBack prop
63
+ */
64
+ onIsGoingBack?: EventHandler<boolean>;
65
+
66
+ /**
67
+ * Fired when the `numPanels` field changes.
68
+ * Contains the current number of Panel nodes that are children of the PanelSet node.
69
+ */
70
+ onNumPanels?: EventHandler<number>;
71
+ }
@@ -0,0 +1,53 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IPanelSetProps } from "./panel-set.props";
5
+ import { Component } from "./types";
6
+ export type { IPanelSetProps } from "./panel-set.props";
7
+
8
+ /**
9
+ * PanelSet is a Group node subclass that provides the panel sliding behavior seen in the Roku
10
+ * home screen. It manages child Panel nodes and implements left/right panel sliding behavior.
11
+ *
12
+ * In most cases, a PanelSet displays two Panel nodes: one on the left side (the "left panel")
13
+ * and one on the right side (the "right panel"). The panels slide left or right depending on
14
+ * user input, creating a navigation experience similar to the Roku OS.
15
+ *
16
+ * @see {@link https://developer.roku.com/dev/docs/panelset}
17
+ * @see {@link https://developer.roku.com/dev/docs/sliding-panels-nodes | Sliding Panels Overview}
18
+ *
19
+ * @example
20
+ * Basic PanelSet with narrow and wide panels:
21
+ * ```tsx
22
+ * <panelSet width={1280} height={605}>
23
+ * <panel panelSize="narrow" leftOnly={true}>
24
+ * <label text="Menu" translation={[50, 50]} />
25
+ * </panel>
26
+ * <panel panelSize="wide">
27
+ * <label text="Details" translation={[50, 50]} />
28
+ * </panel>
29
+ * </panelSet>
30
+ * ```
31
+ *
32
+ * @example
33
+ * PanelSet with event handlers:
34
+ * ```tsx
35
+ * <panelSet
36
+ * width={1920}
37
+ * height={880}
38
+ * slideDuration={300}
39
+ * onIsGoingBack={(e) => console.log("Going back:", e.value)}
40
+ * onNumPanels={(e) => console.log("Panel count:", e.value)}
41
+ * >
42
+ * <panel panelSize="medium">
43
+ * {/* Panel content *\/}
44
+ * </panel>
45
+ * <panel panelSize="medium">
46
+ * {/* Panel content *\/}
47
+ * </panel>
48
+ * </panelSet>
49
+ * ```
50
+ */
51
+ export const PanelSet: Component<IPanelSetProps> = props => {
52
+ return <panelSet {...props}></panelSet>;
53
+ };
@@ -0,0 +1,176 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IGroupProps } from "./group.props";
5
+ import type { EventHandler } from "./types";
6
+
7
+ /**
8
+ * Panel size options for the Panel component.
9
+ * Controls the width and leftPosition of the panel when displayed in a PanelSet.
10
+ *
11
+ * | Value | Meaning |
12
+ * |----------|------------------------------------------------------------------------------|
13
+ * | `narrow` | Set the width and leftPosition fields to the values for a narrow Panel |
14
+ * | `medium` | Set the width and leftPosition fields to the values for a medium width Panel|
15
+ * | `wide` | Set the width and leftPosition fields to the values for a wide Panel |
16
+ * | `full` | Set the width and leftPosition fields to the values for a full width Panel |
17
+ *
18
+ * Note: PanelSet usage mandates that whenever two Panels are visible, they should include
19
+ * either one narrow and one wide panel or two medium width panels. If one Panel is visible,
20
+ * its panelSize should be set to "full".
21
+ */
22
+ export type PanelSize = "narrow" | "medium" | "wide" | "full";
23
+
24
+ /**
25
+ * Properties for the Panel component.
26
+ *
27
+ * Panel extends Group and is used to create sliding panels for app UI,
28
+ * similar to those in the Roku OS home screen.
29
+ *
30
+ * @see {@link https://developer.roku.com/dev/docs/panel}
31
+ * @see {@link https://developer.roku.com/dev/docs/sliding-panels-nodes Sliding Panels Overview}
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * import { Panel, PanelSet } from "@roku-sdk/components";
36
+ *
37
+ * <PanelSet>
38
+ * <Panel panelSize="medium" leftOnly={true}>
39
+ * <Label text="Panel Content" />
40
+ * </Panel>
41
+ * </PanelSet>
42
+ * ```
43
+ */
44
+ export interface IPanelProps extends IGroupProps {
45
+ /**
46
+ * Specifies one of the default panel sizes. Setting this field causes the width
47
+ * and leftPosition fields to be set to values that match the RSG preferred layout
48
+ * for a panel of the specified size.
49
+ *
50
+ * @default "narrow"
51
+ */
52
+ panelSize?: PanelSize;
53
+
54
+ /**
55
+ * Specifies the width of the panel in pixels. In most cases, this should be set
56
+ * by setting the panelSize field to one of the pre-configured settings.
57
+ *
58
+ * @default 388
59
+ */
60
+ width?: number;
61
+
62
+ /**
63
+ * Specifies the height of the panel in pixels. In most cases, this will be set
64
+ * by the PanelSet and should be treated as a read-only value.
65
+ *
66
+ * @default -1
67
+ */
68
+ height?: number;
69
+
70
+ /**
71
+ * Specifies the horizontal position of the panel relative to the left edge of the
72
+ * PanelSet (which is at the left edge of the display by default). In most cases,
73
+ * this should be set by setting the panelSize field to one of the pre-configured settings.
74
+ *
75
+ * @default 105
76
+ */
77
+ leftPosition?: number;
78
+
79
+ /**
80
+ * Provides information to the PanelSet that this Panel should never appear in the
81
+ * right position of the PanelSet. When the panels are sliding back towards the home
82
+ * position (as a result of a Left or Back key press), and the panel slides into the
83
+ * right position, the PanelSet initiates another slide in the same back direction
84
+ * so that the panel does not end up on the right.
85
+ *
86
+ * @default false
87
+ */
88
+ leftOnly?: boolean;
89
+
90
+ /**
91
+ * Provides information to the PanelSet as to whether or not this panel has another
92
+ * panel to its right. If set to true, the PanelSet's right arrow indicator is displayed
93
+ * and pressing the right arrow button on the remote triggers the PanelSet to move the
94
+ * focus one panel to the right, sliding the Panels as needed to make sure the panel
95
+ * that has the focus ends up onscreen. If set to false, the PanelSet's right arrow
96
+ * indicator is not displayed and the right arrow button does not trigger any change
97
+ * to the focused panel.
98
+ *
99
+ * @default false
100
+ */
101
+ hasNextPanel?: boolean;
102
+
103
+ /**
104
+ * Indicates that this panel should be the only panel displayed (i.e., it will take
105
+ * up both the left and right positions in the PanelSet).
106
+ *
107
+ * @default false
108
+ */
109
+ isFullScreen?: boolean;
110
+
111
+ /**
112
+ * Setting this field to a value greater than 1 causes the PanelSet to move the focus
113
+ * back that many panels when the user presses the left arrow button, sliding the
114
+ * Panels as needed to make sure the panel that has the focus ends up onscreen.
115
+ *
116
+ * @default 1
117
+ */
118
+ goBackCount?: number;
119
+
120
+ /**
121
+ * When set to true, pressing the OK/Select button on the remote control causes the
122
+ * PanelSet focus to move to the next panel.
123
+ *
124
+ * `ListPanel` may override this transiently. When its next panel is supplied
125
+ * asynchronously, the field is forced to false for the few milliseconds the previously
126
+ * built panel is still attached, then restored to the value it had when suppression
127
+ * began — so a change made to this prop during that window is not picked up.
128
+ *
129
+ * @default true
130
+ */
131
+ selectButtonMovesPanelForward?: boolean;
132
+
133
+ /**
134
+ * When the panel is used as part of the OverhangPanelSetScene, setting the overhangTitle
135
+ * field will cause that text to be displayed as the title in the overhang when the panel
136
+ * slides into the left position of the PanelSet.
137
+ *
138
+ * @default ""
139
+ */
140
+ overhangTitle?: string;
141
+
142
+ /**
143
+ * When the panel is used as part of the OverhangPanelSetScene, setting the clockText
144
+ * field will cause that text to be displayed instead of the clock in the overhang when
145
+ * the panel slides into the left position of the PanelSet.
146
+ *
147
+ * @default ""
148
+ */
149
+ clockText?: string;
150
+
151
+ /**
152
+ * When the panel is used as part of the OverhangPanelSetScene, setting optionsAvailable
153
+ * will enable/disable the options button handling when the panel slides into the left
154
+ * position of the PanelSet. The overhang's options prompt will change appearance to
155
+ * provide feedback to the user that the options button is enabled/disabled.
156
+ *
157
+ * @default false
158
+ */
159
+ optionsAvailable?: boolean;
160
+
161
+ /**
162
+ * Event handler called when the panel's offscreen left state changes.
163
+ * This field is set by the PanelSet to indicate that the panel is positioned offscreen
164
+ * of the left edge of the PanelSet. This field is often observed to cancel outstanding
165
+ * load requests for images that are displayed on the panel.
166
+ */
167
+ onIsOffscreenLeft?: EventHandler<boolean>;
168
+
169
+ /**
170
+ * Event handler called when the panel's left orientation changes.
171
+ * When the panel is used as part of the OverhangPanelSetScene, leftOrientation will be
172
+ * set to true when the panel moves into the left position of the PanelSet and set to
173
+ * false when the panel moves into the right position of the PanelSet.
174
+ */
175
+ onLeftOrientation?: EventHandler<boolean>;
176
+ }
@@ -0,0 +1,56 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IPanelProps } from "./panel.props";
5
+ import { Component } from "./types";
6
+ export type { PanelSize, IPanelProps } from "./panel.props";
7
+
8
+ /**
9
+ * Panel is a Group node subclass used to create sliding panels for app UI,
10
+ * similar to those in the Roku OS home screen.
11
+ *
12
+ * Panels provide a standardized way to create multi-level navigation screens where users
13
+ * can drill down through content. When used within a PanelSet, panels automatically handle
14
+ * sliding animations and focus management.
15
+ *
16
+ * The Panel component supports various size presets through the `panelSize` prop, or
17
+ * custom dimensions through `width` and `height` props. Navigation behavior can be
18
+ * customized through props like `hasNextPanel`, `leftOnly`, and `goBackCount`.
19
+ *
20
+ * @see {@link https://developer.roku.com/dev/docs/panel}
21
+ * @see {@link https://developer.roku.com/dev/docs/sliding-panels-nodes Sliding Panels Overview}
22
+ *
23
+ * @example
24
+ * Basic panel usage:
25
+ * ```tsx
26
+ * <panel panelSize="medium">
27
+ * <label text="Hello Panel" translation={[50, 50]} />
28
+ * </panel>
29
+ * ```
30
+ *
31
+ * @example
32
+ * Panel with navigation control:
33
+ * ```tsx
34
+ * <panel
35
+ * panelSize="wide"
36
+ * leftOnly={true}
37
+ * hasNextPanel={true}
38
+ * onIsOffscreenLeft={(isOffscreen) => {
39
+ * console.log("Panel offscreen:", isOffscreen);
40
+ * }}
41
+ * >
42
+ * <markupList id="menuList" />
43
+ * </panel>
44
+ * ```
45
+ *
46
+ * @example
47
+ * Full screen panel:
48
+ * ```tsx
49
+ * <panel isFullScreen={true} hasNextPanel={false}>
50
+ * {/* Full screen content *\/}
51
+ * </panel>
52
+ * ```
53
+ */
54
+ export const Panel: Component<IPanelProps> = props => {
55
+ return <panel {...props}></panel>;
56
+ };