@tempots/dom 31.6.0 → 32.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tempots/dom",
3
- "version": "31.6.0",
3
+ "version": "32.0.0",
4
4
  "type": "module",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.js",
@@ -1,10 +1,13 @@
1
1
  import { Renderable } from '../types/domain';
2
2
  import { DOMContext } from '../dom/dom-context';
3
3
  export type DisposeCallback = (removeTree: boolean, ctx: DOMContext) => void;
4
+ export type WithDispose = {
5
+ dispose: DisposeCallback;
6
+ };
4
7
  /**
5
8
  * Creates a renderable function that will be called when the component is unmounted.
6
9
  * @param fns - The function(s) to be called when the component is unmounted.
7
10
  * @returns A renderable function that takes a DOMContext and returns a function that takes a boolean indicating whether to remove the tree.
8
11
  * @public
9
12
  */
10
- export declare const OnDispose: (...fns: DisposeCallback[]) => Renderable;
13
+ export declare const OnDispose: (...fns: (DisposeCallback | WithDispose)[]) => Renderable;
@@ -5,6 +5,10 @@ import { Value } from '../std/value';
5
5
  /**
6
6
  * Renders the given `renderable` with the provided `ctx` DOM context.
7
7
  *
8
+ * Creates a DisposalScope for automatic signal disposal. All signals created
9
+ * during the renderable execution are tracked and disposed when the clear
10
+ * function is called.
11
+ *
8
12
  * @param renderable - The renderable node to be rendered.
9
13
  * @param ctx - The DOM context to be used for rendering.
10
14
  * @returns A function that can be called to clear the rendered node.
@@ -218,6 +218,7 @@ export declare const style: {
218
218
  gridTemplateRows: (value: NValue<string>) => Renderable;
219
219
  height: (value: NValue<string>) => Renderable;
220
220
  hyphenateCharacter: (value: NValue<string>) => Renderable;
221
+ hyphenateLimitChars: (value: NValue<string>) => Renderable;
221
222
  hyphens: (value: NValue<string>) => Renderable;
222
223
  imageOrientation: (value: NValue<string>) => Renderable;
223
224
  imageRendering: (value: NValue<string>) => Renderable;
@@ -296,7 +297,9 @@ export declare const style: {
296
297
  outlineWidth: (value: NValue<string>) => Renderable;
297
298
  overflow: (value: NValue<string>) => Renderable;
298
299
  overflowAnchor: (value: NValue<string>) => Renderable;
300
+ overflowBlock: (value: NValue<string>) => Renderable;
299
301
  overflowClipMargin: (value: NValue<string>) => Renderable;
302
+ overflowInline: (value: NValue<string>) => Renderable;
300
303
  overflowWrap: (value: NValue<string>) => Renderable;
301
304
  overflowX: (value: NValue<string>) => Renderable;
302
305
  overflowY: (value: NValue<string>) => Renderable;
@@ -20,6 +20,7 @@ export declare const handleValueOrSignal: <T, R>(value: Value<T>, onSignal: (sig
20
20
  * - Setting up a signal listener that re-renders on changes
21
21
  * - Properly cleaning up the old render before creating a new one
22
22
  * - Disposing the signal listener and context on cleanup
23
+ * - Creating a disposal scope for each branch to track signals
23
24
  *
24
25
  * @param ctx - The parent DOM context
25
26
  * @param signal - The signal to watch for changes
@@ -0,0 +1,39 @@
1
+ import { DisposalScope } from '../std/disposal-scope';
2
+ import { Renderable, TNode } from '../types/domain';
3
+ /**
4
+ * Creates a renderable that provides explicit access to a DisposalScope.
5
+ *
6
+ * This is useful when you need to create signals in async contexts (like setTimeout,
7
+ * fetch callbacks, event handlers) where automatic scope tracking doesn't work.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * // Using scope in async context
12
+ * WithScope(scope => {
13
+ * setTimeout(() => {
14
+ * const signal = scope.prop(42)
15
+ * // signal will be disposed when component unmounts
16
+ * }, 1000)
17
+ *
18
+ * return html.div('Loading...')
19
+ * })
20
+ * ```
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Using scope with fetch
25
+ * WithScope(scope => {
26
+ * fetch('/api/data').then(response => {
27
+ * const data = scope.prop(response.data)
28
+ * // data will be disposed when component unmounts
29
+ * })
30
+ *
31
+ * return html.div('Fetching...')
32
+ * })
33
+ * ```
34
+ *
35
+ * @param fn - Function that receives the scope and returns content to render
36
+ * @returns A renderable that manages the scope lifecycle
37
+ * @public
38
+ */
39
+ export declare const WithScope: (fn: (scope: DisposalScope) => TNode) => Renderable;
@@ -0,0 +1,97 @@
1
+ import { AnySignal, Computed, ListenerOptions, Prop } from './signal';
2
+ import { Value } from './value';
3
+ import { ValueTypes } from '../types/domain';
4
+ /**
5
+ * A DisposalScope tracks signals created during its lifetime and disposes them when the scope ends.
6
+ * This enables automatic signal disposal without manual OnDispose() calls.
7
+ *
8
+ * @public
9
+ */
10
+ export declare class DisposalScope {
11
+ private _signals;
12
+ private _callbacks;
13
+ private _disposed;
14
+ /**
15
+ * Register a signal with this scope for automatic disposal.
16
+ *
17
+ * @param signal - The signal to track
18
+ * @throws Error if the scope has already been disposed
19
+ * @throws Error if the signal has already been disposed
20
+ * @public
21
+ */
22
+ track(signal: AnySignal): void;
23
+ /**
24
+ * Register a disposal callback to be called when this scope is disposed.
25
+ * Callbacks are called before signals are disposed.
26
+ * Use this for cleanup that doesn't need the `removeTree` parameter.
27
+ *
28
+ * @param callback - The callback to call on disposal
29
+ * @throws Error if the scope has already been disposed
30
+ * @public
31
+ */
32
+ onDispose(callback: () => void): void;
33
+ /**
34
+ * Dispose all signals tracked by this scope.
35
+ * This method is idempotent - calling it multiple times is safe.
36
+ *
37
+ * @public
38
+ */
39
+ dispose(): void;
40
+ /**
41
+ * Check if this scope has been disposed.
42
+ *
43
+ * @returns true if the scope has been disposed
44
+ * @public
45
+ */
46
+ get disposed(): boolean;
47
+ /**
48
+ * Creates a prop signal and tracks it in this scope.
49
+ * Use this method in async contexts where automatic tracking doesn't work.
50
+ *
51
+ * @param value - The initial value
52
+ * @param equals - Optional equality function
53
+ * @returns A tracked Prop signal
54
+ * @public
55
+ */
56
+ prop<T>(value: T, equals?: (a: T, b: T) => boolean): Prop<T>;
57
+ /**
58
+ * Creates a computed signal and tracks it in this scope.
59
+ * Use this method in async contexts where automatic tracking doesn't work.
60
+ *
61
+ * @param fn - The computation function
62
+ * @param dependencies - Array of signals this computed depends on
63
+ * @param equals - Optional equality function
64
+ * @returns A tracked Computed signal
65
+ * @public
66
+ */
67
+ computed<T>(fn: () => T, dependencies: Array<AnySignal>, equals?: (a: T, b: T) => boolean): Computed<T>;
68
+ /**
69
+ * Creates an effect and tracks it in this scope.
70
+ * Use this method in async contexts where automatic tracking doesn't work.
71
+ *
72
+ * @param fn - The effect function
73
+ * @param signals - Array of signals to listen to
74
+ * @param options - Optional listener options
75
+ * @returns A clear function (the effect itself is tracked in the scope)
76
+ * @public
77
+ */
78
+ effect(fn: () => void, signals: Array<AnySignal>, options?: ListenerOptions): () => void;
79
+ /**
80
+ * Creates a computed signal with curried signature and tracks it in this scope.
81
+ * Use this method in async contexts where automatic tracking doesn't work.
82
+ *
83
+ * @param args - Values or signals to compute from
84
+ * @returns A function that takes the computation function and returns a tracked Computed signal
85
+ * @public
86
+ */
87
+ computedOf<T extends Value<unknown>[]>(...args: T): <O>(fn: (...args: ValueTypes<T>) => O, equals?: (a: O, b: O) => boolean) => Computed<O>;
88
+ /**
89
+ * Creates an effect with curried signature and tracks it in this scope.
90
+ * Use this method in async contexts where automatic tracking doesn't work.
91
+ *
92
+ * @param args - Values or signals to listen to
93
+ * @returns A function that takes the effect function and returns a clear function
94
+ * @public
95
+ */
96
+ effectOf<T extends Value<unknown>[]>(...args: T): (fn: (...args: ValueTypes<T>) => void, options?: ListenerOptions) => (() => void);
97
+ }
@@ -52,5 +52,13 @@ export declare class ElementPosition {
52
52
  * @returns `true` if the element is the last element, `false` otherwise.
53
53
  */
54
54
  get isLast(): Signal<boolean>;
55
+ /**
56
+ * Disposes the internal signal created by `isLast`.
57
+ *
58
+ * **Note:** With automatic signal disposal, this method is now a no-op when used within
59
+ * a disposal scope (e.g., inside a renderable). The signal created by `isLast` is
60
+ * automatically tracked and disposed when the scope ends. This method is kept for
61
+ * backward compatibility and for cases where ElementPosition is used outside a scope.
62
+ */
55
63
  readonly dispose: () => void;
56
64
  }
@@ -0,0 +1,76 @@
1
+ import { DisposalScope } from './disposal-scope';
2
+ /**
3
+ * Global scope stack for tracking active disposal scopes.
4
+ * The last element in the array is the current scope.
5
+ *
6
+ * @internal
7
+ */
8
+ export declare const scopeStack: DisposalScope[];
9
+ /**
10
+ * Push a scope onto the stack, making it the current scope.
11
+ *
12
+ * @param scope - The scope to push
13
+ * @internal
14
+ */
15
+ export declare const pushScope: (scope: DisposalScope) => void;
16
+ /**
17
+ * Pop the current scope from the stack.
18
+ *
19
+ * @throws Error if the stack is empty
20
+ * @internal
21
+ */
22
+ export declare const popScope: () => void;
23
+ /**
24
+ * Get the current active scope.
25
+ *
26
+ * @returns The current scope, or null if no scope is active
27
+ * @public
28
+ */
29
+ export declare const getCurrentScope: () => DisposalScope | null;
30
+ /**
31
+ * Get the full scope stack.
32
+ * Useful for debugging scope hierarchy.
33
+ *
34
+ * @advanced Most users don't need this. Use getCurrentScope() instead.
35
+ * @returns Read-only array of active scopes
36
+ * @public
37
+ */
38
+ export declare const getScopeStack: () => readonly DisposalScope[];
39
+ /**
40
+ * Get the parent scope of the current scope.
41
+ *
42
+ * @advanced Most users don't need this. Accessing parent scopes can lead to
43
+ * unexpected behavior. Only use this for debugging or advanced use cases.
44
+ * @returns The parent scope or null if no parent exists
45
+ * @public
46
+ */
47
+ export declare const getParentScope: () => DisposalScope | null;
48
+ /**
49
+ * Execute a function within a scope context.
50
+ * The scope is pushed before the function executes and popped after.
51
+ * The scope is NOT disposed - the caller is responsible for disposal.
52
+ *
53
+ * @param scope - The scope to use
54
+ * @param fn - The function to execute
55
+ * @returns The result of the function
56
+ * @public
57
+ */
58
+ export declare const withScope: <T>(scope: DisposalScope, fn: () => T) => T;
59
+ /**
60
+ * Execute a function in a new scope and dispose the scope immediately after.
61
+ * Useful for one-off scoped operations.
62
+ *
63
+ * @param fn - The function to execute, receives the scope as parameter
64
+ * @returns The result of the function
65
+ * @public
66
+ */
67
+ export declare const scoped: <T>(fn: (scope: DisposalScope) => T) => T;
68
+ /**
69
+ * Execute a function without any scope tracking.
70
+ * Signals created inside will NOT be automatically tracked.
71
+ *
72
+ * @param fn - The function to execute
73
+ * @returns The result of the function
74
+ * @public
75
+ */
76
+ export declare const untracked: <T>(fn: () => T) => T;
package/std/signal.d.ts CHANGED
@@ -157,6 +157,15 @@ export declare class Signal<T> {
157
157
  * @param options - Options for the listener.
158
158
  */
159
159
  readonly on: (listener: (value: T, previousValue: T | undefined) => void, options?: ListenerOptions) => () => void;
160
+ /**
161
+ * Registers a listener function to be called whenever the value of the signal changes.
162
+ * The listener function will not be called with the current value of the signal.
163
+ * Returns a function that can be called to unregister the listener.
164
+ *
165
+ * @param listener - The listener function to be called when the value of the signal changes.
166
+ * @param options - Options for the listener.
167
+ */
168
+ readonly onChange: (listener: (value: T, previousValue: T) => void, options?: ListenerOptions) => () => void;
160
169
  /**
161
170
  * @internal
162
171
  */
@@ -225,10 +234,22 @@ export declare class Signal<T> {
225
234
  * )
226
235
  * ```
227
236
  *
237
+ * **Auto-Disposal:** The returned computed signal is automatically registered with the current
238
+ * disposal scope (if one exists). When used within a renderable or `WithScope()`, the signal
239
+ * will be automatically disposed when the component unmounts. No manual `OnDispose()` needed!
240
+ *
241
+ * ```typescript
242
+ * const MyComponent: Renderable = (ctx) => {
243
+ * const count = prop(0);
244
+ * const doubled = count.map(x => x * 2); // ✅ Auto-disposed
245
+ * return html.div(doubled);
246
+ * };
247
+ * ```
248
+ *
228
249
  * @typeParam O - The type of the transformed value
229
250
  * @param fn - Function that transforms the signal's value to a new value
230
251
  * @param equals - Optional function to determine if two transformed values are equal (defaults to strict equality)
231
- * @returns A new computed signal with the transformed value
252
+ * @returns A new computed signal with the transformed value (auto-registered with current scope)
232
253
  */
233
254
  readonly map: <O>(fn: (value: T) => O, equals?: (a: O, b: O) => boolean) => Computed<O>;
234
255
  /**
@@ -365,9 +386,28 @@ export declare class Computed<T> extends Signal<T> {
365
386
  */
366
387
  protected _isDirty: boolean;
367
388
  /**
368
- * Represents a Signal object.
369
- * @param _fn - The function that returns the value of the signal.
389
+ * Creates a new Computed signal.
390
+ *
391
+ * **Auto-Registration:** This constructor automatically registers the signal with the current
392
+ * disposal scope (if one exists). This ensures that computed signals created by methods like
393
+ * `.map()`, `.flatMap()`, `.filter()`, etc. are automatically tracked and disposed.
394
+ *
395
+ * When a computed signal is created within a renderable or `WithScope()`, it will be
396
+ * automatically disposed when the component unmounts or the scope is disposed.
397
+ *
398
+ * To create a computed signal that outlives the current scope, use `untracked()`:
399
+ * ```typescript
400
+ * const globalSignal = untracked(() => mySignal.map(x => x * 2));
401
+ * // Remember to dispose manually: globalSignal.dispose()
402
+ * ```
403
+ *
404
+ * @param _fn - The function that computes the value of the signal.
370
405
  * @param equals - The function used to compare two values of type T for equality.
406
+ *
407
+ * @see {@link computed} - Factory function for creating computed signals with explicit dependencies
408
+ * @see {@link Signal.map} - Creates a computed signal by transforming values
409
+ * @see {@link getCurrentScope} - Get the current disposal scope
410
+ * @see {@link untracked} - Create signals outside of scope tracking
371
411
  */
372
412
  constructor(_fn: () => T, equals: (a: T, b: T) => boolean);
373
413
  /**
@@ -390,6 +430,12 @@ export declare class Computed<T> extends Signal<T> {
390
430
  readonly get: () => T;
391
431
  /** {@inheritDoc Signal.value} */
392
432
  get value(): T;
433
+ /**
434
+ * Disposes the computed signal and cancels any pending recomputations.
435
+ * This override increments the schedule count to invalidate all pending
436
+ * microtasks before disposing the signal.
437
+ */
438
+ readonly dispose: () => void;
393
439
  }
394
440
  /**
395
441
  * Represents the data passed to a reducer effect.
package/std/value.d.ts CHANGED
@@ -57,6 +57,14 @@ export declare const Value: {
57
57
  * @param value - The value or Signal instance to dispose of.
58
58
  */
59
59
  dispose: <T>(value: Value<T>) => void;
60
+ /**
61
+ * Returns a function that disposes of a value or a Signal.
62
+ * If the value is a Signal, it returns a function that disposes of the Signal.
63
+ * If the value is not a Signal, it returns a function that does nothing.
64
+ * @param value - The value or Signal instance to dispose of.
65
+ * @returns A function to dispose of the value or Signal.
66
+ */
67
+ disposeFn: <T>(value: Value<T>) => () => void;
60
68
  /**
61
69
  * Derives a Prop from a Signal.
62
70
  * If the value is a Signal, it returns a new Prop with the derived value.