paintcannon-react 0.0.41 → 0.0.43

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
@@ -88,6 +88,7 @@ DOM API:
88
88
 
89
89
  - `Div`
90
90
  - `Span`
91
+ - `A`
91
92
  - `Input`
92
93
  - `Textarea`
93
94
  - `Button`
@@ -112,6 +113,15 @@ app.paintCannon.addEventListener("blur", () => {
112
113
 
113
114
  Inside tmux, terminal focus reporting requires `set -g focus-events on`.
114
115
 
116
+ Font capabilities are available through `app.paintCannon.font`. Use
117
+ `hasNerdFontSet(set)` or `hasSymbolsForLegacyComputing()` for initial support,
118
+ then subscribe with `font.addEventListener("nerdfont-powerline-change", listener)`
119
+ or `"symbols-for-legacy-computing-change"` to update React state from
120
+ `event.supported`. Remove the listener in your effect's cleanup. Rust refreshes
121
+ support every two seconds, requires support in every attached tmux terminal
122
+ client, and repaints chunky borders automatically. See [font detection in
123
+ PaintCannon](../paintcannon/README.md#nerd-font-detection) for all sets and limits.
124
+
115
125
  ### Common props:
116
126
 
117
127
  All host components accept:
@@ -227,6 +237,7 @@ both supported:
227
237
 
228
238
  - `Div`: shared props, plus `scrollLeft?: number` and `scrollTop?: number`.
229
239
  - `Span`: shared props, plus `scrollLeft?: number` and `scrollTop?: number`.
240
+ - `A`: shared props, `scrollLeft?: number`, `scrollTop?: number`, and `href?: string`.
230
241
  - `Form`: shared props, plus `scrollLeft?: number` and `scrollTop?: number`.
231
242
  - `Button`: shared props, plus `type?: "submit" | "button"`,
232
243
  `scrollLeft?: number`, and `scrollTop?: number`.
@@ -0,0 +1,8 @@
1
+ import type { AnchorElement } from "paintcannon";
2
+ import type { CommonProps, HostComponent, Scrollable } from "../shared.ts";
3
+ export type Props = CommonProps & Partial<Scrollable> & {
4
+ href?: string;
5
+ };
6
+ export type Element = AnchorElement;
7
+ export declare const type: "paintcannon.a";
8
+ export declare const Component: HostComponent<Props, Element>;
@@ -0,0 +1,3 @@
1
+ import { typeString } from "../shared.js";
2
+ export const type = typeString("paintcannon.a");
3
+ export const Component = type;
@@ -0,0 +1,6 @@
1
+ import type { PaintCannon } from "paintcannon";
2
+ import * as a from "./a.ts";
3
+ import type { MountedComponent } from "../mounted.ts";
4
+ import { type ApplyCommonProps } from "../reconcile-shared.ts";
5
+ export declare function create(paintCannon: PaintCannon, props: a.Props, applyCommonProps: ApplyCommonProps): MountedComponent<typeof a>;
6
+ export declare function applyProps(node: a.Element, oldProps: Partial<a.Props>, newProps: a.Props, applyCommonProps: ApplyCommonProps): void;
@@ -0,0 +1,13 @@
1
+ import * as a from "./a.js";
2
+ import { applyScrollableElementProps } from "../reconcile-shared.js";
3
+ export function create(paintCannon, props, applyCommonProps) {
4
+ const node = paintCannon.createElement("a");
5
+ applyProps(node, {}, props, applyCommonProps);
6
+ return { kind: "element", type: a.type, props, children: new Set(), node };
7
+ }
8
+ export function applyProps(node, oldProps, newProps, applyCommonProps) {
9
+ applyScrollableElementProps(node, oldProps, newProps, applyCommonProps);
10
+ if (oldProps.href !== newProps.href) {
11
+ node.href = newProps.href ?? "";
12
+ }
13
+ }
@@ -12,3 +12,5 @@ export * as reconcileSpan from "./span/reconcile-span.ts";
12
12
  export * as reconcileTextarea from "./textarea/reconcile-textarea.ts";
13
13
  export * as span from "./span/span.ts";
14
14
  export * as textarea from "./textarea/textarea.ts";
15
+ export * as a from "./a/a.ts";
16
+ export * as reconcileA from "./a/reconcile-a.ts";
@@ -12,3 +12,5 @@ export * as reconcileSpan from "./span/reconcile-span.js";
12
12
  export * as reconcileTextarea from "./textarea/reconcile-textarea.js";
13
13
  export * as span from "./span/span.js";
14
14
  export * as textarea from "./textarea/textarea.js";
15
+ export * as a from "./a/a.js";
16
+ export * as reconcileA from "./a/reconcile-a.js";
@@ -1,4 +1,5 @@
1
1
  import type { PaintElement, TextNode } from "paintcannon";
2
+ import * as a from "./a/a.ts";
2
3
  import * as button from "./button/button.ts";
3
4
  import * as div from "./div/div.ts";
4
5
  import * as form from "./form/form.ts";
@@ -6,7 +7,7 @@ import * as input from "./input/input.ts";
6
7
  import * as span from "./span/span.ts";
7
8
  import * as textarea from "./textarea/textarea.ts";
8
9
  import type { HostComponent, HostType } from "./shared.ts";
9
- export type HostProps = div.Props | span.Props | form.Props | button.Props | input.Props | textarea.Props;
10
+ export type HostProps = a.Props | div.Props | span.Props | form.Props | button.Props | input.Props | textarea.Props;
10
11
  export type MountedHostElement<Type extends HostType, Props, Element extends PaintElement> = {
11
12
  kind: "element";
12
13
  type: Type;
@@ -19,7 +20,7 @@ export type MountedComponent<Module> = Module extends {
19
20
  type: infer Type extends HostType;
20
21
  Component: HostComponent<infer Props, infer Element extends PaintElement>;
21
22
  } ? MountedHostElement<Type, Props, Element> : never;
22
- export type HostElement = MountedComponent<typeof div> | MountedComponent<typeof span> | MountedComponent<typeof form> | MountedComponent<typeof button> | MountedComponent<typeof input> | MountedComponent<typeof textarea>;
23
+ export type HostElement = MountedComponent<typeof a> | MountedComponent<typeof div> | MountedComponent<typeof span> | MountedComponent<typeof form> | MountedComponent<typeof button> | MountedComponent<typeof input> | MountedComponent<typeof textarea>;
23
24
  export type HostText = {
24
25
  kind: "text";
25
26
  text: string;
@@ -31,6 +32,7 @@ export type HostParent = {
31
32
  children: Set<HostNode>;
32
33
  };
33
34
  declare const hostPropsForType: {
35
+ "paintcannon.a": a.Props;
34
36
  "paintcannon.div": div.Props;
35
37
  "paintcannon.span": span.Props;
36
38
  "paintcannon.form": form.Props;
@@ -1,3 +1,4 @@
1
+ import * as a from "./a/a.js";
1
2
  import * as button from "./button/button.js";
2
3
  import * as div from "./div/div.js";
3
4
  import * as form from "./form/form.js";
@@ -5,6 +6,7 @@ import * as input from "./input/input.js";
5
6
  import * as span from "./span/span.js";
6
7
  import * as textarea from "./textarea/textarea.js";
7
8
  const hostPropsForType = {
9
+ [a.type]: undefined,
8
10
  [div.type]: undefined,
9
11
  [span.type]: undefined,
10
12
  [form.type]: undefined,
@@ -5,4 +5,4 @@ import type { CommonProps, Scrollable } from "./shared.ts";
5
5
  export type ApplyCommonProps = (node: PaintElement, oldProps: Partial<CommonProps>, newProps: CommonProps) => void;
6
6
  export declare function applyScrollableElementProps<Props extends CommonProps & Partial<Scrollable>>(node: PaintElement & Scrollable, oldProps: Partial<Props>, newProps: Props, applyCommonProps: ApplyCommonProps): void;
7
7
  export declare function applyScrollableProps(node: Scrollable, props: Partial<Scrollable>): void;
8
- export declare function applyTextControlProps<T extends InputElement | TextAreaElement>(node: T, oldProps: Partial<input.Props | textarea.Props>, newProps: input.Props | textarea.Props): void;
8
+ export declare function applyTextControlProps<T extends InputElement | TextAreaElement>(node: T, _oldProps: Partial<input.Props | textarea.Props>, newProps: input.Props | textarea.Props): void;
@@ -10,7 +10,7 @@ export function applyScrollableProps(node, props) {
10
10
  node.scrollTop = props.scrollTop;
11
11
  }
12
12
  }
13
- export function applyTextControlProps(node, oldProps, newProps) {
13
+ export function applyTextControlProps(node, _oldProps, newProps) {
14
14
  if (newProps.value !== undefined) {
15
15
  node.value = newProps.value;
16
16
  }
@@ -20,7 +20,4 @@ export function applyTextControlProps(node, oldProps, newProps) {
20
20
  if (newProps.cursorPosition !== undefined) {
21
21
  node.cursorPosition = newProps.cursorPosition;
22
22
  }
23
- if (newProps.autoFocus === true && oldProps.autoFocus !== true) {
24
- node.focus();
25
- }
26
23
  }
@@ -31,11 +31,11 @@ type ElementEventProps = {
31
31
  export type CommonProps = ElementEventProps & {
32
32
  children?: React.ReactNode;
33
33
  style?: CSSStyleProperties;
34
+ autoFocus?: boolean;
34
35
  };
35
36
  export type TextControlProps = CommonProps & {
36
37
  value?: string;
37
38
  placeholder?: string;
38
39
  cursorPosition?: number;
39
- autoFocus?: boolean;
40
40
  };
41
41
  export {};
@@ -19,3 +19,6 @@ export declare const Input: hostComponents.HostComponent<hostComponents.input.Pr
19
19
  export type TextareaProps = hostComponents.textarea.Props;
20
20
  export type TextareaElement = hostComponents.textarea.Element;
21
21
  export declare const Textarea: hostComponents.HostComponent<hostComponents.textarea.Props, import("paintcannon").TextAreaElement>;
22
+ export type AProps = hostComponents.a.Props;
23
+ export type AnchorElement = hostComponents.a.Element;
24
+ export declare const A: hostComponents.HostComponent<hostComponents.a.Props, import("paintcannon").AnchorElement>;
package/dist/src/index.js CHANGED
@@ -7,3 +7,4 @@ export const Form = hostComponents.form.Component;
7
7
  export const Button = hostComponents.button.Component;
8
8
  export const Input = hostComponents.input.Component;
9
9
  export const Textarea = hostComponents.textarea.Component;
10
+ export const A = hostComponents.a.Component;
@@ -70,7 +70,10 @@ const reconciler = createReconciler({
70
70
  removeVirtualChild(container, child);
71
71
  destroyHostNode(child);
72
72
  },
73
- finalizeInitialChildren: () => false,
73
+ finalizeInitialChildren: (_instance, _type, props) => props.autoFocus === true,
74
+ commitMount(instance) {
75
+ instance.node.focus();
76
+ },
74
77
  resetTextContent: () => { },
75
78
  getPublicInstance(instance) {
76
79
  return instance.node;
@@ -229,6 +232,9 @@ function createHostElement(paintCannon, type, props) {
229
232
  if (type === hostComponents.div.type) {
230
233
  return hostComponents.reconcileDiv.create(paintCannon, hostComponents.castHostProps(type, props), applyCommonProps);
231
234
  }
235
+ if (type === hostComponents.a.type) {
236
+ return hostComponents.reconcileA.create(paintCannon, hostComponents.castHostProps(type, props), applyCommonProps);
237
+ }
232
238
  if (type === hostComponents.span.type) {
233
239
  return hostComponents.reconcileSpan.create(paintCannon, hostComponents.castHostProps(type, props), applyCommonProps);
234
240
  }
@@ -268,6 +274,11 @@ function applyHostElementProps(host, newProps) {
268
274
  hostComponents.reconcileDiv.applyProps(host.node, host.props, props, applyCommonProps);
269
275
  return props;
270
276
  }
277
+ if (host.type === hostComponents.a.type) {
278
+ const props = hostComponents.castHostProps(host.type, newProps);
279
+ hostComponents.reconcileA.applyProps(host.node, host.props, props, applyCommonProps);
280
+ return props;
281
+ }
271
282
  if (host.type === hostComponents.span.type) {
272
283
  const props = hostComponents.castHostProps(host.type, newProps);
273
284
  hostComponents.reconcileSpan.applyProps(host.node, host.props, props, applyCommonProps);
@@ -359,7 +370,7 @@ function reactEventListener(listener) {
359
370
  return wrapped;
360
371
  }
361
372
  function defaultDisplay(type) {
362
- return type === hostComponents.span.type ? "inline" : "block";
373
+ return type === hostComponents.span.type || type === hostComponents.a.type ? "inline" : "block";
363
374
  }
364
375
  function reportReactError(error) {
365
376
  if (error !== null && error !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paintcannon-react",
3
- "version": "0.0.41",
3
+ "version": "0.0.43",
4
4
  "license": "ISC",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,6 +23,7 @@
23
23
  "build": "tsc",
24
24
  "demo": "npm run build && node dist/examples/counter.js",
25
25
  "demo:controlled": "npm run build && node dist/examples/controlled.js",
26
+ "demo:emoji-width": "npm run build && node dist/examples/emoji-width.js",
26
27
  "demo:form": "npm run build && node dist/examples/form.js",
27
28
  "demo:keyboard": "npm run build && node dist/examples/keyboard.js",
28
29
  "demo:lifecycle": "npm run build && node dist/examples/lifecycle.js",
@@ -36,7 +37,7 @@
36
37
  "scheduler": "^0.25.0"
37
38
  },
38
39
  "peerDependencies": {
39
- "paintcannon": "^0.0.41",
40
+ "paintcannon": "^0.0.43",
40
41
  "react": "^19.0.0"
41
42
  },
42
43
  "devDependencies": {