foldkit 0.100.0 → 0.101.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 (60) hide show
  1. package/README.md +38 -36
  2. package/dist/devTools/overlay.d.ts.map +1 -1
  3. package/dist/devTools/overlay.js +25 -40
  4. package/dist/devTools/store.d.ts.map +1 -1
  5. package/dist/devTools/store.js +50 -49
  6. package/dist/runtime/runtime.d.ts +10 -10
  7. package/dist/runtime/runtime.d.ts.map +1 -1
  8. package/dist/runtime/runtime.js +3 -2
  9. package/dist/runtime/subscription.d.ts +139 -19
  10. package/dist/runtime/subscription.d.ts.map +1 -1
  11. package/dist/runtime/subscription.js +90 -9
  12. package/dist/subscription/animationFrame.d.ts +23 -26
  13. package/dist/subscription/animationFrame.d.ts.map +1 -1
  14. package/dist/subscription/animationFrame.js +17 -18
  15. package/dist/subscription/public.d.ts +2 -2
  16. package/dist/subscription/public.d.ts.map +1 -1
  17. package/dist/subscription/public.js +1 -1
  18. package/dist/test/apps/bubbling.d.ts.map +1 -1
  19. package/dist/test/apps/bubbling.js +7 -5
  20. package/dist/test/apps/counter.d.ts.map +1 -1
  21. package/dist/test/apps/counter.js +8 -6
  22. package/dist/test/apps/disabledButton.d.ts.map +1 -1
  23. package/dist/test/apps/disabledButton.js +32 -21
  24. package/dist/test/apps/fileUpload.d.ts.map +1 -1
  25. package/dist/test/apps/fileUpload.js +18 -16
  26. package/dist/test/apps/interactions.d.ts.map +1 -1
  27. package/dist/test/apps/interactions.js +21 -19
  28. package/dist/test/apps/keypress.d.ts.map +1 -1
  29. package/dist/test/apps/keypress.js +12 -10
  30. package/dist/test/apps/login.d.ts.map +1 -1
  31. package/dist/test/apps/login.js +38 -36
  32. package/dist/test/apps/logoutButton.d.ts.map +1 -1
  33. package/dist/test/apps/logoutButton.js +4 -2
  34. package/dist/test/apps/mountPanel.d.ts.map +1 -1
  35. package/dist/test/apps/mountPanel.js +29 -21
  36. package/dist/test/apps/multiRole.d.ts.map +1 -1
  37. package/dist/test/apps/multiRole.js +6 -4
  38. package/dist/test/apps/pointer.d.ts.map +1 -1
  39. package/dist/test/apps/pointer.js +15 -13
  40. package/dist/test/apps/resumeUpload.d.ts.map +1 -1
  41. package/dist/test/apps/resumeUpload.js +20 -15
  42. package/dist/ui/dragAndDrop/index.d.ts +383 -101
  43. package/dist/ui/dragAndDrop/index.d.ts.map +1 -1
  44. package/dist/ui/dragAndDrop/index.js +19 -24
  45. package/dist/ui/dragAndDrop/public.d.ts +1 -1
  46. package/dist/ui/dragAndDrop/public.d.ts.map +1 -1
  47. package/dist/ui/dragAndDrop/public.js +1 -1
  48. package/dist/ui/slider/index.d.ts +193 -87
  49. package/dist/ui/slider/index.d.ts.map +1 -1
  50. package/dist/ui/slider/index.js +12 -19
  51. package/dist/ui/slider/public.d.ts +1 -1
  52. package/dist/ui/slider/public.d.ts.map +1 -1
  53. package/dist/ui/slider/public.js +1 -1
  54. package/dist/ui/virtualList/index.d.ts +45 -39
  55. package/dist/ui/virtualList/index.d.ts.map +1 -1
  56. package/dist/ui/virtualList/index.js +5 -12
  57. package/dist/ui/virtualList/public.d.ts +1 -1
  58. package/dist/ui/virtualList/public.d.ts.map +1 -1
  59. package/dist/ui/virtualList/public.js +1 -1
  60. package/package.json +1 -1
@@ -1,23 +1,143 @@
1
- import { type Equivalence, type Schema, type Stream } from 'effect';
2
- type ResolveMessage<T> = T extends Schema.Top ? Schema.Schema.Type<T> : T;
3
- /** A reactive binding between Model state and a long-running stream of Messages. */
4
- export type Subscription<Model, Message, StreamDeps, Resources = never> = {
5
- readonly modelToDependencies: (model: Model) => StreamDeps;
6
- readonly equivalence?: Equivalence.Equivalence<StreamDeps>;
7
- readonly dependenciesToStream: (deps: StreamDeps, readDependencies: () => StreamDeps) => Stream.Stream<ResolveMessage<Message>, never, Resources>;
1
+ import { type Equivalence, Schema, Stream } from 'effect';
2
+ type SubscriptionBrand = {
3
+ readonly __subscription: never;
8
4
  };
9
- type SubscriptionConfig<Model, Message, StreamDeps, Resources = never> = {
10
- readonly schema: Schema.Schema<StreamDeps>;
11
- } & Subscription<Model, Message, StreamDeps, Resources>;
12
- /** A record of named subscription configurations, keyed by dependency field name. */
13
- export type Subscriptions<Model, Message, SubscriptionDependencies extends Schema.Struct<any>, Resources = never> = {
14
- readonly [K in keyof Schema.Schema.Type<SubscriptionDependencies>]: SubscriptionConfig<Model, Message, Schema.Schema.Type<SubscriptionDependencies>[K], Resources>;
5
+ type DependenciesSchema<Dependencies> = Schema.Schema<Dependencies> & {
6
+ readonly fields: Schema.Struct.Fields;
15
7
  };
16
- /** Creates type-safe subscription configurations from a dependency schema. */
17
- export declare const makeSubscriptions: <SubscriptionDependencies extends Schema.Struct<any>>(SubscriptionDependencies: SubscriptionDependencies) => <Model, Message, Resources = never>(configs: { readonly [K in keyof Schema.Schema.Type<SubscriptionDependencies>]: {
18
- readonly modelToDependencies: (model: Model) => Schema.Schema.Type<SubscriptionDependencies>[K];
19
- readonly equivalence?: Equivalence.Equivalence<Schema.Schema.Type<SubscriptionDependencies>[K]> | undefined;
20
- readonly dependenciesToStream: (deps: Schema.Schema.Type<SubscriptionDependencies>[K], readDependencies: () => Schema.Schema.Type<SubscriptionDependencies>[K]) => Stream.Stream<ResolveMessage<Message>, never, Resources>;
21
- }; }) => Subscriptions<Model, Message, SubscriptionDependencies, Resources>;
8
+ type EntryWithoutKeepAlive<Model, Message, Dependencies, Services> = {
9
+ readonly dependenciesSchema: DependenciesSchema<Dependencies>;
10
+ readonly modelToDependencies: (model: Model) => Dependencies;
11
+ readonly keepAliveEquivalence?: never;
12
+ readonly dependenciesToStream: (dependencies: Dependencies) => Stream.Stream<Message, never, Services>;
13
+ };
14
+ type EntryWithKeepAlive<Model, Message, Dependencies, Services> = {
15
+ readonly dependenciesSchema: DependenciesSchema<Dependencies>;
16
+ readonly modelToDependencies: (model: Model) => Dependencies;
17
+ readonly keepAliveEquivalence: Equivalence.Equivalence<Dependencies>;
18
+ readonly dependenciesToStream: (dependencies: Dependencies, readDependencies: () => Dependencies) => Stream.Stream<Message, never, Services>;
19
+ };
20
+ type Entry<Model, Message, Dependencies, Services = never> = EntryWithoutKeepAlive<Model, Message, Dependencies, Services> | EntryWithKeepAlive<Model, Message, Dependencies, Services>;
21
+ /**
22
+ * A single subscription entry produced by `Subscription.make`,
23
+ * `Subscription.lift`, or `Subscription.aggregate`. The brand field is
24
+ * `never`, so application code cannot manually construct a `Subscription`
25
+ * value: it must go through one of those constructors (or a helper like
26
+ * `Subscription.persistent` that returns an entry shape, then through
27
+ * `make`).
28
+ *
29
+ * Two variants by `keepAliveEquivalence` presence:
30
+ *
31
+ * - Without `keepAliveEquivalence` (the common case), every Model change recomputes
32
+ * the dependencies. Equivalent dependencies leave the Stream alone; any
33
+ * change tears it down and restarts. `dependenciesToStream` takes a single
34
+ * argument: the latest dependencies.
35
+ * - With `keepAliveEquivalence` (an escape hatch), Model changes that the
36
+ * equivalence treats as equal leave the Stream running, but the running
37
+ * Stream can still read the latest dependencies via the second
38
+ * `readDependencies` argument. Use this when the Stream needs mid-flight
39
+ * access to data that changes often but shouldn't trigger restarts
40
+ * (Foldkit UI's `Ui.DragAndDrop.autoScroll` reading the latest pointer
41
+ * `clientY` each rAF tick is the canonical example).
42
+ *
43
+ * `dependenciesSchema` must be a `Schema.Struct` so every dependency is
44
+ * explicitly named at the schema level.
45
+ */
46
+ export type Subscription<Model, Message, Dependencies, Services = never> = Entry<Model, Message, Dependencies, Services> & SubscriptionBrand;
47
+ /** A record of named Subscriptions keyed by dependency field name. */
48
+ export type Subscriptions<Model, Message, Services = never> = Readonly<Record<string, Subscription<Model, Message, any, Services>>>;
49
+ /**
50
+ * Callbacks for a subscription entry without `keepAliveEquivalence`. Dependencies
51
+ * are inferred from the field map passed to `entry`.
52
+ */
53
+ type EntryCallbacksWithoutKeepAlive<Model, Message, Dependencies, Services> = {
54
+ readonly modelToDependencies: (model: Model) => Dependencies;
55
+ readonly keepAliveEquivalence?: never;
56
+ readonly dependenciesToStream: (dependencies: Dependencies) => Stream.Stream<Message, never, Services>;
57
+ };
58
+ /**
59
+ * Callbacks for a subscription entry with `keepAliveEquivalence`. Dependencies
60
+ * are inferred from the field map passed to `entry`.
61
+ */
62
+ type EntryCallbacksWithKeepAlive<Model, Message, Dependencies, Services> = {
63
+ readonly modelToDependencies: (model: Model) => Dependencies;
64
+ readonly keepAliveEquivalence: Equivalence.Equivalence<Dependencies>;
65
+ readonly dependenciesToStream: (dependencies: Dependencies, readDependencies: () => Dependencies) => Stream.Stream<Message, never, Services>;
66
+ };
67
+ /**
68
+ * Builds a single subscription entry from a field map and callbacks.
69
+ *
70
+ * The field map is the same shape you would pass to `S.Struct`. Reading the
71
+ * schema as a positional argument (rather than a property on the entry
72
+ * literal) lets TypeScript fully resolve the `Dependencies` type before
73
+ * contextually typing `modelToDependencies` and `dependenciesToStream`, so
74
+ * destructuring patterns like `({ maybeMapHostId })` are inferred correctly
75
+ * even when the field schemas use transforms (e.g. `S.Option`).
76
+ *
77
+ * Two overloads, one per `keepAliveEquivalence` presence:
78
+ *
79
+ * - Without `keepAliveEquivalence`, `dependenciesToStream` takes a single
80
+ * `dependencies` argument.
81
+ * - With `keepAliveEquivalence`, `dependenciesToStream` also receives a
82
+ * `readDependencies` thunk for accessing the latest value while the Stream
83
+ * stays running across Model changes the equivalence accepts as equal.
84
+ */
85
+ export type EntryBuilder<Model, Message, Services> = <const Fields extends Schema.Struct.Fields, Callbacks extends EntryCallbacksWithoutKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services> | EntryCallbacksWithKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services>>(fields: Fields, callbacks: Callbacks) => Callbacks extends {
86
+ readonly keepAliveEquivalence: Equivalence.Equivalence<any>;
87
+ } ? EntryWithKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services> : EntryWithoutKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services>;
88
+ /**
89
+ * Declares a Subscriptions record. The Model, Message, and optional Services
90
+ * generics are provided up front; the entries record follows, built from
91
+ * calls to the `entry` builder passed into the inner function.
92
+ *
93
+ * Reach for `Subscription.aggregate` to combine multiple records, and
94
+ * `Subscription.lift` to translate a child Submodel's record into a parent
95
+ * context.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * Subscription.make<Model, Message>()(entry => ({
100
+ * tick: entry(
101
+ * { isRunning: S.Boolean },
102
+ * {
103
+ * modelToDependencies: model => ({ isRunning: model.isRunning }),
104
+ * dependenciesToStream: ({ isRunning }) =>
105
+ * Stream.when(..., Effect.sync(() => isRunning)),
106
+ * },
107
+ * ),
108
+ * }))
109
+ * ```
110
+ */
111
+ export declare const make: <Model, Message, Services = never>() => <Entries extends Readonly<Record<string, Entry<Model, Message, any, Services>>>>(build: (entry: EntryBuilder<Model, Message, Services>) => Entries) => { readonly [K in keyof Entries]: Entries[K] & SubscriptionBrand; };
112
+ /**
113
+ * Combines multiple Subscriptions records into one. Throws on duplicate
114
+ * keys so a misconfigured aggregate fails loudly at startup rather than
115
+ * silently overriding.
116
+ */
117
+ export declare const aggregate: <Model, Message, Services = never>() => (...records: ReadonlyArray<Subscriptions<Model, Message, Services>>) => Subscriptions<Model, Message, Services>;
118
+ /**
119
+ * Wraps a Stream as a Subscription entry whose lifecycle is independent of
120
+ * the Model. The Stream runs for the lifetime of the Subscriptions record;
121
+ * no Model change tears it down or restarts it. Use for any Stream whose
122
+ * work doesn't depend on Model state, such as system theme listeners,
123
+ * viewport width observers, or route-independent timers.
124
+ *
125
+ * Returns an entry shape, not a branded Subscription. Pass it into `make`
126
+ * as an entry value.
127
+ */
128
+ export declare const persistent: <Message, Services = never>(stream: Stream.Stream<Message, never, Services>) => EntryWithoutKeepAlive<unknown, Message, Record<string, never>, Services>;
129
+ type ChildModelOf<ChildSubscriptions> = ChildSubscriptions[keyof ChildSubscriptions] extends Subscription<infer ChildModel, any, any, any> ? ChildModel : never;
130
+ type ChildMessageOf<ChildSubscriptions> = ChildSubscriptions[keyof ChildSubscriptions] extends Subscription<any, infer ChildMessage, any, any> ? ChildMessage : never;
131
+ /**
132
+ * Lifts a record of child Subscriptions into a parent's Model and Message
133
+ * context, applying a Model accessor and a Message wrapper uniformly to
134
+ * every entry. Per-entry dependency types, schemas, and `keepAliveEquivalence`
135
+ * settings are preserved; each lifted entry's variant (with or without
136
+ * `readDependencies`) matches its source entry's.
137
+ */
138
+ export declare const lift: <Subscriptions extends Readonly<Record<string, Subscription<any, any, any, any>>>>(subscriptions: Subscriptions) => <ParentModel, ParentMessage>(config: {
139
+ readonly toChildModel: (parentModel: ParentModel) => ChildModelOf<Subscriptions>;
140
+ readonly toParentMessage: (message: ChildMessageOf<Subscriptions>) => ParentMessage;
141
+ }) => { readonly [K in keyof Subscriptions]: Subscriptions[K] extends Subscription<any, any, infer Dependencies, infer Services> ? Subscription<ParentModel, ParentMessage, Dependencies, Services> : never; };
22
142
  export {};
23
143
  //# sourceMappingURL=subscription.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/runtime/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAU,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE3E,KAAK,cAAc,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAEzE,oFAAoF;AACpF,MAAM,MAAM,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,IAAI;IACxE,QAAQ,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,UAAU,CAAA;IAC1D,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;IAC1D,QAAQ,CAAC,oBAAoB,EAAE,CAC7B,IAAI,EAAE,UAAU,EAChB,gBAAgB,EAAE,MAAM,UAAU,KAC/B,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;CAC9D,CAAA;AAED,KAAK,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,IAAI;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;CAC3C,GAAG,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;AAEvD,qFAAqF;AACrF,MAAM,MAAM,aAAa,CACvB,KAAK,EACL,OAAO,EACP,wBAAwB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EACnD,SAAS,GAAG,KAAK,IACf;IACF,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,GAAG,kBAAkB,CACpF,KAAK,EACL,OAAO,EACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,EAC/C,SAAS,CACV;CACF,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,GAC3B,wBAAwB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAClD,0BAA0B,wBAAwB,MAEnD,KAAK,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,EAC3C,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,GAAG;IAClE,QAAQ,CAAC,mBAAmB,EAAE,CAC5B,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAA;IACpD,QAAQ,CAAC,WAAW,CAAC,EACjB,WAAW,CAAC,WAAW,CACrB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAChD,GACD,SAAS,CAAA;IACb,QAAQ,CAAC,oBAAoB,EAAE,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,EACrD,gBAAgB,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,KACpE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;CAC9D,GACF,KAAG,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,wBAAwB,EAAE,SAAS,CAelE,CAAA"}
1
+ {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/runtime/subscription.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,92 @@
1
- import { Record } from 'effect';
2
- /** Creates type-safe subscription configurations from a dependency schema. */
3
- export const makeSubscriptions = (SubscriptionDependencies) => (configs) =>
1
+ import { Record, Schema, Stream } from 'effect';
2
+ /**
3
+ * Declares a Subscriptions record. The Model, Message, and optional Services
4
+ * generics are provided up front; the entries record follows, built from
5
+ * calls to the `entry` builder passed into the inner function.
6
+ *
7
+ * Reach for `Subscription.aggregate` to combine multiple records, and
8
+ * `Subscription.lift` to translate a child Submodel's record into a parent
9
+ * context.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * Subscription.make<Model, Message>()(entry => ({
14
+ * tick: entry(
15
+ * { isRunning: S.Boolean },
16
+ * {
17
+ * modelToDependencies: model => ({ isRunning: model.isRunning }),
18
+ * dependenciesToStream: ({ isRunning }) =>
19
+ * Stream.when(..., Effect.sync(() => isRunning)),
20
+ * },
21
+ * ),
22
+ * }))
23
+ * ```
24
+ */
25
+ export const make = () => (build) => {
26
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
27
+ const entryBuilder = ((fields, callbacks) => ({
28
+ dependenciesSchema: Schema.Struct(fields),
29
+ ...callbacks,
30
+ }));
31
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
32
+ return build(entryBuilder);
33
+ };
34
+ /**
35
+ * Combines multiple Subscriptions records into one. Throws on duplicate
36
+ * keys so a misconfigured aggregate fails loudly at startup rather than
37
+ * silently overriding.
38
+ */
39
+ export const aggregate = () => (...records) => {
40
+ const result = {};
41
+ for (const record of records) {
42
+ for (const key of Object.keys(record)) {
43
+ if (key in result) {
44
+ throw new Error(`Subscription.aggregate: duplicate key "${key}" across records`);
45
+ }
46
+ result[key] = record[key];
47
+ }
48
+ }
49
+ return result;
50
+ };
51
+ /**
52
+ * Wraps a Stream as a Subscription entry whose lifecycle is independent of
53
+ * the Model. The Stream runs for the lifetime of the Subscriptions record;
54
+ * no Model change tears it down or restarts it. Use for any Stream whose
55
+ * work doesn't depend on Model state, such as system theme listeners,
56
+ * viewport width observers, or route-independent timers.
57
+ *
58
+ * Returns an entry shape, not a branded Subscription. Pass it into `make`
59
+ * as an entry value.
60
+ */
61
+ export const persistent = (stream) => ({
62
+ dependenciesSchema: Schema.Struct({}),
63
+ modelToDependencies: () => ({}),
64
+ dependenciesToStream: () => stream,
65
+ });
66
+ /**
67
+ * Lifts a record of child Subscriptions into a parent's Model and Message
68
+ * context, applying a Model accessor and a Message wrapper uniformly to
69
+ * every entry. Per-entry dependency types, schemas, and `keepAliveEquivalence`
70
+ * settings are preserved; each lifted entry's variant (with or without
71
+ * `readDependencies`) matches its source entry's.
72
+ */
73
+ export const lift = (subscriptions) => (config) =>
4
74
  /* eslint-disable @typescript-eslint/consistent-type-assertions */
5
- Record.map(configs, ({ modelToDependencies, equivalence, dependenciesToStream }, key) => ({
6
- schema: SubscriptionDependencies.fields[key],
7
- modelToDependencies,
8
- equivalence,
9
- dependenciesToStream,
10
- }));
75
+ Record.map(subscriptions, subscription => {
76
+ const modelToDependencies = (parentModel) => subscription.modelToDependencies(config.toChildModel(parentModel));
77
+ const wrapStream = (stream) => Stream.map(stream, config.toParentMessage);
78
+ if (subscription.keepAliveEquivalence !== undefined) {
79
+ return {
80
+ dependenciesSchema: subscription.dependenciesSchema,
81
+ modelToDependencies,
82
+ keepAliveEquivalence: subscription.keepAliveEquivalence,
83
+ dependenciesToStream: (dependencies, readDependencies) => wrapStream(subscription.dependenciesToStream(dependencies, readDependencies)),
84
+ };
85
+ }
86
+ return {
87
+ dependenciesSchema: subscription.dependenciesSchema,
88
+ modelToDependencies,
89
+ dependenciesToStream: (dependencies) => wrapStream(subscription.dependenciesToStream(dependencies)),
90
+ };
91
+ });
11
92
  /* eslint-enable @typescript-eslint/consistent-type-assertions */
@@ -1,4 +1,4 @@
1
- import { Stream } from 'effect';
1
+ import { Schema as S, Stream } from 'effect';
2
2
  /**
3
3
  * Configuration for the `animationFrame` Subscription helper.
4
4
  *
@@ -13,41 +13,38 @@ export type AnimationFrameConfig<Model, Message> = Readonly<{
13
13
  toMessage: (deltaTime: number) => Message;
14
14
  }>;
15
15
  /**
16
- * The subscription record returned by `animationFrame`. Shape matches the
17
- * `{ modelToDependencies, dependenciesToStream }` form expected by
18
- * `Subscription.makeSubscriptions` field configs.
19
- */
20
- export type AnimationFrameSubscription<Model, Message> = Readonly<{
21
- modelToDependencies: (model: Model) => boolean;
22
- dependenciesToStream: (isActive: boolean) => Stream.Stream<Message>;
23
- }>;
24
- /**
25
- * Build a Subscription field config that emits a Message on every
16
+ * Build a Subscription that emits a Message on every
26
17
  * `requestAnimationFrame` tick, with the inter-frame delta in milliseconds.
27
18
  *
28
- * Pair with `S.Boolean` in the `SubscriptionDependencies` schema:
29
- *
30
19
  * @example
31
20
  * ```typescript
32
- * const SubscriptionDependencies = S.Struct({
33
- * frame: S.Boolean,
34
- * })
35
- *
36
- * const subscriptions = Subscription.makeSubscriptions(SubscriptionDependencies)<
37
- * Model,
38
- * Message
39
- * >({
21
+ * const subscriptions = Subscription.make<Model, Message>()(_entry => ({
40
22
  * frame: Subscription.animationFrame({
41
23
  * isActive: model => model.isPlaying,
42
24
  * toMessage: deltaTime => Tick({ deltaTime }),
43
25
  * }),
44
- * })
26
+ * }))
45
27
  * ```
46
28
  *
47
29
  * The browser pauses `requestAnimationFrame` when the tab is hidden, so
48
- * `deltaTime` may spike on the first frame after the tab regains focus.
49
- * Clamp it in the update function if your simulation is sensitive to large
50
- * jumps.
30
+ * `deltaTime` may spike to several seconds on the first frame after the
31
+ * tab regains focus. If your `update` function multiplies `deltaTime`
32
+ * against motion or physics, cap it to a reasonable maximum (32ms is
33
+ * typical) before using it. Otherwise a multi-second `deltaTime` can send
34
+ * moving objects flying across the screen in one frame.
35
+ *
36
+ * Returns an entry shape, not a branded Subscription. Pass it into
37
+ * `Subscription.make` as an entry value.
51
38
  */
52
- export declare const animationFrame: <Model, Message>(config: AnimationFrameConfig<Model, Message>) => AnimationFrameSubscription<Model, Message>;
39
+ export declare const animationFrame: <Model, Message>(config: AnimationFrameConfig<Model, Message>) => {
40
+ dependenciesSchema: S.Struct<{
41
+ readonly isActive: S.Boolean;
42
+ }>;
43
+ modelToDependencies: (model: Model) => {
44
+ isActive: boolean;
45
+ };
46
+ dependenciesToStream: ({ isActive }: {
47
+ readonly isActive: boolean;
48
+ }) => Stream.Stream<Message, never, never>;
49
+ };
53
50
  //# sourceMappingURL=animationFrame.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"animationFrame.d.ts","sourceRoot":"","sources":["../../src/subscription/animationFrame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAA;CAC1C,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAChE,mBAAmB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IAC9C,oBAAoB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;CACpE,CAAC,CAAA;AA2BF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,EAAE,OAAO,EAC3C,QAAQ,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,KAC3C,0BAA0B,CAAC,KAAK,EAAE,OAAO,CAO1C,CAAA"}
1
+ {"version":3,"file":"animationFrame.d.ts","sourceRoot":"","sources":["../../src/subscription/animationFrame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE3D;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAA;CAC1C,CAAC,CAAA;AA2BF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,EAAE,OAAO,EAC3C,QAAQ,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC;;;;iCAGf,KAAK;;;yCAGG;QAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;KAAE;CAKnE,CAAA"}
@@ -1,4 +1,4 @@
1
- import { Effect, Queue, Stream } from 'effect';
1
+ import { Effect, Queue, Schema as S, Stream } from 'effect';
2
2
  const makeAnimationFrameStream = (toMessage) => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
3
3
  const state = {
4
4
  frameId: 0,
@@ -14,34 +14,33 @@ const makeAnimationFrameStream = (toMessage) => Stream.callback(queue => Effect.
14
14
  return state;
15
15
  }), state => Effect.sync(() => cancelAnimationFrame(state.frameId))).pipe(Effect.flatMap(() => Effect.never)));
16
16
  /**
17
- * Build a Subscription field config that emits a Message on every
17
+ * Build a Subscription that emits a Message on every
18
18
  * `requestAnimationFrame` tick, with the inter-frame delta in milliseconds.
19
19
  *
20
- * Pair with `S.Boolean` in the `SubscriptionDependencies` schema:
21
- *
22
20
  * @example
23
21
  * ```typescript
24
- * const SubscriptionDependencies = S.Struct({
25
- * frame: S.Boolean,
26
- * })
27
- *
28
- * const subscriptions = Subscription.makeSubscriptions(SubscriptionDependencies)<
29
- * Model,
30
- * Message
31
- * >({
22
+ * const subscriptions = Subscription.make<Model, Message>()(_entry => ({
32
23
  * frame: Subscription.animationFrame({
33
24
  * isActive: model => model.isPlaying,
34
25
  * toMessage: deltaTime => Tick({ deltaTime }),
35
26
  * }),
36
- * })
27
+ * }))
37
28
  * ```
38
29
  *
39
30
  * The browser pauses `requestAnimationFrame` when the tab is hidden, so
40
- * `deltaTime` may spike on the first frame after the tab regains focus.
41
- * Clamp it in the update function if your simulation is sensitive to large
42
- * jumps.
31
+ * `deltaTime` may spike to several seconds on the first frame after the
32
+ * tab regains focus. If your `update` function multiplies `deltaTime`
33
+ * against motion or physics, cap it to a reasonable maximum (32ms is
34
+ * typical) before using it. Otherwise a multi-second `deltaTime` can send
35
+ * moving objects flying across the screen in one frame.
36
+ *
37
+ * Returns an entry shape, not a branded Subscription. Pass it into
38
+ * `Subscription.make` as an entry value.
43
39
  */
44
40
  export const animationFrame = (config) => ({
45
- modelToDependencies: model => config.isActive(model),
46
- dependenciesToStream: isActive => Stream.when(makeAnimationFrameStream(config.toMessage), Effect.sync(() => isActive)),
41
+ dependenciesSchema: S.Struct({ isActive: S.Boolean }),
42
+ modelToDependencies: (model) => ({
43
+ isActive: config.isActive(model),
44
+ }),
45
+ dependenciesToStream: ({ isActive }) => Stream.when(makeAnimationFrameStream(config.toMessage), Effect.sync(() => isActive)),
47
46
  });
@@ -1,5 +1,5 @@
1
- export { makeSubscriptions } from '../runtime/subscription.js';
1
+ export { aggregate, lift, make, persistent } from '../runtime/subscription.js';
2
2
  export type { Subscription, Subscriptions } from '../runtime/subscription.js';
3
3
  export { animationFrame } from './animationFrame.js';
4
- export type { AnimationFrameConfig, AnimationFrameSubscription, } from './animationFrame.js';
4
+ export type { AnimationFrameConfig } from './animationFrame.js';
5
5
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAE9D,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EACV,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE9E,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
@@ -1,2 +1,2 @@
1
- export { makeSubscriptions } from '../runtime/subscription.js';
1
+ export { aggregate, lift, make, persistent } from '../runtime/subscription.js';
2
2
  export { animationFrame } from './animationFrame.js';
@@ -1 +1 @@
1
- {"version":3,"file":"bubbling.d.ts","sourceRoot":"","sources":["../../../src/test/apps/bubbling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAOrC,QAAA,MAAM,OAAO,sLAAsD,CAAA;AACnE,KAAK,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIlC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAUrC,CAAA;AAMH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAajC,CAAA"}
1
+ {"version":3,"file":"bubbling.d.ts","sourceRoot":"","sources":["../../../src/test/apps/bubbling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAOrC,QAAA,MAAM,OAAO,sLAAsD,CAAA;AACnE,KAAK,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIlC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAUrC,CAAA;AAIH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAgBnC,CAAA"}
@@ -24,8 +24,10 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
24
24
  ],
25
25
  }));
26
26
  // VIEW
27
- const h = html();
28
- export const view = (model) => h.div([], [
29
- h.div([h.Role('option'), h.OnClick(ClickedContainer())], [h.span([], [`clicks=${model.clicks}`])]),
30
- h.div([h.Role('listitem'), h.OnDoubleClick(DoubleClickedContainer())], [h.span([], [`dbl=${model.doubleClicks}`])]),
31
- ]);
27
+ export const view = (model) => {
28
+ const h = html();
29
+ return h.div([], [
30
+ h.div([h.Role('option'), h.OnClick(ClickedContainer())], [h.span([], [`clicks=${model.clicks}`])]),
31
+ h.div([h.Role('listitem'), h.OnDoubleClick(DoubleClickedContainer())], [h.span([], [`dbl=${model.doubleClicks}`])]),
32
+ ]);
33
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../../src/test/apps/counter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,gBAAgB,8EAAwB,CAAA;AACrD,eAAO,MAAM,gBAAgB,8EAAwB,CAAA;AACrD,eAAO,MAAM,YAAY,0EAAoB,CAAA;AAC7C,eAAO,MAAM,gBAAgB;;EAA0C,CAAA;AACvE,eAAO,MAAM,mBAAmB,iFAA2B,CAAA;AAC3D,eAAO,MAAM,qBAAqB,mFAA6B,CAAA;AAC/D,eAAO,MAAM,mBAAmB,iFAA2B,CAAA;AAC3D,eAAO,MAAM,mBAAmB;;EAAgD,CAAA;AAChF,eAAO,MAAM,gBAAgB;;EAA6C,CAAA;AAE1E,eAAO,MAAM,OAAO;;;;;;IAUlB,CAAA;AACF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,UAAU;;;iBAIgC,CAAA;AAEvD,eAAO,MAAM,cAAc;;;;;iBAKyC,CAAA;AAIpE,eAAO,MAAM,YAAY,EAAE,KAA6B,CAAA;AAIxD,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAiCxD,CAAA;AAMH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAcjC,CAAA"}
1
+ {"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../../src/test/apps/counter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,eAAO,MAAM,KAAK;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,gBAAgB,8EAAwB,CAAA;AACrD,eAAO,MAAM,gBAAgB,8EAAwB,CAAA;AACrD,eAAO,MAAM,YAAY,0EAAoB,CAAA;AAC7C,eAAO,MAAM,gBAAgB;;EAA0C,CAAA;AACvE,eAAO,MAAM,mBAAmB,iFAA2B,CAAA;AAC3D,eAAO,MAAM,qBAAqB,mFAA6B,CAAA;AAC/D,eAAO,MAAM,mBAAmB,iFAA2B,CAAA;AAC3D,eAAO,MAAM,mBAAmB;;EAAgD,CAAA;AAChF,eAAO,MAAM,gBAAgB;;EAA6C,CAAA;AAE1E,eAAO,MAAM,OAAO;;;;;;IAUlB,CAAA;AACF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,UAAU;;;iBAIgC,CAAA;AAEvD,eAAO,MAAM,cAAc;;;;;iBAKyC,CAAA;AAIpE,eAAO,MAAM,YAAY,EAAE,KAA6B,CAAA;AAIxD,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAiCxD,CAAA;AAIH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAiBnC,CAAA"}
@@ -63,9 +63,11 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
63
63
  FailedFetchCount: () => [model, []],
64
64
  }));
65
65
  // VIEW
66
- const h = html();
67
- export const view = (model) => h.div([], [
68
- h.span([h.Role('status')], [`count: ${model.count}`]),
69
- h.button([h.OnClick(StartedThreeFetches()), h.Role('button')], ['Start three fetches']),
70
- h.button([h.OnClick(StartedTwoFetchesById()), h.Role('button')], ['Start two fetches by id']),
71
- ]);
66
+ export const view = (model) => {
67
+ const h = html();
68
+ return h.div([], [
69
+ h.span([h.Role('status')], [`count: ${model.count}`]),
70
+ h.button([h.OnClick(StartedThreeFetches()), h.Role('button')], ['Start three fetches']),
71
+ h.button([h.OnClick(StartedTwoFetchesById()), h.Role('button')], ['Start two fetches by id']),
72
+ ]);
73
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"disabledButton.d.ts","sourceRoot":"","sources":["../../../src/test/apps/disabledButton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAMrD,eAAO,MAAM,KAAK;;;;;;;;;;;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,gBAAgB;;;;EAE3B,CAAA;AAEF,eAAO,MAAM,OAAO;;;;IAA4D,CAAA;AAChF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAyBxD,CAAA;AAeH,sCAAsC;AACtC,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAOjC,CAAA;AAEH,8DAA8D;AAC9D,eAAO,MAAM,cAAc,GAAI,OAAO,KAAK,KAAG,IAY3C,CAAA;AAKH,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,KAAG,IAY/C,CAAA"}
1
+ {"version":3,"file":"disabledButton.d.ts","sourceRoot":"","sources":["../../../src/test/apps/disabledButton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAMrD,eAAO,MAAM,KAAK;;;;;;;;;;;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,gBAAgB;;;;EAE3B,CAAA;AAEF,eAAO,MAAM,OAAO;;;;IAA4D,CAAA;AAChF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAyBxD,CAAA;AAgBH,sCAAsC;AACtC,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAUnC,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,cAAc,GAAI,OAAO,KAAK,KAAG,IAe7C,CAAA;AAKD,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,KAAG,IAejD,CAAA"}
@@ -33,28 +33,39 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
33
33
  },
34
34
  }));
35
35
  // VIEW
36
- const h = html();
37
- const submitButton = (isEnabled) => h.button([
38
- h.Class('submit'),
39
- ...(isEnabled ? [h.OnClick(ClickedSubmit())] : [h.Disabled(true)]),
40
- ], ['Submit']);
36
+ const submitButton = (isEnabled) => {
37
+ const h = html();
38
+ return h.button([
39
+ h.Class('submit'),
40
+ ...(isEnabled ? [h.OnClick(ClickedSubmit())] : [h.Disabled(true)]),
41
+ ], ['Submit']);
42
+ };
41
43
  /** Plain view — no dialog wrapper. */
42
- export const view = (model) => h.div([], [
43
- h.button([h.OnClick(ClickedToggle())], ['Toggle']),
44
- submitButton(model.isEnabled),
45
- ]);
44
+ export const view = (model) => {
45
+ const h = html();
46
+ return h.div([], [
47
+ h.button([h.OnClick(ClickedToggle())], ['Toggle']),
48
+ submitButton(model.isEnabled),
49
+ ]);
50
+ };
46
51
  /** View with submit button inside a dialog's panelContent. */
47
- export const viewWithDialog = (model) => h.div([], [
48
- h.button([h.OnClick(ClickedToggle())], ['Toggle']),
49
- Dialog.view({
50
- model: model.dialog,
51
- toParentMessage: (dialogMessage) => GotDialogMessage({ message: dialogMessage }),
52
- panelContent: submitButton(model.isEnabled),
53
- }),
54
- ]);
52
+ export const viewWithDialog = (model) => {
53
+ const h = html();
54
+ return h.div([], [
55
+ h.button([h.OnClick(ClickedToggle())], ['Toggle']),
56
+ Dialog.view({
57
+ model: model.dialog,
58
+ toParentMessage: (dialogMessage) => GotDialogMessage({ message: dialogMessage }),
59
+ panelContent: submitButton(model.isEnabled),
60
+ }),
61
+ ]);
62
+ };
55
63
  /** View using Dialog.lazy with panelContent passed dynamically. */
56
64
  const lazyDialogView = Dialog.lazy({});
57
- export const viewWithLazyDialog = (model) => h.div([], [
58
- h.button([h.OnClick(ClickedToggle())], ['Toggle']),
59
- lazyDialogView(model.dialog, (dialogMessage) => GotDialogMessage({ message: dialogMessage }), submitButton(model.isEnabled)),
60
- ]);
65
+ export const viewWithLazyDialog = (model) => {
66
+ const h = html();
67
+ return h.div([], [
68
+ h.button([h.OnClick(ClickedToggle())], ['Toggle']),
69
+ lazyDialogView(model.dialog, (dialogMessage) => GotDialogMessage({ message: dialogMessage }), submitButton(model.isEnabled)),
70
+ ]);
71
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"fileUpload.d.ts","sourceRoot":"","sources":["../../../src/test/apps/fileUpload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC,IAAI,CAAC,CAAA;CACnC,CAAC,CAAA;AAEF,eAAO,MAAM,YAAY,EAAE,KAA6B,CAAA;AAIxD,eAAO,MAAM,aAAa;;EAA+C,CAAA;AAEzE,eAAO,MAAM,OAAO;;IAA2B,CAAA;AAC/C,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAMrC,CAAA;AAMH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IA2BjC,CAAA"}
1
+ {"version":3,"file":"fileUpload.d.ts","sourceRoot":"","sources":["../../../src/test/apps/fileUpload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC,IAAI,CAAC,CAAA;CACnC,CAAC,CAAA;AAEF,eAAO,MAAM,YAAY,EAAE,KAA6B,CAAA;AAIxD,eAAO,MAAM,aAAa;;EAA+C,CAAA;AAEzE,eAAO,MAAM,OAAO;;IAA2B,CAAA;AAC/C,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAMrC,CAAA;AAIH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IA8BnC,CAAA"}
@@ -11,19 +11,21 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
11
11
  ReceivedFiles: ({ files }) => [{ ...model, receivedFiles: files }, []],
12
12
  }));
13
13
  // VIEW
14
- const h = html();
15
- export const view = (model) => h.div([], [
16
- h.input([
17
- h.Key('file-input'),
18
- h.AriaLabel('resume'),
19
- h.Type('file'),
20
- h.OnFileChange(files => ReceivedFiles({ files })),
21
- ]),
22
- h.div([
23
- h.Key('drop-zone'),
24
- h.AriaLabel('attachments'),
25
- h.OnDropFiles(files => ReceivedFiles({ files })),
26
- ], ['Drop files here']),
27
- h.div([h.Key('received-count')], [`count=${String(model.receivedFiles.length)}`]),
28
- h.div([h.Key('received-names')], [`names=${model.receivedFiles.map(file => file.name).join(',')}`]),
29
- ]);
14
+ export const view = (model) => {
15
+ const h = html();
16
+ return h.div([], [
17
+ h.input([
18
+ h.Key('file-input'),
19
+ h.AriaLabel('resume'),
20
+ h.Type('file'),
21
+ h.OnFileChange(files => ReceivedFiles({ files })),
22
+ ]),
23
+ h.div([
24
+ h.Key('drop-zone'),
25
+ h.AriaLabel('attachments'),
26
+ h.OnDropFiles(files => ReceivedFiles({ files })),
27
+ ], ['Drop files here']),
28
+ h.div([h.Key('received-count')], [`count=${String(model.receivedFiles.length)}`]),
29
+ h.div([h.Key('received-names')], [`names=${model.receivedFiles.map(file => file.name).join(',')}`]),
30
+ ]);
31
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../../../src/test/apps/interactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,eAAO,MAAM,KAAK;;;;;;EAMhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,mBAAmB,iFAA2B,CAAA;AAC3D,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,YAAY,0EAAoB,CAAA;AAC7C,eAAO,MAAM,YAAY,0EAAoB,CAAA;AAC7C,eAAO,MAAM,aAAa;;EAA0C,CAAA;AAEpE,eAAO,MAAM,OAAO;;IAOlB,CAAA;AACF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAcrC,CAAA;AAMH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IA2BjC,CAAA"}
1
+ {"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../../../src/test/apps/interactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAKrD,eAAO,MAAM,KAAK;;;;;;EAMhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,mBAAmB,iFAA2B,CAAA;AAC3D,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,YAAY,0EAAoB,CAAA;AAC7C,eAAO,MAAM,YAAY,0EAAoB,CAAA;AAC7C,eAAO,MAAM,aAAa;;EAA0C,CAAA;AAEpE,eAAO,MAAM,OAAO;;IAOlB,CAAA;AACF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAcrC,CAAA;AAIH,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IA8BnC,CAAA"}