foldkit 0.133.0 → 0.135.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 (88) hide show
  1. package/README.md +13 -17
  2. package/dist/canvas/view.d.ts +18 -11
  3. package/dist/canvas/view.d.ts.map +1 -1
  4. package/dist/canvas/view.js +18 -11
  5. package/dist/customElement/index.d.ts +12 -6
  6. package/dist/customElement/index.d.ts.map +1 -1
  7. package/dist/customElement/index.js +17 -4
  8. package/dist/html/index.d.ts +625 -1601
  9. package/dist/html/index.d.ts.map +1 -1
  10. package/dist/html/index.js +52 -15
  11. package/dist/html/public.d.ts +2 -2
  12. package/dist/html/public.d.ts.map +1 -1
  13. package/dist/html/public.js +1 -1
  14. package/dist/html/runtimeSingleton.d.ts +1 -1
  15. package/dist/html/runtimeSingleton.d.ts.map +1 -1
  16. package/dist/html/runtimeSingleton.js +1 -1
  17. package/dist/html/submodel.d.ts +51 -19
  18. package/dist/html/submodel.d.ts.map +1 -1
  19. package/dist/html/submodel.js +37 -13
  20. package/dist/managedResource/managedResource.d.ts +21 -10
  21. package/dist/managedResource/managedResource.d.ts.map +1 -1
  22. package/dist/managedResource/managedResource.js +6 -1
  23. package/dist/runtime/crashUI.d.ts.map +1 -1
  24. package/dist/runtime/crashUI.js +29 -30
  25. package/dist/runtime/runtime.d.ts +9 -6
  26. package/dist/runtime/runtime.d.ts.map +1 -1
  27. package/dist/runtime/runtime.js +9 -8
  28. package/dist/test/apps/attributes.d.ts +2 -2
  29. package/dist/test/apps/attributes.d.ts.map +1 -1
  30. package/dist/test/apps/attributes.js +1 -3
  31. package/dist/test/apps/bubbling.d.ts +2 -2
  32. package/dist/test/apps/bubbling.d.ts.map +1 -1
  33. package/dist/test/apps/bubbling.js +1 -3
  34. package/dist/test/apps/colorPicker.d.ts +28 -0
  35. package/dist/test/apps/colorPicker.d.ts.map +1 -0
  36. package/dist/test/apps/colorPicker.js +43 -0
  37. package/dist/test/apps/counter.d.ts +5 -3
  38. package/dist/test/apps/counter.d.ts.map +1 -1
  39. package/dist/test/apps/counter.js +12 -7
  40. package/dist/test/apps/crashOnRender.d.ts +2 -2
  41. package/dist/test/apps/crashOnRender.d.ts.map +1 -1
  42. package/dist/test/apps/crashOnRender.js +1 -3
  43. package/dist/test/apps/drafts.d.ts +2 -2
  44. package/dist/test/apps/drafts.d.ts.map +1 -1
  45. package/dist/test/apps/drafts.js +1 -3
  46. package/dist/test/apps/feedSocket.d.ts +74 -0
  47. package/dist/test/apps/feedSocket.d.ts.map +1 -0
  48. package/dist/test/apps/feedSocket.js +69 -0
  49. package/dist/test/apps/fileUpload.d.ts +2 -2
  50. package/dist/test/apps/fileUpload.d.ts.map +1 -1
  51. package/dist/test/apps/fileUpload.js +1 -3
  52. package/dist/test/apps/interactions.d.ts +2 -2
  53. package/dist/test/apps/interactions.d.ts.map +1 -1
  54. package/dist/test/apps/interactions.js +1 -3
  55. package/dist/test/apps/keypress.d.ts +2 -2
  56. package/dist/test/apps/keypress.d.ts.map +1 -1
  57. package/dist/test/apps/keypress.js +1 -3
  58. package/dist/test/apps/login.d.ts +2 -2
  59. package/dist/test/apps/login.d.ts.map +1 -1
  60. package/dist/test/apps/login.js +1 -3
  61. package/dist/test/apps/logoutButton.d.ts +2 -2
  62. package/dist/test/apps/logoutButton.d.ts.map +1 -1
  63. package/dist/test/apps/logoutButton.js +1 -3
  64. package/dist/test/apps/mountPanel.d.ts +4 -4
  65. package/dist/test/apps/mountPanel.d.ts.map +1 -1
  66. package/dist/test/apps/mountPanel.js +3 -7
  67. package/dist/test/apps/multiRole.d.ts +2 -2
  68. package/dist/test/apps/multiRole.d.ts.map +1 -1
  69. package/dist/test/apps/multiRole.js +1 -3
  70. package/dist/test/apps/pointer.d.ts +2 -2
  71. package/dist/test/apps/pointer.d.ts.map +1 -1
  72. package/dist/test/apps/pointer.js +1 -3
  73. package/dist/test/apps/resumeUpload.d.ts +2 -2
  74. package/dist/test/apps/resumeUpload.d.ts.map +1 -1
  75. package/dist/test/apps/resumeUpload.js +3 -6
  76. package/dist/test/apps/scorePanel.d.ts +32 -0
  77. package/dist/test/apps/scorePanel.d.ts.map +1 -0
  78. package/dist/test/apps/scorePanel.js +19 -0
  79. package/dist/test/apps/uploads.d.ts +2 -2
  80. package/dist/test/apps/uploads.d.ts.map +1 -1
  81. package/dist/test/apps/uploads.js +1 -3
  82. package/dist/test/scene.d.ts +81 -9
  83. package/dist/test/scene.d.ts.map +1 -1
  84. package/dist/test/scene.js +206 -26
  85. package/dist/test/story.d.ts +4 -5
  86. package/dist/test/story.d.ts.map +1 -1
  87. package/dist/test/story.js +1 -2
  88. package/package.json +1 -1
package/README.md CHANGED
@@ -41,7 +41,7 @@ A complete Foldkit program. State lives in a single Model, events become Message
41
41
  // src/main.ts
42
42
  import { Match as M, Schema as S } from 'effect'
43
43
  import { Command, Runtime } from 'foldkit'
44
- import { Document, html } from 'foldkit/html'
44
+ import { Document, HtmlBuilder } from 'foldkit/html'
45
45
  import { m } from 'foldkit/message'
46
46
 
47
47
  // MODEL
@@ -88,22 +88,18 @@ export const init: Runtime.ApplicationInit<Model, Message> = () => [
88
88
 
89
89
  // VIEW
90
90
 
91
- export const view = (model: Model): Document => {
92
- const h = html<Message>()
93
-
94
- return {
95
- title: `Counter: ${model.count}`,
96
- body: h.div(
97
- [],
98
- [
99
- h.p([], [model.count.toString()]),
100
- h.button([h.OnClick(ClickedDecrement())], ['-']),
101
- h.button([h.OnClick(ClickedReset())], ['Reset']),
102
- h.button([h.OnClick(ClickedIncrement())], ['+']),
103
- ],
104
- ),
105
- }
106
- }
91
+ export const view = (model: Model, h: HtmlBuilder<Message>): Document => ({
92
+ title: `Counter: ${model.count}`,
93
+ body: h.div(
94
+ [],
95
+ [
96
+ h.p([], [model.count.toString()]),
97
+ h.button([h.OnClick(ClickedDecrement())], ['-']),
98
+ h.button([h.OnClick(ClickedReset())], ['Reset']),
99
+ h.button([h.OnClick(ClickedIncrement())], ['+']),
100
+ ],
101
+ ),
102
+ })
107
103
  ```
108
104
 
109
105
  ```ts
@@ -1,4 +1,4 @@
1
- import { type Html } from '../html/index.js';
1
+ import { type Html, type HtmlBuilder } from '../html/index.js';
2
2
  import type { Point, Shape } from './shape.js';
3
3
  /**
4
4
  * Configuration for `Canvas.view`. Pointer handlers are optional and
@@ -21,18 +21,25 @@ export type ViewConfig<Message> = Readonly<{
21
21
  * postpatch hook re-paints on every render. The canvas is a pure function of
22
22
  * `shapes`. Same shapes produce the same pixels.
23
23
  *
24
+ * The trailing `h` fixes `Message` to the calling view's frame; the pointer
25
+ * handlers dispatch through that frame at fire time, so passing the builder
26
+ * that built the surrounding view is what makes the handler types truthful.
27
+ *
24
28
  * @example
25
29
  * ```typescript
26
- * Canvas.view<Message>({
27
- * width: 400,
28
- * height: 300,
29
- * shapes: [
30
- * Canvas.Rect({ x: 0, y: 0, width: 400, height: 300, fill: '#000' }),
31
- * Canvas.Circle({ x: 200, y: 150, radius: 50, fill: '#f0a' }),
32
- * ],
33
- * onPointerDown: ({ x, y }) => ClickedCanvas({ x, y }),
34
- * })
30
+ * Canvas.view(
31
+ * {
32
+ * width: 400,
33
+ * height: 300,
34
+ * shapes: [
35
+ * Canvas.Rect({ x: 0, y: 0, width: 400, height: 300, fill: '#000' }),
36
+ * Canvas.Circle({ x: 200, y: 150, radius: 50, fill: '#f0a' }),
37
+ * ],
38
+ * onPointerDown: ({ x, y }) => ClickedCanvas({ x, y }),
39
+ * },
40
+ * h,
41
+ * )
35
42
  * ```
36
43
  */
37
- export declare const view: <Message>(config: ViewConfig<Message>) => Html;
44
+ export declare const view: <Message>(config: ViewConfig<NoInfer<Message>>, _h: HtmlBuilder<Message>) => Html;
38
45
  //# sourceMappingURL=view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/canvas/view.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,IAAI,EAAqB,MAAM,kBAAkB,CAAA;AAI/D,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;IACvD,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;IACvD,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;CACtD,CAAC,CAAA;AA8BF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,EAAE,QAAQ,UAAU,CAAC,OAAO,CAAC,KAAG,IAwE3D,CAAA"}
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/canvas/view.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,IAAI,EACT,KAAK,WAAW,EAEjB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;IACvD,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;IACvD,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;CACtD,CAAC,CAAA;AA8BF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,EAC1B,QAAQ,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EACpC,IAAI,WAAW,CAAC,OAAO,CAAC,KACvB,IAwEF,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { Array, Predicate, Record, String, pipe } from 'effect';
2
- import { __requireDispatch } from '../html/index.js';
2
+ import { __requireDispatch, } from '../html/index.js';
3
3
  import { h } from '../snabbdom/index.js';
4
4
  import { paintScene } from './paint.js';
5
5
  /**
@@ -24,20 +24,27 @@ const classesFromClassName = (className) => pipe(className, String.split(/\s+/),
24
24
  * postpatch hook re-paints on every render. The canvas is a pure function of
25
25
  * `shapes`. Same shapes produce the same pixels.
26
26
  *
27
+ * The trailing `h` fixes `Message` to the calling view's frame; the pointer
28
+ * handlers dispatch through that frame at fire time, so passing the builder
29
+ * that built the surrounding view is what makes the handler types truthful.
30
+ *
27
31
  * @example
28
32
  * ```typescript
29
- * Canvas.view<Message>({
30
- * width: 400,
31
- * height: 300,
32
- * shapes: [
33
- * Canvas.Rect({ x: 0, y: 0, width: 400, height: 300, fill: '#000' }),
34
- * Canvas.Circle({ x: 200, y: 150, radius: 50, fill: '#f0a' }),
35
- * ],
36
- * onPointerDown: ({ x, y }) => ClickedCanvas({ x, y }),
37
- * })
33
+ * Canvas.view(
34
+ * {
35
+ * width: 400,
36
+ * height: 300,
37
+ * shapes: [
38
+ * Canvas.Rect({ x: 0, y: 0, width: 400, height: 300, fill: '#000' }),
39
+ * Canvas.Circle({ x: 200, y: 150, radius: 50, fill: '#f0a' }),
40
+ * ],
41
+ * onPointerDown: ({ x, y }) => ClickedCanvas({ x, y }),
42
+ * },
43
+ * h,
44
+ * )
38
45
  * ```
39
46
  */
40
- export const view = (config) => {
47
+ export const view = (config, _h) => {
41
48
  const dispatchSync = __requireDispatch();
42
49
  const { width, height, shapes, className, onPointerDown, onPointerMove, onPointerUp, } = config;
43
50
  const pointerListener = (toMessage) => (event) => {
@@ -1,5 +1,5 @@
1
1
  import { type Schema } from 'effect';
2
- import type { Attribute, Child, Html } from '../html/index.js';
2
+ import type { Attribute, Child, Html, HtmlBuilder } from '../html/index.js';
3
3
  /** Type-level kebab-case to PascalCase, used to convert event names like
4
4
  * `'change-rating'` into the corresponding factory name `'OnChangeRating'`. */
5
5
  type KebabToPascal<S extends string> = S extends `${infer Head}-${infer Tail}` ? `${Capitalize<Head>}${KebabToPascal<Tail>}` : Capitalize<S>;
@@ -24,19 +24,24 @@ export interface CustomElementConfig<Tag extends string, Properties extends Reco
24
24
  readonly events: Events;
25
25
  }
26
26
  /** A defined custom element, untyped on Message at definition time so the
27
- * spec can be exported and shared across modules. Call `.withMessage<Message>()`
28
- * inside a view module to mint a typed `ElementBuilder` bound to the
29
- * consumer's Message universe. */
27
+ * spec can be exported and shared across modules. Call `.withMessage(h)`
28
+ * with a view's builder to mint a typed `ElementBuilder` bound to that
29
+ * frame's Message universe. The builder argument is a type witness: it
30
+ * fixes `Message` to the frame the element renders in, so the spec cannot
31
+ * be bound to a Message universe its events will not dispatch into. */
30
32
  export interface CustomElementSpec<Tag extends string, Properties extends Record<string, Schema.Top>, Events extends Record<string, Schema.Top>> {
31
33
  readonly tag: Tag;
32
34
  readonly properties: Properties;
33
35
  readonly events: Events;
34
- readonly withMessage: <Message>() => ElementBuilder<Message, Properties, Events>;
36
+ readonly withMessage: <Message>(h: HtmlBuilder<Message>) => ElementBuilder<Message, Properties, Events>;
35
37
  }
36
38
  /** The typed builder for a given spec and Message universe. Equivalent to
37
39
  * the value `Spec.withMessage<Message>()` returns, expressed as a type
38
40
  * consumers can name without reaching for `ReturnType<typeof ...>`. */
39
41
  export type Builder<Spec, Message> = Spec extends CustomElementSpec<string, infer Properties, infer Events> ? ElementBuilder<Message, Properties, Events> : never;
42
+ /** @internal Shared with the Scene test helpers so their error messages name
43
+ * the same `On*` factory this module generates. */
44
+ export declare const kebabToPascal: (input: string) => string;
40
45
  /**
41
46
  * Define a typed binding for a custom element. The returned spec describes
42
47
  * the element's properties and events with Schema, and exposes a
@@ -62,7 +67,8 @@ export type Builder<Spec, Message> = Spec extends CustomElementSpec<string, infe
62
67
  * },
63
68
  * })
64
69
  *
65
- * const picker = hexColorPicker.withMessage<Message>()
70
+ * // Inside a view, with its `h` in scope:
71
+ * const picker = hexColorPicker.withMessage(h)
66
72
  *
67
73
  * picker(
68
74
  * [
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/customElement/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/customElement/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,MAAM,EAAgB,MAAM,QAAQ,CAAA;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAO3E;gFACgF;AAChF,KAAK,aAAa,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GAC1E,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,GAC3C,UAAU,CAAC,CAAC,CAAC,CAAA;AAEjB,KAAK,eAAe,CAAC,OAAO,EAAE,SAAS,IAAI,CACzC,KAAK,EAAE,SAAS,KACb,SAAS,CAAC,OAAO,CAAC,CAAA;AAEvB,KAAK,YAAY,CAAC,OAAO,EAAE,UAAU,IAAI,CACvC,SAAS,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,KACvC,SAAS,CAAC,OAAO,CAAC,CAAA;AAEvB,gBAAgB;AAChB,KAAK,iBAAiB,CACpB,OAAO,EACP,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,IAC3C;IACF,QAAQ,EAAE,CAAC,IAAI,MAAM,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,eAAe,CACzE,OAAO,EACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAClC;CACF,CAAA;AAED,gBAAgB;AAChB,KAAK,cAAc,CAAC,OAAO,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI;IACxE,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,IAAI,KAAK,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,GAAG,YAAY,CAC5E,OAAO,EACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAC9B;CACF,CAAA;AAED;;+EAE+E;AAC/E,MAAM,MAAM,cAAc,CACxB,OAAO,EACP,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAC7C,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,IACvC,CAAC,CACH,UAAU,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAC9C,QAAQ,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,KAC5B,IAAI,CAAC,GACR,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,GACtC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AAEjC,wDAAwD;AACxD,MAAM,WAAW,mBAAmB,CAClC,GAAG,SAAS,MAAM,EAClB,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAC7C,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;IAEzC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IACjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED;;;;;wEAKwE;AACxE,MAAM,WAAW,iBAAiB,CAChC,GAAG,SAAS,MAAM,EAClB,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAC7C,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC;IAEzC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAA;IACjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAC5B,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,KACpB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,CAAA;CACjD;AAED;;wEAEwE;AACxE,MAAM,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,IAC/B,IAAI,SAAS,iBAAiB,CAAC,MAAM,EAAE,MAAM,UAAU,EAAE,MAAM,MAAM,CAAC,GAClE,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,GAC3C,KAAK,CAAA;AAEX;oDACoD;AACpD,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,MACgC,CAAA;AAyB9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,MAAM,GACjB,GAAG,SAAS,MAAM,EAClB,UAAU,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAC7C,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAEzC,QAAQ,mBAAmB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,KACnD,iBAAiB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CA+D3C,CAAA"}
@@ -1,6 +1,8 @@
1
1
  import { Array, String, pipe } from 'effect';
2
2
  import { OnCustomEvent, Prop, customElement as customElementVNode, } from '../html/index.js';
3
- const kebabToPascal = (input) => pipe(input, String.split('-'), Array.map(String.capitalize), Array.join(''));
3
+ /** @internal Shared with the Scene test helpers so their error messages name
4
+ * the same `On*` factory this module generates. */
5
+ export const kebabToPascal = (input) => pipe(input, String.split('-'), Array.map(String.capitalize), Array.join(''));
4
6
  const IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
5
7
  const isValidPropertyName = (name) => IDENTIFIER_PATTERN.test(name);
6
8
  const isValidEventName = (name) => {
@@ -38,7 +40,8 @@ const eventFactoryName = (eventName) => `On${kebabToPascal(eventName)}`;
38
40
  * },
39
41
  * })
40
42
  *
41
- * const picker = hexColorPicker.withMessage<Message>()
43
+ * // Inside a view, with its `h` in scope:
44
+ * const picker = hexColorPicker.withMessage(h)
42
45
  *
43
46
  * picker(
44
47
  * [
@@ -53,7 +56,7 @@ export const define = (config) => {
53
56
  const propertyNames = Object.keys(config.properties);
54
57
  const eventNames = Object.keys(config.events);
55
58
  validateNames({ tag: config.tag, propertyNames, eventNames });
56
- const buildForMessage = () => {
59
+ const buildElementBuilder = () => {
57
60
  const createVNode = customElementVNode()(config.tag);
58
61
  const elementFn = (attributes = [], children = []) => createVNode(attributes, children);
59
62
  /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
@@ -70,11 +73,21 @@ export const define = (config) => {
70
73
  /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
71
74
  return builder;
72
75
  };
76
+ // NOTE: `withMessage` is called once per render, so the ElementBuilder is
77
+ // constructed once and retyped per call. Message is erased at runtime: the
78
+ // factories close over nothing Message-specific, exactly like the html
79
+ // builder singleton.
80
+ let cachedElementBuilder;
81
+ const withMessage = (_h) => {
82
+ cachedElementBuilder ??= buildElementBuilder();
83
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
84
+ return cachedElementBuilder;
85
+ };
73
86
  return {
74
87
  tag: config.tag,
75
88
  properties: config.properties,
76
89
  events: config.events,
77
- withMessage: buildForMessage,
90
+ withMessage,
78
91
  };
79
92
  };
80
93
  const validateNames = (input) => {