@opentui/react 0.1.33 → 0.1.35

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/README.md CHANGED
@@ -19,13 +19,15 @@ bun install @opentui/react @opentui/core react
19
19
  ## Quick Start
20
20
 
21
21
  ```tsx
22
- import { render } from "@opentui/react"
22
+ import { createCliRenderer } from "@opentui/core"
23
+ import { createRoot } from "@opentui/react"
23
24
 
24
25
  function App() {
25
26
  return <text>Hello, world!</text>
26
27
  }
27
28
 
28
- render(<App />)
29
+ const renderer = await createCliRenderer()
30
+ createRoot(renderer).render(<App />)
29
31
  ```
30
32
 
31
33
  ## TypeScript Configuration
@@ -83,23 +85,32 @@ Components can be styled using props or the `style` prop:
83
85
 
84
86
  ## API Reference
85
87
 
86
- ### `render(element, config?)`
88
+ ### `createRoot(renderer)`
87
89
 
88
- Renders a React element to the terminal.
90
+ Creates a root for rendering a React tree with the given CLI renderer.
89
91
 
90
92
  ```tsx
91
- import { render } from "@opentui/react"
93
+ import { createCliRenderer } from "@opentui/core"
94
+ import { createRoot } from "@opentui/react"
92
95
 
93
- await render(<App />, {
96
+ const renderer = await createCliRenderer({
94
97
  // Optional renderer configuration
95
98
  exitOnCtrlC: false,
96
99
  })
100
+ createRoot(renderer).render(<App />)
97
101
  ```
98
102
 
99
103
  **Parameters:**
100
104
 
101
- - `element`: React element to render
102
- - `config?`: Optional `CliRendererConfig` object
105
+ - `renderer`: A `CliRenderer` instance (typically created with `createCliRenderer()`)
106
+
107
+ **Returns:** An object with a `render` method that accepts a React element.
108
+
109
+ ### `render(element, config?)` (Deprecated)
110
+
111
+ > **Deprecated:** Use `createRoot(renderer).render(node)` instead.
112
+
113
+ Renders a React element to the terminal. This function is deprecated in favor of `createRoot`.
103
114
 
104
115
  ### Hooks
105
116
 
@@ -193,7 +204,7 @@ function App() {
193
204
  Create and manage animations using OpenTUI's timeline system. This hook automatically registers and unregisters the timeline with the animation engine.
194
205
 
195
206
  ```tsx
196
- import { render, useTimeline } from "@opentui/react"
207
+ import { useTimeline } from "@opentui/react"
197
208
  import { useEffect, useState } from "react"
198
209
 
199
210
  function App() {
@@ -326,6 +337,66 @@ function App() {
326
337
  }
327
338
  ```
328
339
 
340
+ ### Textarea Component
341
+
342
+ ```tsx
343
+ import type { TextareaRenderable } from "@opentui/core"
344
+ import { useKeyboard, useRenderer } from "@opentui/react"
345
+ import { useEffect, useRef } from "react"
346
+
347
+ function App() {
348
+ const renderer = useRenderer()
349
+ const textareaRef = useRef<TextareaRenderable>(null)
350
+
351
+ useEffect(() => {
352
+ renderer.console.show()
353
+ }, [renderer])
354
+
355
+ useKeyboard((key) => {
356
+ if (key.name === "return") {
357
+ console.log(textareaRef.current?.plainText)
358
+ }
359
+ })
360
+
361
+ return (
362
+ <box title="Interactive Editor" style={{ border: true, flexGrow: 1 }}>
363
+ <textarea ref={textareaRef} placeholder="Type here..." focused />
364
+ </box>
365
+ )
366
+ }
367
+ ```
368
+
369
+ ### Code Component
370
+
371
+ ```tsx
372
+ import { RGBA, SyntaxStyle } from "@opentui/core"
373
+
374
+ const syntaxStyle = SyntaxStyle.fromStyles({
375
+ keyword: { fg: RGBA.fromHex("#ff6b6b"), bold: true }, // red, bold
376
+ string: { fg: RGBA.fromHex("#51cf66") }, // green
377
+ comment: { fg: RGBA.fromHex("#868e96"), italic: true }, // gray, italic
378
+ number: { fg: RGBA.fromHex("#ffd43b") }, // yellow
379
+ default: { fg: RGBA.fromHex("#ffffff") }, // white
380
+ })
381
+
382
+ const codeExample = `function hello() {
383
+ // This is a comment
384
+
385
+ const message = "Hello, world!"
386
+ const count = 42
387
+
388
+ return message + " " + count
389
+ }`
390
+
391
+ function App() {
392
+ return (
393
+ <box style={{ border: true, flexGrow: 1 }}>
394
+ <code content={codeExample} filetype="javascript" syntaxStyle={syntaxStyle} />
395
+ </box>
396
+ )
397
+ }
398
+ ```
399
+
329
400
  ### Select Component
330
401
 
331
402
  Dropdown selection component.
@@ -470,7 +541,8 @@ function App() {
470
541
  ### Login Form
471
542
 
472
543
  ```tsx
473
- import { render, useKeyboard } from "@opentui/react"
544
+ import { createCliRenderer } from "@opentui/core"
545
+ import { createRoot, useKeyboard } from "@opentui/react"
474
546
  import { useCallback, useState } from "react"
475
547
 
476
548
  function App() {
@@ -526,13 +598,15 @@ function App() {
526
598
  )
527
599
  }
528
600
 
529
- render(<App />)
601
+ const renderer = await createCliRenderer()
602
+ createRoot(renderer).render(<App />)
530
603
  ```
531
604
 
532
605
  ### Counter with Timer
533
606
 
534
607
  ```tsx
535
- import { render } from "@opentui/react"
608
+ import { createCliRenderer } from "@opentui/core"
609
+ import { createRoot } from "@opentui/react"
536
610
  import { useEffect, useState } from "react"
537
611
 
538
612
  function App() {
@@ -553,14 +627,15 @@ function App() {
553
627
  )
554
628
  }
555
629
 
556
- render(<App />)
630
+ const renderer = await createCliRenderer()
631
+ createRoot(renderer).render(<App />)
557
632
  ```
558
633
 
559
634
  ### System Monitor Animation
560
635
 
561
636
  ```tsx
562
- import { TextAttributes } from "@opentui/core"
563
- import { render, useTimeline } from "@opentui/react"
637
+ import { createCliRenderer, TextAttributes } from "@opentui/core"
638
+ import { createRoot, useTimeline } from "@opentui/react"
564
639
  import { useEffect, useState } from "react"
565
640
 
566
641
  type Stats = {
@@ -636,13 +711,15 @@ export const App = () => {
636
711
  )
637
712
  }
638
713
 
639
- render(<App />)
714
+ const renderer = await createCliRenderer()
715
+ createRoot(renderer).render(<App />)
640
716
  ```
641
717
 
642
718
  ### Styled Text Showcase
643
719
 
644
720
  ```tsx
645
- import { render } from "@opentui/react"
721
+ import { createCliRenderer } from "@opentui/core"
722
+ import { createRoot } from "@opentui/react"
646
723
 
647
724
  function App() {
648
725
  return (
@@ -670,7 +747,8 @@ function App() {
670
747
  )
671
748
  }
672
749
 
673
- render(<App />)
750
+ const renderer = await createCliRenderer()
751
+ createRoot(renderer).render(<App />)
674
752
  ```
675
753
 
676
754
  ## Component Extension
@@ -678,8 +756,15 @@ render(<App />)
678
756
  You can create custom components by extending OpenTUIs base renderables:
679
757
 
680
758
  ```tsx
681
- import { BoxRenderable, OptimizedBuffer, RGBA, type BoxOptions, type RenderContext } from "@opentui/core"
682
- import { extend, render } from "@opentui/react"
759
+ import {
760
+ BoxRenderable,
761
+ createCliRenderer,
762
+ OptimizedBuffer,
763
+ RGBA,
764
+ type BoxOptions,
765
+ type RenderContext,
766
+ } from "@opentui/core"
767
+ import { createRoot, extend } from "@opentui/react"
683
768
 
684
769
  // Create custom component class
685
770
  class ButtonRenderable extends BoxRenderable {
@@ -733,5 +818,6 @@ function App() {
733
818
  )
734
819
  }
735
820
 
736
- render(<App />)
821
+ const renderer = await createCliRenderer()
822
+ createRoot(renderer).render(<App />)
737
823
  ```
package/index.js CHANGED
@@ -3,10 +3,12 @@
3
3
  import {
4
4
  ASCIIFontRenderable,
5
5
  BoxRenderable,
6
+ CodeRenderable,
6
7
  InputRenderable,
7
8
  ScrollBoxRenderable,
8
9
  SelectRenderable,
9
10
  TabSelectRenderable,
11
+ TextareaRenderable,
10
12
  TextRenderable
11
13
  } from "@opentui/core";
12
14
 
@@ -68,8 +70,10 @@ class LineBreakRenderable extends SpanRenderable {
68
70
  var baseComponents = {
69
71
  box: BoxRenderable,
70
72
  text: TextRenderable,
73
+ code: CodeRenderable,
71
74
  input: InputRenderable,
72
75
  select: SelectRenderable,
76
+ textarea: TextareaRenderable,
73
77
  scrollbox: ScrollBoxRenderable,
74
78
  "ascii-font": ASCIIFontRenderable,
75
79
  "tab-select": TabSelectRenderable,
@@ -180,6 +184,35 @@ var useTimeline = (options = {}) => {
180
184
  import { createCliRenderer, engine as engine2 } from "@opentui/core";
181
185
  import React2 from "react";
182
186
 
187
+ // src/components/error-boundary.tsx
188
+ import React from "react";
189
+
190
+ // jsx-dev-runtime.js
191
+ import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
192
+
193
+ // src/components/error-boundary.tsx
194
+ class ErrorBoundary extends React.Component {
195
+ constructor(props) {
196
+ super(props);
197
+ this.state = { hasError: false, error: null };
198
+ }
199
+ static getDerivedStateFromError(error) {
200
+ return { hasError: true, error };
201
+ }
202
+ render() {
203
+ if (this.state.hasError && this.state.error) {
204
+ return /* @__PURE__ */ jsxDEV("box", {
205
+ style: { flexDirection: "column", padding: 2 },
206
+ children: /* @__PURE__ */ jsxDEV("text", {
207
+ fg: "red",
208
+ children: this.state.error.stack || this.state.error.message
209
+ }, undefined, false, undefined, this)
210
+ }, undefined, false, undefined, this);
211
+ }
212
+ return this.props.children;
213
+ }
214
+ }
215
+
183
216
  // src/reconciler/reconciler.ts
184
217
  import ReactReconciler from "react-reconciler";
185
218
  import { ConcurrentRoot } from "react-reconciler/constants";
@@ -473,41 +506,20 @@ function _render(element, root) {
473
506
  reconciler.updateContainer(element, container, null, () => {});
474
507
  }
475
508
 
476
- // src/components/error-boundary.tsx
477
- import React from "react";
478
-
479
- // jsx-dev-runtime.js
480
- import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
481
-
482
- // src/components/error-boundary.tsx
483
- class ErrorBoundary extends React.Component {
484
- constructor(props) {
485
- super(props);
486
- this.state = { hasError: false, error: null };
487
- }
488
- static getDerivedStateFromError(error) {
489
- return { hasError: true, error };
490
- }
491
- render() {
492
- if (this.state.hasError && this.state.error) {
493
- return /* @__PURE__ */ jsxDEV("box", {
494
- style: { flexDirection: "column", padding: 2 },
495
- children: /* @__PURE__ */ jsxDEV("text", {
496
- fg: "red",
497
- children: this.state.error.stack || this.state.error.message
498
- }, undefined, false, undefined, this)
499
- }, undefined, false, undefined, this);
500
- }
501
- return this.props.children;
502
- }
503
- }
504
-
505
509
  // src/reconciler/renderer.ts
506
510
  async function render(node, rendererConfig = {}) {
507
511
  const renderer = await createCliRenderer(rendererConfig);
508
512
  engine2.attach(renderer);
509
513
  _render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
510
514
  }
515
+ function createRoot(renderer) {
516
+ return {
517
+ render: (node) => {
518
+ engine2.attach(renderer);
519
+ _render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
520
+ }
521
+ };
522
+ }
511
523
  export {
512
524
  useTimeline,
513
525
  useTerminalDimensions,
@@ -518,6 +530,7 @@ export {
518
530
  render,
519
531
  getComponentCatalogue,
520
532
  extend,
533
+ createRoot,
521
534
  componentCatalogue,
522
535
  baseComponents,
523
536
  AppContext
@@ -2,6 +2,7 @@ import type * as React from "react"
2
2
  import type {
3
3
  AsciiFontProps,
4
4
  BoxProps,
5
+ CodeProps,
5
6
  ExtendedIntrinsicElements,
6
7
  InputProps,
7
8
  LineBreakProps,
@@ -10,6 +11,7 @@ import type {
10
11
  SelectProps,
11
12
  SpanProps,
12
13
  TabSelectProps,
14
+ TextareaProps,
13
15
  TextProps,
14
16
  } from "./src/types/components"
15
17
 
@@ -34,7 +36,9 @@ export namespace JSX {
34
36
  box: BoxProps
35
37
  text: TextProps
36
38
  span: SpanProps
39
+ code: CodeProps
37
40
  input: InputProps
41
+ textarea: TextareaProps
38
42
  select: SelectProps
39
43
  scrollbox: ScrollBoxProps
40
44
  "ascii-font": AsciiFontProps
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "index.js",
5
5
  "types": "src/index.d.ts",
6
6
  "type": "module",
7
- "version": "0.1.33",
7
+ "version": "0.1.35",
8
8
  "description": "React renderer for building terminal user interfaces using OpenTUI core",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -35,7 +35,7 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@opentui/core": "0.1.33",
38
+ "@opentui/core": "0.1.35",
39
39
  "react-reconciler": "^0.32.0"
40
40
  },
41
41
  "devDependencies": {
@@ -1,11 +1,13 @@
1
- import { ASCIIFontRenderable, BoxRenderable, InputRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
1
+ import { ASCIIFontRenderable, BoxRenderable, CodeRenderable, InputRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextareaRenderable, TextRenderable } from "@opentui/core";
2
2
  import type { RenderableConstructor } from "../types/components";
3
3
  import { BoldSpanRenderable, ItalicSpanRenderable, LineBreakRenderable, SpanRenderable, UnderlineSpanRenderable } from "./text";
4
4
  export declare const baseComponents: {
5
5
  box: typeof BoxRenderable;
6
6
  text: typeof TextRenderable;
7
+ code: typeof CodeRenderable;
7
8
  input: typeof InputRenderable;
8
9
  select: typeof SelectRenderable;
10
+ textarea: typeof TextareaRenderable;
9
11
  scrollbox: typeof ScrollBoxRenderable;
10
12
  "ascii-font": typeof ASCIIFontRenderable;
11
13
  "tab-select": typeof TabSelectRenderable;
@@ -1,3 +1,19 @@
1
- import { type CliRendererConfig } from "@opentui/core";
1
+ import { CliRenderer, type CliRendererConfig } from "@opentui/core";
2
2
  import { type ReactNode } from "react";
3
+ /**
4
+ * @deprecated Use `createRoot(renderer).render(node)` instead
5
+ */
3
6
  export declare function render(node: ReactNode, rendererConfig?: CliRendererConfig): Promise<void>;
7
+ /**
8
+ * Creates a root for rendering a React tree with the given CLI renderer.
9
+ * @param renderer The CLI renderer to use
10
+ * @returns A root object with a `render` method
11
+ * @example
12
+ * ```tsx
13
+ * const renderer = await createCliRenderer()
14
+ * createRoot(renderer).render(<App />)
15
+ * ```
16
+ */
17
+ export declare function createRoot(renderer: CliRenderer): {
18
+ render: (node: ReactNode) => void;
19
+ };
@@ -10,6 +10,35 @@ var AppContext = createContext({
10
10
  renderer: null
11
11
  });
12
12
 
13
+ // src/components/error-boundary.tsx
14
+ import React from "react";
15
+
16
+ // jsx-dev-runtime.js
17
+ import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
18
+
19
+ // src/components/error-boundary.tsx
20
+ class ErrorBoundary extends React.Component {
21
+ constructor(props) {
22
+ super(props);
23
+ this.state = { hasError: false, error: null };
24
+ }
25
+ static getDerivedStateFromError(error) {
26
+ return { hasError: true, error };
27
+ }
28
+ render() {
29
+ if (this.state.hasError && this.state.error) {
30
+ return /* @__PURE__ */ jsxDEV("box", {
31
+ style: { flexDirection: "column", padding: 2 },
32
+ children: /* @__PURE__ */ jsxDEV("text", {
33
+ fg: "red",
34
+ children: this.state.error.stack || this.state.error.message
35
+ }, undefined, false, undefined, this)
36
+ }, undefined, false, undefined, this);
37
+ }
38
+ return this.props.children;
39
+ }
40
+ }
41
+
13
42
  // src/reconciler/reconciler.ts
14
43
  import ReactReconciler from "react-reconciler";
15
44
  import { ConcurrentRoot } from "react-reconciler/constants";
@@ -23,10 +52,12 @@ import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constant
23
52
  import {
24
53
  ASCIIFontRenderable,
25
54
  BoxRenderable,
55
+ CodeRenderable,
26
56
  InputRenderable,
27
57
  ScrollBoxRenderable,
28
58
  SelectRenderable,
29
59
  TabSelectRenderable,
60
+ TextareaRenderable,
30
61
  TextRenderable
31
62
  } from "@opentui/core";
32
63
 
@@ -88,8 +119,10 @@ class LineBreakRenderable extends SpanRenderable {
88
119
  var baseComponents = {
89
120
  box: BoxRenderable,
90
121
  text: TextRenderable,
122
+ code: CodeRenderable,
91
123
  input: InputRenderable,
92
124
  select: SelectRenderable,
125
+ textarea: TextareaRenderable,
93
126
  scrollbox: ScrollBoxRenderable,
94
127
  "ascii-font": ASCIIFontRenderable,
95
128
  "tab-select": TabSelectRenderable,
@@ -390,41 +423,21 @@ function _render(element, root) {
390
423
  reconciler.updateContainer(element, container, null, () => {});
391
424
  }
392
425
 
393
- // src/components/error-boundary.tsx
394
- import React from "react";
395
-
396
- // jsx-dev-runtime.js
397
- import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
398
-
399
- // src/components/error-boundary.tsx
400
- class ErrorBoundary extends React.Component {
401
- constructor(props) {
402
- super(props);
403
- this.state = { hasError: false, error: null };
404
- }
405
- static getDerivedStateFromError(error) {
406
- return { hasError: true, error };
407
- }
408
- render() {
409
- if (this.state.hasError && this.state.error) {
410
- return /* @__PURE__ */ jsxDEV("box", {
411
- style: { flexDirection: "column", padding: 2 },
412
- children: /* @__PURE__ */ jsxDEV("text", {
413
- fg: "red",
414
- children: this.state.error.stack || this.state.error.message
415
- }, undefined, false, undefined, this)
416
- }, undefined, false, undefined, this);
417
- }
418
- return this.props.children;
419
- }
420
- }
421
-
422
426
  // src/reconciler/renderer.ts
423
427
  async function render(node, rendererConfig = {}) {
424
428
  const renderer = await createCliRenderer(rendererConfig);
425
429
  engine.attach(renderer);
426
430
  _render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
427
431
  }
432
+ function createRoot(renderer) {
433
+ return {
434
+ render: (node) => {
435
+ engine.attach(renderer);
436
+ _render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
437
+ }
438
+ };
439
+ }
428
440
  export {
429
- render
441
+ render,
442
+ createRoot
430
443
  };
@@ -1,4 +1,4 @@
1
- import type { ASCIIFontOptions, ASCIIFontRenderable, BaseRenderable, BoxOptions, BoxRenderable, InputRenderable, InputRenderableOptions, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextNodeOptions, TextNodeRenderable, TextOptions, TextRenderable } from "@opentui/core";
1
+ import type { ASCIIFontOptions, ASCIIFontRenderable, BaseRenderable, BoxOptions, BoxRenderable, CodeOptions, CodeRenderable, InputRenderable, InputRenderableOptions, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextareaOptions, TextareaRenderable, TextNodeOptions, TextNodeRenderable, TextOptions, TextRenderable } from "@opentui/core";
2
2
  import type React from "react";
3
3
  /** Properties that should not be included in the style prop */
4
4
  export type NonStyledProps = "id" | "buffered" | "live" | "enableLayout" | "selectable" | "renderAfter" | "renderBefore" | `on${string}`;
@@ -14,7 +14,7 @@ type ExtractRenderableOptions<TConstructor> = TConstructor extends new (ctx: Ren
14
14
  /** Extract the renderable type from a constructor */
15
15
  type ExtractRenderable<TConstructor> = TConstructor extends new (ctx: RenderContext, options: any) => infer TRenderable ? TRenderable : never;
16
16
  /** Determine which properties should be excluded from styling for different renderable types */
17
- export type GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "placeholder" | "value" : NonStyledProps;
17
+ export type GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "placeholder" | "value" : TConstructor extends RenderableConstructor<TextareaRenderable> ? NonStyledProps | "placeholder" | "initialValue" : TConstructor extends RenderableConstructor<CodeRenderable> ? NonStyledProps | "content" | "filetype" | "syntaxStyle" | "treeSitterClient" | "conceal" | "drawUnstyledText" : NonStyledProps;
18
18
  /** Base props for container components that accept children */
19
19
  type ContainerProps<TOptions> = TOptions & {
20
20
  children?: React.ReactNode;
@@ -39,6 +39,10 @@ export type InputProps = ComponentProps<InputRenderableOptions, InputRenderable>
39
39
  onChange?: (value: string) => void;
40
40
  onSubmit?: (value: string) => void;
41
41
  };
42
+ export type TextareaProps = ComponentProps<TextareaOptions, TextareaRenderable> & {
43
+ focused?: boolean;
44
+ };
45
+ export type CodeProps = ComponentProps<CodeOptions, CodeRenderable>;
42
46
  export type SelectProps = ComponentProps<SelectRenderableOptions, SelectRenderable> & {
43
47
  focused?: boolean;
44
48
  onChange?: (index: number, option: SelectOption | null) => void;