paintcannon-react 0.0.1

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 (58) hide show
  1. package/README.md +213 -0
  2. package/dist/src/hooks/app-context.d.ts +8 -0
  3. package/dist/src/hooks/app-context.js +2 -0
  4. package/dist/src/hooks/index.d.ts +2 -0
  5. package/dist/src/hooks/index.js +2 -0
  6. package/dist/src/hooks/use-animation.d.ts +35 -0
  7. package/dist/src/hooks/use-animation.js +120 -0
  8. package/dist/src/hooks/use-app.d.ts +9 -0
  9. package/dist/src/hooks/use-app.js +9 -0
  10. package/dist/src/host-components/button/button.d.ts +8 -0
  11. package/dist/src/host-components/button/button.js +3 -0
  12. package/dist/src/host-components/button/reconcile-button.d.ts +6 -0
  13. package/dist/src/host-components/button/reconcile-button.js +14 -0
  14. package/dist/src/host-components/button.d.ts +8 -0
  15. package/dist/src/host-components/button.js +3 -0
  16. package/dist/src/host-components/div/div.d.ts +6 -0
  17. package/dist/src/host-components/div/div.js +3 -0
  18. package/dist/src/host-components/div/reconcile-div.d.ts +6 -0
  19. package/dist/src/host-components/div/reconcile-div.js +10 -0
  20. package/dist/src/host-components/div.d.ts +6 -0
  21. package/dist/src/host-components/div.js +3 -0
  22. package/dist/src/host-components/form/form.d.ts +6 -0
  23. package/dist/src/host-components/form/form.js +3 -0
  24. package/dist/src/host-components/form/reconcile-form.d.ts +6 -0
  25. package/dist/src/host-components/form/reconcile-form.js +10 -0
  26. package/dist/src/host-components/form.d.ts +6 -0
  27. package/dist/src/host-components/form.js +3 -0
  28. package/dist/src/host-components/index.d.ts +14 -0
  29. package/dist/src/host-components/index.js +14 -0
  30. package/dist/src/host-components/input/input.d.ts +8 -0
  31. package/dist/src/host-components/input/input.js +3 -0
  32. package/dist/src/host-components/input/reconcile-input.d.ts +6 -0
  33. package/dist/src/host-components/input/reconcile-input.js +14 -0
  34. package/dist/src/host-components/input.d.ts +8 -0
  35. package/dist/src/host-components/input.js +3 -0
  36. package/dist/src/host-components/mounted.d.ts +38 -0
  37. package/dist/src/host-components/mounted.js +17 -0
  38. package/dist/src/host-components/reconcile-shared.d.ts +8 -0
  39. package/dist/src/host-components/reconcile-shared.js +26 -0
  40. package/dist/src/host-components/shared.d.ts +40 -0
  41. package/dist/src/host-components/shared.js +18 -0
  42. package/dist/src/host-components/span/reconcile-span.d.ts +6 -0
  43. package/dist/src/host-components/span/reconcile-span.js +10 -0
  44. package/dist/src/host-components/span/span.d.ts +6 -0
  45. package/dist/src/host-components/span/span.js +3 -0
  46. package/dist/src/host-components/span.d.ts +6 -0
  47. package/dist/src/host-components/span.js +3 -0
  48. package/dist/src/host-components/textarea/reconcile-textarea.d.ts +6 -0
  49. package/dist/src/host-components/textarea/reconcile-textarea.js +12 -0
  50. package/dist/src/host-components/textarea/textarea.d.ts +8 -0
  51. package/dist/src/host-components/textarea/textarea.js +3 -0
  52. package/dist/src/host-components/textarea.d.ts +8 -0
  53. package/dist/src/host-components/textarea.js +3 -0
  54. package/dist/src/index.d.ts +21 -0
  55. package/dist/src/index.js +9 -0
  56. package/dist/src/reconciler.d.ts +17 -0
  57. package/dist/src/reconciler.js +403 -0
  58. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # paintcannon-react
2
+
3
+ A React reconciler for `paintcannon`, built for fast terminal UIs.
4
+
5
+ `paintcannon-react` lets you render React components into PaintCannon's
6
+ Rust-backed terminal renderer. It is intended as a faster, no-flicker
7
+ alternative for terminal React interfaces that would otherwise use Ink.
8
+
9
+ ![A cannon shooting paint](https://raw.githubusercontent.com/synthetic-lab/paintcannon/refs/heads/main/paintcannon-shot.png)
10
+
11
+ ## Installation
12
+
13
+ `paintcannon` and `react` are peer dependencies, so install them alongside the
14
+ reconciler:
15
+
16
+ ```bash
17
+ npm install --save paintcannon paintcannon-react react
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```tsx
23
+ import React, { useState } from "react";
24
+ import { Button, Div, Span, render } from "paintcannon-react";
25
+
26
+ function Counter(): React.ReactElement {
27
+ const [count, setCount] = useState(0);
28
+
29
+ return (
30
+ <Div
31
+ style={{
32
+ width: "100%",
33
+ height: "100%",
34
+ display: "flex",
35
+ flexDirection: "column",
36
+ alignItems: "center",
37
+ justifyContent: "center",
38
+ gap: 1,
39
+ backgroundColor: "#020617",
40
+ color: "#e2e8f0",
41
+ }}
42
+ >
43
+ <Span style={{ color: "#38bdf8" }}>paintcannon-react</Span>
44
+ <Button
45
+ style={{
46
+ border: "chunky-rounded",
47
+ borderColor: "#fb923c",
48
+ backgroundColor: count % 2 === 0 ? "#0f172a" : "#7c2d12",
49
+ color: "#f8fafc",
50
+ padding: "1 2",
51
+ cursor: "pointer",
52
+ }}
53
+ onClick={() => {
54
+ setCount(value => value + 1);
55
+ }}
56
+ >
57
+ Clicked {count} times
58
+ </Button>
59
+ </Div>
60
+ );
61
+ }
62
+
63
+ const app = render(<Counter />, {
64
+ alternateScreen: true,
65
+ captureMouse: true,
66
+ });
67
+
68
+ await app.waitUntilExit();
69
+ ```
70
+
71
+ ## API differences from Ink
72
+
73
+ PaintCannon-React isn't 100% drop-in compatible with Ink: the primary
74
+ difference is that PaintCannon expects you to use PaintCannon's native
75
+ `<input>` and `<textarea>` components for input, rather than relying on custom
76
+ JS reimplementations of input handling on top of React. Although this is
77
+ _different_ thank Ink, this comes with a significant performance win: typing in
78
+ PaintCannon is much lower latency as a result.
79
+
80
+ PaintCannon is also somewhat less restrictive thank Ink: you aren't required to
81
+ wrap text content in `<Text>` nodes. Text content Just Works inside PaintCannon
82
+ components, just like with a regular browser.
83
+
84
+ ## Host Components
85
+
86
+ PaintCannon exposes the following host components that mirror a subset of the
87
+ DOM API:
88
+
89
+ - `Div`
90
+ - `Span`
91
+ - `Input`
92
+ - `Textarea`
93
+ - `Button`
94
+ - `Form`
95
+
96
+ These components map to PaintCannon's DOM-like core API and support typed style
97
+ props, bubbling events, forms, focus handling, controlled inputs, and mouse
98
+ interactions. The subset of the React-DOM props they support is as follows:
99
+
100
+ ### Common props:
101
+
102
+ All host components accept:
103
+
104
+ - `children?: React.ReactNode`
105
+ - `style?: CSSStyleProperties`
106
+ - `ref?: React.Ref<Element>`
107
+
108
+ All host components accept these event props:
109
+
110
+ - `onKeyDown`
111
+ - `onKeyUp`
112
+ - `onClick`
113
+ - `onMouseEnter`
114
+ - `onMouseLeave`
115
+ - `onMouseMove`
116
+ - `onFocus`
117
+ - `onBlur`
118
+ - `onSubmit`
119
+ - `onChange`
120
+ - `onTransitionStart`
121
+ - `onTransitionEnd`
122
+ - `onScroll`
123
+
124
+ `style` accepts the following CSS property names. Kebab-case and camelCase are
125
+ both supported:
126
+
127
+ - `display`
128
+ - `overflow`
129
+ - `overflow-x` / `overflowX`
130
+ - `overflow-y` / `overflowY`
131
+ - `image-rendering` / `imageRendering`
132
+ - `flex-direction` / `flexDirection`
133
+ - `flex-wrap` / `flexWrap`
134
+ - `flex-flow` / `flexFlow`
135
+ - `flex-basis` / `flexBasis`
136
+ - `flex-grow` / `flexGrow`
137
+ - `flex-shrink` / `flexShrink`
138
+ - `flex`
139
+ - `justify-content` / `justifyContent`
140
+ - `align-items` / `alignItems`
141
+ - `align-self` / `alignSelf`
142
+ - `align-content` / `alignContent`
143
+ - `justify-items` / `justifyItems`
144
+ - `justify-self` / `justifySelf`
145
+ - `gap`
146
+ - `row-gap` / `rowGap`
147
+ - `column-gap` / `columnGap`
148
+ - `padding`
149
+ - `padding-top` / `paddingTop`
150
+ - `padding-right` / `paddingRight`
151
+ - `padding-bottom` / `paddingBottom`
152
+ - `padding-left` / `paddingLeft`
153
+ - `margin`
154
+ - `margin-top` / `marginTop`
155
+ - `margin-right` / `marginRight`
156
+ - `margin-bottom` / `marginBottom`
157
+ - `margin-left` / `marginLeft`
158
+ - `width`
159
+ - `height`
160
+ - `min-height` / `minHeight`
161
+ - `max-height` / `maxHeight`
162
+ - `white-space` / `whiteSpace`
163
+ - `border`
164
+ - `border-top` / `borderTop`
165
+ - `border-right` / `borderRight`
166
+ - `border-bottom` / `borderBottom`
167
+ - `border-left` / `borderLeft`
168
+ - `border-color` / `borderColor`
169
+ - `color`
170
+ - `placeholder-color` / `placeholderColor`
171
+ - `transition`
172
+ - `background`
173
+ - `background-color` / `backgroundColor`
174
+ - `selection-background-color` / `selectionBackgroundColor`
175
+ - `cursor`
176
+ - `grid-template-columns` / `gridTemplateColumns`
177
+ - `grid-template-rows` / `gridTemplateRows`
178
+ - `grid-auto-columns` / `gridAutoColumns`
179
+ - `grid-auto-rows` / `gridAutoRows`
180
+ - `grid-auto-flow` / `gridAutoFlow`
181
+ - `grid-column` / `gridColumn`
182
+ - `grid-row` / `gridRow`
183
+ - `grid-column-start` / `gridColumnStart`
184
+ - `grid-column-end` / `gridColumnEnd`
185
+ - `grid-row-start` / `gridRowStart`
186
+ - `grid-row-end` / `gridRowEnd`
187
+
188
+ ### Per-component props:
189
+
190
+ - `Div`: shared props, plus `scrollLeft?: number` and `scrollTop?: number`.
191
+ - `Span`: shared props, plus `scrollLeft?: number` and `scrollTop?: number`.
192
+ - `Form`: shared props, plus `scrollLeft?: number` and `scrollTop?: number`.
193
+ - `Button`: shared props, plus `type?: "submit" | "button"`,
194
+ `scrollLeft?: number`, and `scrollTop?: number`.
195
+ - `Input`: shared props, plus `type?: "text"`, `value?: string`,
196
+ `placeholder?: string`, `cursorPosition?: number`, and
197
+ `autoFocus?: boolean`.
198
+ - `Textarea`: shared props, plus `value?: string`, `placeholder?: string`,
199
+ `cursorPosition?: number`, `autoFocus?: boolean`, `scrollLeft?: number`, and
200
+ `scrollTop?: number`. `Textarea` does not accept a `type` prop.
201
+
202
+ ## Hooks
203
+
204
+ `paintcannon-react` includes:
205
+
206
+ - `useApp()` for exiting the rendered app from inside React.
207
+ - `useAnimation()` for requestAnimationFrame-driven animations that share a
208
+ single render cycle.
209
+
210
+ ## Core Runtime
211
+
212
+ This package depends on `paintcannon`, which provides the NAPI-RS native
213
+ renderer and DOM-like API.
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import type { PaintCannon } from 'paintcannon';
3
+ export type PaintCannonReactApp = {
4
+ readonly paintCannon: PaintCannon;
5
+ exit(errorOrResult?: unknown): void;
6
+ waitUntilExit(): Promise<unknown>;
7
+ };
8
+ export declare const AppContext: React.Context<PaintCannonReactApp | undefined>;
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export const AppContext = React.createContext(undefined);
@@ -0,0 +1,2 @@
1
+ export { AppContext, useApp, type PaintCannonReactApp } from "./use-app.ts";
2
+ export { AnimationContext, AnimationScheduler, useAnimation, type AnimationOptions, type AnimationResult, } from "./use-animation.ts";
@@ -0,0 +1,2 @@
1
+ export { AppContext, useApp } from "./use-app.js";
2
+ export { AnimationContext, AnimationScheduler, useAnimation, } from "./use-animation.js";
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+ import type { PaintCannon } from "paintcannon";
3
+ type AnimationCallback = (currentTime: number) => void;
4
+ type AnimationSubscription = {
5
+ startTime: number;
6
+ unsubscribe(): void;
7
+ };
8
+ type AnimationContextValue = {
9
+ subscribe(callback: AnimationCallback, interval: number | undefined): AnimationSubscription;
10
+ };
11
+ export type AnimationOptions = {
12
+ interval?: number;
13
+ isActive?: boolean;
14
+ };
15
+ export type AnimationResult = {
16
+ readonly frame: number;
17
+ readonly time: number;
18
+ readonly delta: number;
19
+ readonly reset: () => void;
20
+ };
21
+ export declare const AnimationContext: React.Context<AnimationContextValue | undefined>;
22
+ export declare function useAnimation(options?: AnimationOptions): AnimationResult;
23
+ export declare class AnimationScheduler implements AnimationContextValue {
24
+ private readonly paintCannon;
25
+ private nextId;
26
+ private animationFrameId;
27
+ private readonly subscribers;
28
+ subscribe(callback: AnimationCallback, interval: number | undefined): AnimationSubscription;
29
+ constructor(paintCannon: PaintCannon);
30
+ stop(): void;
31
+ private scheduleAnimationFrame;
32
+ private cancelAnimationFrame;
33
+ private tick;
34
+ }
35
+ export {};
@@ -0,0 +1,120 @@
1
+ import React from "react";
2
+ export const AnimationContext = React.createContext(undefined);
3
+ const maximumTimerInterval = 2_147_483_647;
4
+ const zeroAnimationState = { frame: 0, time: 0, delta: 0 };
5
+ export function useAnimation(options = {}) {
6
+ const { interval, isActive = true } = options;
7
+ const safeInterval = interval === undefined ? undefined : normalizeAnimationInterval(interval);
8
+ const animation = React.useContext(AnimationContext);
9
+ if (animation === undefined) {
10
+ throw new Error("useAnimation() must be used inside a paintcannon-react render tree");
11
+ }
12
+ const [resetKey, setResetKey] = React.useState(0);
13
+ const [state, setState] = React.useState(zeroAnimationState);
14
+ const lastRenderTimeRef = React.useRef(0);
15
+ const previousOptionsRef = React.useRef({ isActive, safeInterval, resetKey });
16
+ const previousOptions = previousOptionsRef.current;
17
+ const shouldReset = isActive &&
18
+ (safeInterval !== previousOptions.safeInterval ||
19
+ !previousOptions.isActive ||
20
+ resetKey !== previousOptions.resetKey);
21
+ const reset = React.useCallback(() => {
22
+ setResetKey(value => value + 1);
23
+ }, []);
24
+ React.useLayoutEffect(() => {
25
+ if (!isActive) {
26
+ return undefined;
27
+ }
28
+ setState(zeroAnimationState);
29
+ let startTime = 0;
30
+ const subscription = animation.subscribe(currentTime => {
31
+ const elapsed = currentTime - startTime;
32
+ const delta = currentTime - lastRenderTimeRef.current;
33
+ lastRenderTimeRef.current = currentTime;
34
+ setState(previous => ({
35
+ frame: safeInterval === undefined ? previous.frame + 1 : Math.floor(elapsed / safeInterval),
36
+ time: elapsed,
37
+ delta,
38
+ }));
39
+ }, safeInterval);
40
+ startTime = subscription.startTime;
41
+ lastRenderTimeRef.current = subscription.startTime;
42
+ return subscription.unsubscribe;
43
+ }, [animation, isActive, safeInterval, resetKey]);
44
+ React.useLayoutEffect(() => {
45
+ previousOptionsRef.current = { isActive, safeInterval, resetKey };
46
+ }, [isActive, safeInterval, resetKey]);
47
+ if (shouldReset) {
48
+ return { ...zeroAnimationState, reset };
49
+ }
50
+ return { ...state, reset };
51
+ }
52
+ export class AnimationScheduler {
53
+ paintCannon;
54
+ nextId = 1;
55
+ animationFrameId;
56
+ subscribers = new Map();
57
+ subscribe(callback, interval) {
58
+ const id = this.nextId;
59
+ this.nextId += 1;
60
+ const startTime = performance.now();
61
+ this.subscribers.set(id, {
62
+ callback,
63
+ interval,
64
+ nextTime: interval === undefined ? startTime : startTime + interval,
65
+ });
66
+ this.scheduleAnimationFrame();
67
+ return {
68
+ startTime,
69
+ unsubscribe: () => {
70
+ this.subscribers.delete(id);
71
+ if (this.subscribers.size === 0) {
72
+ this.cancelAnimationFrame();
73
+ }
74
+ },
75
+ };
76
+ }
77
+ constructor(paintCannon) {
78
+ this.paintCannon = paintCannon;
79
+ }
80
+ stop() {
81
+ this.cancelAnimationFrame();
82
+ this.subscribers.clear();
83
+ }
84
+ scheduleAnimationFrame() {
85
+ if (this.animationFrameId !== undefined || this.subscribers.size === 0) {
86
+ return;
87
+ }
88
+ this.animationFrameId = this.paintCannon.requestAnimationFrame(timestamp => {
89
+ this.animationFrameId = undefined;
90
+ this.tick(timestamp);
91
+ });
92
+ }
93
+ cancelAnimationFrame() {
94
+ if (this.animationFrameId === undefined) {
95
+ return;
96
+ }
97
+ this.paintCannon.cancelAnimationFrame(this.animationFrameId);
98
+ this.animationFrameId = undefined;
99
+ }
100
+ tick(currentTime) {
101
+ for (const subscriber of this.subscribers.values()) {
102
+ if (subscriber.interval === undefined) {
103
+ subscriber.callback(currentTime);
104
+ continue;
105
+ }
106
+ if (subscriber.nextTime <= currentTime) {
107
+ const intervalsElapsed = Math.max(1, Math.floor((currentTime - subscriber.nextTime) / subscriber.interval) + 1);
108
+ subscriber.nextTime += subscriber.interval * intervalsElapsed;
109
+ subscriber.callback(currentTime);
110
+ }
111
+ }
112
+ this.scheduleAnimationFrame();
113
+ }
114
+ }
115
+ function normalizeAnimationInterval(interval) {
116
+ if (!Number.isFinite(interval)) {
117
+ return maximumTimerInterval;
118
+ }
119
+ return Math.min(maximumTimerInterval, Math.max(1, interval));
120
+ }
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { PaintCannon } from "paintcannon";
3
+ export type PaintCannonReactApp = {
4
+ readonly paintCannon: PaintCannon;
5
+ exit(errorOrResult?: unknown): void;
6
+ waitUntilExit(): Promise<unknown>;
7
+ };
8
+ export declare const AppContext: React.Context<PaintCannonReactApp | undefined>;
9
+ export declare function useApp(): PaintCannonReactApp;
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ export const AppContext = React.createContext(undefined);
3
+ export function useApp() {
4
+ const app = React.useContext(AppContext);
5
+ if (app === undefined) {
6
+ throw new Error("useApp() must be used inside a paintcannon-react render tree");
7
+ }
8
+ return app;
9
+ }
@@ -0,0 +1,8 @@
1
+ import type { ButtonElement as PaintButtonElement } from "paintcannon";
2
+ import type { CommonProps, HostComponent, Scrollable } from "../shared.ts";
3
+ export type Props = CommonProps & Partial<Scrollable> & {
4
+ type?: "submit" | "button";
5
+ };
6
+ export type Element = PaintButtonElement;
7
+ export declare const type: "paintcannon.button";
8
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "../shared.js";
2
+ export const type = typeString("paintcannon.button");
3
+ export const Component = type;
@@ -0,0 +1,6 @@
1
+ import type { PaintCannon } from "paintcannon";
2
+ import * as button from "./button.ts";
3
+ import type { MountedComponent } from "../mounted.ts";
4
+ import { type ApplyCommonProps } from "../reconcile-shared.ts";
5
+ export declare function create(paintCannon: PaintCannon, props: button.Props, applyCommonProps: ApplyCommonProps): MountedComponent<typeof button>;
6
+ export declare function applyProps(node: button.Element, oldProps: Partial<button.Props>, newProps: button.Props, applyCommonProps: ApplyCommonProps): void;
@@ -0,0 +1,14 @@
1
+ import * as button from "./button.js";
2
+ import { applyScrollableProps } from "../reconcile-shared.js";
3
+ export function create(paintCannon, props, applyCommonProps) {
4
+ const node = paintCannon.createElement("button");
5
+ applyProps(node, {}, props, applyCommonProps);
6
+ return { kind: "element", type: button.type, props, children: [], node };
7
+ }
8
+ export function applyProps(node, oldProps, newProps, applyCommonProps) {
9
+ applyCommonProps(node, oldProps, newProps);
10
+ if (newProps.type !== undefined) {
11
+ node.type = newProps.type;
12
+ }
13
+ applyScrollableProps(node, newProps);
14
+ }
@@ -0,0 +1,8 @@
1
+ import type { ButtonElement as PaintButtonElement } from 'paintcannon';
2
+ import type { CommonProps, HostComponent, Scrollable } from './shared.ts';
3
+ export type Props = CommonProps & Partial<Scrollable> & {
4
+ type?: 'submit' | 'button';
5
+ };
6
+ export type Element = PaintButtonElement;
7
+ export declare const type: "paintcannon.button";
8
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "./shared.js";
2
+ export const type = typeString('paintcannon.button');
3
+ export const Component = type;
@@ -0,0 +1,6 @@
1
+ import type { DivElement as PaintDivElement } from "paintcannon";
2
+ import type { CommonProps, HostComponent, Scrollable } from "../shared.ts";
3
+ export type Props = CommonProps & Partial<Scrollable>;
4
+ export type Element = PaintDivElement;
5
+ export declare const type: "paintcannon.div";
6
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "../shared.js";
2
+ export const type = typeString("paintcannon.div");
3
+ export const Component = type;
@@ -0,0 +1,6 @@
1
+ import type { PaintCannon } from "paintcannon";
2
+ import * as div from "./div.ts";
3
+ import type { MountedComponent } from "../mounted.ts";
4
+ import { type ApplyCommonProps } from "../reconcile-shared.ts";
5
+ export declare function create(paintCannon: PaintCannon, props: div.Props, applyCommonProps: ApplyCommonProps): MountedComponent<typeof div>;
6
+ export declare function applyProps(node: div.Element, oldProps: Partial<div.Props>, newProps: div.Props, applyCommonProps: ApplyCommonProps): void;
@@ -0,0 +1,10 @@
1
+ import * as div from "./div.js";
2
+ import { applyScrollableElementProps } from "../reconcile-shared.js";
3
+ export function create(paintCannon, props, applyCommonProps) {
4
+ const node = paintCannon.createElement("div");
5
+ applyProps(node, {}, props, applyCommonProps);
6
+ return { kind: "element", type: div.type, props, children: [], node };
7
+ }
8
+ export function applyProps(node, oldProps, newProps, applyCommonProps) {
9
+ applyScrollableElementProps(node, oldProps, newProps, applyCommonProps);
10
+ }
@@ -0,0 +1,6 @@
1
+ import type { DivElement as PaintDivElement } from 'paintcannon';
2
+ import type { CommonProps, HostComponent, Scrollable } from './shared.ts';
3
+ export type Props = CommonProps & Partial<Scrollable>;
4
+ export type Element = PaintDivElement;
5
+ export declare const type: "paintcannon.div";
6
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "./shared.js";
2
+ export const type = typeString('paintcannon.div');
3
+ export const Component = type;
@@ -0,0 +1,6 @@
1
+ import type { FormElement as PaintFormElement } from "paintcannon";
2
+ import type { CommonProps, HostComponent, Scrollable } from "../shared.ts";
3
+ export type Props = CommonProps & Partial<Scrollable>;
4
+ export type Element = PaintFormElement;
5
+ export declare const type: "paintcannon.form";
6
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "../shared.js";
2
+ export const type = typeString("paintcannon.form");
3
+ export const Component = type;
@@ -0,0 +1,6 @@
1
+ import type { PaintCannon } from "paintcannon";
2
+ import * as form from "./form.ts";
3
+ import type { MountedComponent } from "../mounted.ts";
4
+ import { type ApplyCommonProps } from "../reconcile-shared.ts";
5
+ export declare function create(paintCannon: PaintCannon, props: form.Props, applyCommonProps: ApplyCommonProps): MountedComponent<typeof form>;
6
+ export declare function applyProps(node: form.Element, oldProps: Partial<form.Props>, newProps: form.Props, applyCommonProps: ApplyCommonProps): void;
@@ -0,0 +1,10 @@
1
+ import * as form from "./form.js";
2
+ import { applyScrollableElementProps } from "../reconcile-shared.js";
3
+ export function create(paintCannon, props, applyCommonProps) {
4
+ const node = paintCannon.createElement("form");
5
+ applyProps(node, {}, props, applyCommonProps);
6
+ return { kind: "element", type: form.type, props, children: [], node };
7
+ }
8
+ export function applyProps(node, oldProps, newProps, applyCommonProps) {
9
+ applyScrollableElementProps(node, oldProps, newProps, applyCommonProps);
10
+ }
@@ -0,0 +1,6 @@
1
+ import type { FormElement as PaintFormElement } from 'paintcannon';
2
+ import type { CommonProps, HostComponent, Scrollable } from './shared.ts';
3
+ export type Props = CommonProps & Partial<Scrollable>;
4
+ export type Element = PaintFormElement;
5
+ export declare const type: "paintcannon.form";
6
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "./shared.js";
2
+ export const type = typeString('paintcannon.form');
3
+ export const Component = type;
@@ -0,0 +1,14 @@
1
+ export * from "./shared.ts";
2
+ export * from "./mounted.ts";
3
+ export * as button from "./button/button.ts";
4
+ export * as div from "./div/div.ts";
5
+ export * as form from "./form/form.ts";
6
+ export * as input from "./input/input.ts";
7
+ export * as reconcileButton from "./button/reconcile-button.ts";
8
+ export * as reconcileDiv from "./div/reconcile-div.ts";
9
+ export * as reconcileForm from "./form/reconcile-form.ts";
10
+ export * as reconcileInput from "./input/reconcile-input.ts";
11
+ export * as reconcileSpan from "./span/reconcile-span.ts";
12
+ export * as reconcileTextarea from "./textarea/reconcile-textarea.ts";
13
+ export * as span from "./span/span.ts";
14
+ export * as textarea from "./textarea/textarea.ts";
@@ -0,0 +1,14 @@
1
+ export * from "./shared.js";
2
+ export * from "./mounted.js";
3
+ export * as button from "./button/button.js";
4
+ export * as div from "./div/div.js";
5
+ export * as form from "./form/form.js";
6
+ export * as input from "./input/input.js";
7
+ export * as reconcileButton from "./button/reconcile-button.js";
8
+ export * as reconcileDiv from "./div/reconcile-div.js";
9
+ export * as reconcileForm from "./form/reconcile-form.js";
10
+ export * as reconcileInput from "./input/reconcile-input.js";
11
+ export * as reconcileSpan from "./span/reconcile-span.js";
12
+ export * as reconcileTextarea from "./textarea/reconcile-textarea.js";
13
+ export * as span from "./span/span.js";
14
+ export * as textarea from "./textarea/textarea.js";
@@ -0,0 +1,8 @@
1
+ import type { InputElement as PaintInputElement } from "paintcannon";
2
+ import type { HostComponent, TextControlProps } from "../shared.ts";
3
+ export type Props = TextControlProps & {
4
+ type?: "text";
5
+ };
6
+ export type Element = PaintInputElement;
7
+ export declare const type: "paintcannon.input";
8
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "../shared.js";
2
+ export const type = typeString("paintcannon.input");
3
+ export const Component = type;
@@ -0,0 +1,6 @@
1
+ import type { PaintCannon } from "paintcannon";
2
+ import * as input from "./input.ts";
3
+ import type { MountedComponent } from "../mounted.ts";
4
+ import { type ApplyCommonProps } from "../reconcile-shared.ts";
5
+ export declare function create(paintCannon: PaintCannon, props: input.Props, applyCommonProps: ApplyCommonProps): MountedComponent<typeof input>;
6
+ export declare function applyProps(node: input.Element, oldProps: Partial<input.Props>, newProps: input.Props, applyCommonProps: ApplyCommonProps): void;
@@ -0,0 +1,14 @@
1
+ import * as input from "./input.js";
2
+ import { applyTextControlProps } from "../reconcile-shared.js";
3
+ export function create(paintCannon, props, applyCommonProps) {
4
+ const node = paintCannon.createElement("input");
5
+ applyProps(node, {}, props, applyCommonProps);
6
+ return { kind: "element", type: input.type, props, children: [], node };
7
+ }
8
+ export function applyProps(node, oldProps, newProps, applyCommonProps) {
9
+ applyCommonProps(node, oldProps, newProps);
10
+ if (newProps.type !== undefined) {
11
+ node.type = newProps.type;
12
+ }
13
+ applyTextControlProps(node, oldProps, newProps);
14
+ }
@@ -0,0 +1,8 @@
1
+ import type { InputElement as PaintInputElement } from 'paintcannon';
2
+ import type { HostComponent, TextControlProps } from './shared.ts';
3
+ export type Props = TextControlProps & {
4
+ type?: 'text';
5
+ };
6
+ export type Element = PaintInputElement;
7
+ export declare const type: "paintcannon.input";
8
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "./shared.js";
2
+ export const type = typeString('paintcannon.input');
3
+ export const Component = type;