grainjs 1.0.2 → 1.1.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/README.md +23 -71
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/PriorityQueue.d.ts +1 -1
- package/dist/cjs/lib/_computed_queue.js +3 -3
- package/dist/cjs/lib/_computed_queue.js.map +1 -1
- package/dist/cjs/lib/binding.d.ts +11 -4
- package/dist/cjs/lib/binding.js +5 -5
- package/dist/cjs/lib/binding.js.map +1 -1
- package/dist/cjs/lib/computed.d.ts +49 -28
- package/dist/cjs/lib/computed.js +38 -52
- package/dist/cjs/lib/computed.js.map +1 -1
- package/dist/cjs/lib/dispose.d.ts +109 -96
- package/dist/cjs/lib/dispose.js +106 -80
- package/dist/cjs/lib/dispose.js.map +1 -1
- package/dist/cjs/lib/dom.d.ts +38 -18
- package/dist/cjs/lib/dom.js +44 -20
- package/dist/cjs/lib/dom.js.map +1 -1
- package/dist/cjs/lib/domComponent.d.ts +56 -48
- package/dist/cjs/lib/domComponent.js +66 -1
- package/dist/cjs/lib/domComponent.js.map +1 -1
- package/dist/cjs/lib/domComputed.d.ts +31 -21
- package/dist/cjs/lib/domComputed.js +14 -11
- package/dist/cjs/lib/domComputed.js.map +1 -1
- package/dist/cjs/lib/domDispose.d.ts +27 -12
- package/dist/cjs/lib/domDispose.js +26 -11
- package/dist/cjs/lib/domDispose.js.map +1 -1
- package/dist/cjs/lib/domForEach.d.ts +4 -3
- package/dist/cjs/lib/domForEach.js +10 -9
- package/dist/cjs/lib/domForEach.js.map +1 -1
- package/dist/cjs/lib/domImpl.d.ts +33 -10
- package/dist/cjs/lib/domImpl.js +28 -9
- package/dist/cjs/lib/domImpl.js.map +1 -1
- package/dist/cjs/lib/domMethods.d.ts +93 -47
- package/dist/cjs/lib/domMethods.js +88 -46
- package/dist/cjs/lib/domMethods.js.map +1 -1
- package/dist/cjs/lib/domevent.d.ts +87 -62
- package/dist/cjs/lib/domevent.js +84 -59
- package/dist/cjs/lib/domevent.js.map +1 -1
- package/dist/cjs/lib/emit.d.ts +62 -32
- package/dist/cjs/lib/emit.js +67 -53
- package/dist/cjs/lib/emit.js.map +1 -1
- package/dist/cjs/lib/kowrap.d.ts +6 -3
- package/dist/cjs/lib/kowrap.js +6 -3
- package/dist/cjs/lib/kowrap.js.map +1 -1
- package/dist/cjs/lib/obsArray.d.ts +91 -53
- package/dist/cjs/lib/obsArray.js +87 -55
- package/dist/cjs/lib/obsArray.js.map +1 -1
- package/dist/cjs/lib/observable.d.ts +25 -15
- package/dist/cjs/lib/observable.js +29 -18
- package/dist/cjs/lib/observable.js.map +1 -1
- package/dist/cjs/lib/pureComputed.d.ts +12 -15
- package/dist/cjs/lib/pureComputed.js +15 -18
- package/dist/cjs/lib/pureComputed.js.map +1 -1
- package/dist/cjs/lib/styled.d.ts +78 -61
- package/dist/cjs/lib/styled.js +26 -79
- package/dist/cjs/lib/styled.js.map +1 -1
- package/dist/cjs/lib/subscribe.d.ts +41 -37
- package/dist/cjs/lib/subscribe.js +31 -40
- package/dist/cjs/lib/subscribe.js.map +1 -1
- package/dist/cjs/lib/util.js +1 -0
- package/dist/cjs/lib/util.js.map +1 -1
- package/dist/cjs/lib/widgets/input.d.ts +3 -1
- package/dist/cjs/lib/widgets/input.js +6 -4
- package/dist/cjs/lib/widgets/input.js.map +1 -1
- package/dist/cjs/lib/widgets/select.d.ts +4 -2
- package/dist/cjs/lib/widgets/select.js +7 -5
- package/dist/cjs/lib/widgets/select.js.map +1 -1
- package/dist/esm/lib/_computed_queue.js +3 -3
- package/dist/esm/lib/_computed_queue.js.map +1 -1
- package/dist/esm/lib/binding.js +2 -2
- package/dist/esm/lib/binding.js.map +1 -1
- package/dist/esm/lib/computed.js +36 -50
- package/dist/esm/lib/computed.js.map +1 -1
- package/dist/esm/lib/dispose.js +104 -78
- package/dist/esm/lib/dispose.js.map +1 -1
- package/dist/esm/lib/dom.js +38 -18
- package/dist/esm/lib/dom.js.map +1 -1
- package/dist/esm/lib/domComponent.js +65 -0
- package/dist/esm/lib/domComponent.js.map +1 -1
- package/dist/esm/lib/domComputed.js +10 -7
- package/dist/esm/lib/domComputed.js.map +1 -1
- package/dist/esm/lib/domDispose.js +26 -11
- package/dist/esm/lib/domDispose.js.map +1 -1
- package/dist/esm/lib/domForEach.js +3 -2
- package/dist/esm/lib/domForEach.js.map +1 -1
- package/dist/esm/lib/domImpl.js +26 -7
- package/dist/esm/lib/domImpl.js.map +1 -1
- package/dist/esm/lib/domMethods.js +77 -35
- package/dist/esm/lib/domMethods.js.map +1 -1
- package/dist/esm/lib/domevent.js +84 -59
- package/dist/esm/lib/domevent.js.map +1 -1
- package/dist/esm/lib/emit.js +67 -53
- package/dist/esm/lib/emit.js.map +1 -1
- package/dist/esm/lib/kowrap.js +5 -2
- package/dist/esm/lib/kowrap.js.map +1 -1
- package/dist/esm/lib/obsArray.js +82 -50
- package/dist/esm/lib/obsArray.js.map +1 -1
- package/dist/esm/lib/observable.js +26 -15
- package/dist/esm/lib/observable.js.map +1 -1
- package/dist/esm/lib/pureComputed.js +15 -18
- package/dist/esm/lib/pureComputed.js.map +1 -1
- package/dist/esm/lib/styled.js +24 -77
- package/dist/esm/lib/styled.js.map +1 -1
- package/dist/esm/lib/subscribe.js +27 -36
- package/dist/esm/lib/subscribe.js.map +1 -1
- package/dist/esm/lib/util.js +1 -0
- package/dist/esm/lib/util.js.map +1 -1
- package/dist/esm/lib/widgets/input.js +3 -1
- package/dist/esm/lib/widgets/input.js.map +1 -1
- package/dist/esm/lib/widgets/select.js +3 -1
- package/dist/esm/lib/widgets/select.js.map +1 -1
- package/dist/grain-full.debug.js +2146 -3062
- package/dist/grain-full.debug.js.map +7 -0
- package/dist/grain-full.min.js +6 -2
- package/dist/grain-full.min.js.map +7 -1
- package/lib/binding.ts +9 -2
- package/lib/computed.ts +56 -56
- package/lib/dispose.ts +110 -85
- package/lib/dom.ts +39 -20
- package/lib/domComponent.ts +66 -57
- package/lib/domComputed.ts +29 -19
- package/lib/domDispose.ts +28 -11
- package/lib/domForEach.ts +7 -3
- package/lib/domImpl.ts +30 -7
- package/lib/domMethods.ts +101 -46
- package/lib/domevent.ts +85 -60
- package/lib/emit.ts +64 -50
- package/lib/kowrap.ts +5 -2
- package/lib/obsArray.ts +89 -54
- package/lib/observable.ts +26 -15
- package/lib/pureComputed.ts +16 -22
- package/lib/styled.ts +85 -71
- package/lib/subscribe.ts +41 -45
- package/lib/util.ts +1 -0
- package/lib/widgets/input.ts +3 -1
- package/lib/widgets/select.ts +3 -1
- package/package.json +38 -27
package/dist/esm/lib/computed.js
CHANGED
|
@@ -1,48 +1,57 @@
|
|
|
1
|
+
import { setDisposeOwner } from './dispose';
|
|
2
|
+
import { Observable } from './observable';
|
|
3
|
+
import { Subscription } from './subscribe';
|
|
4
|
+
function _noWrite() {
|
|
5
|
+
throw new Error("Can't write to non-writable computed");
|
|
6
|
+
}
|
|
1
7
|
/**
|
|
2
|
-
*
|
|
8
|
+
* `Computed` implements a computed observable, whose value depends on other observables and gets
|
|
3
9
|
* recalculated automatically when they change.
|
|
4
10
|
*
|
|
5
|
-
* E.g. if we have some existing observables (which may themselves be instances of `
|
|
11
|
+
* E.g. if we have some existing observables (which may themselves be instances of `Computed`),
|
|
6
12
|
* we can create a computed that subscribes to them explicitly:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const obs1 = Observable.create(null, 5), obs2 = Observable.create(null, 12);
|
|
15
|
+
* const computed1 = Computed.create(null, obs1, obs2, (use, v1, v2) => v1 + v2);
|
|
16
|
+
* ```
|
|
9
17
|
*
|
|
10
18
|
* or implicitly by using `use(obs)` function:
|
|
11
|
-
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* const computed2 = Computed.create(null, use => use(obs1) + use(obs2));
|
|
21
|
+
* ```
|
|
12
22
|
*
|
|
13
|
-
* In either case, computed1.get() and computed2.get() will have the value 17. If obs1 or
|
|
14
|
-
* changed, computed1 and computed2 will get recomputed automatically.
|
|
23
|
+
* In either case, `computed1.get()` and `computed2.get()` will have the value 17. If `obs1` or
|
|
24
|
+
* `obs2` is changed, `computed1` and `computed2` will get recomputed automatically.
|
|
15
25
|
*
|
|
16
26
|
* Creating a computed allows any number of dependencies to be specified explicitly, and their
|
|
17
|
-
* values will be passed to the read() callback. These may be combined with automatic dependencies
|
|
18
|
-
* detected using use()
|
|
19
|
-
*
|
|
20
|
-
*
|
|
27
|
+
* values will be passed to the `read()` callback. These may be combined with automatic dependencies
|
|
28
|
+
* detected using `use()`. Note that constructor dependencies have less overhead.
|
|
29
|
+
* ```ts
|
|
30
|
+
* const val = Computed.create(null, ...deps, ((use, ...depValues) => READ_CALLBACK));
|
|
31
|
+
* ```
|
|
21
32
|
*
|
|
22
33
|
* You may specify a `write` callback by calling `onWrite(WRITE_CALLBACK)`, which will be called
|
|
23
|
-
* whenever set() is called on the computed by its user. If a `write` bacllback is not specified,
|
|
34
|
+
* whenever `set()` is called on the computed by its user. If a `write` bacllback is not specified,
|
|
24
35
|
* calling `set` on a computed observable will throw an exception.
|
|
25
36
|
*
|
|
26
|
-
* Note that
|
|
37
|
+
* Note that `PureComputed` offers a variation of `Computed` with the same interface, but which
|
|
27
38
|
* stays unsubscribed from dependencies while it itself has no subscribers.
|
|
28
39
|
*
|
|
29
40
|
* A computed may be used with a disposable value using `use.owner` as the value's owner. E.g.
|
|
30
|
-
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* const val = Computed.create(null, ((use) => Foo.create(use.owner, use(a), use(b)));
|
|
43
|
+
* ```
|
|
31
44
|
*
|
|
32
|
-
* When the
|
|
33
|
-
* owned value. Note that only the pattern above works, i.e. use.owner may only be used to take
|
|
45
|
+
* When the `Computed` is re-evaluated, and when it itself is disposed, it disposes the previously
|
|
46
|
+
* owned value. Note that only the pattern above works, i.e. `use.owner` may only be used to take
|
|
34
47
|
* ownership of the same disposable that the callback returns.
|
|
35
48
|
*/
|
|
36
|
-
import { setDisposeOwner } from './dispose';
|
|
37
|
-
import { Observable } from './observable';
|
|
38
|
-
import { Subscription } from './subscribe';
|
|
39
|
-
function _noWrite() {
|
|
40
|
-
throw new Error("Can't write to non-writable computed");
|
|
41
|
-
}
|
|
42
49
|
export class Computed extends Observable {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
static create(owner, ...args) {
|
|
51
|
+
const readCb = args.pop();
|
|
52
|
+
return setDisposeOwner(owner, new Computed(readCb, args));
|
|
53
|
+
}
|
|
54
|
+
// Internal constructor for a Computed observable. You should use computed() function instead.
|
|
46
55
|
constructor(callback, dependencies) {
|
|
47
56
|
// At initialization we force an undefined value even though it's not of type T: it gets set
|
|
48
57
|
// to a proper value during the creation of new Subscription, which calls this._read.
|
|
@@ -51,22 +60,9 @@ export class Computed extends Observable {
|
|
|
51
60
|
this._write = _noWrite;
|
|
52
61
|
this._sub = new Subscription(this._read.bind(this), dependencies, this);
|
|
53
62
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Creates a new Computed, owned by the given owner.
|
|
56
|
-
* @param owner: Object to own this Computed, or null to handle disposal manually.
|
|
57
|
-
* @param ...observables: Zero or more observables on which this computes depends. The callback
|
|
58
|
-
* will get called when any of these changes.
|
|
59
|
-
* @param callback: Read callback that will be called with (use, ...values),
|
|
60
|
-
* i.e. the `use` function and values for all of the ...observables. The callback is called
|
|
61
|
-
* immediately and whenever any dependency changes.
|
|
62
|
-
* @returns {Computed} The newly created computed observable.
|
|
63
|
-
*/
|
|
64
|
-
static create(owner, ...args) {
|
|
65
|
-
const readCb = args.pop();
|
|
66
|
-
return setDisposeOwner(owner, new Computed(readCb, args));
|
|
67
|
-
}
|
|
68
63
|
/**
|
|
69
64
|
* Used by subscriptions to keep track of dependencies.
|
|
65
|
+
* @internal
|
|
70
66
|
*/
|
|
71
67
|
_getDepItem() {
|
|
72
68
|
return this._sub._getDepItem();
|
|
@@ -74,7 +70,7 @@ export class Computed extends Observable {
|
|
|
74
70
|
/**
|
|
75
71
|
* "Sets" the value of the computed by calling the write() callback if one was provided in the
|
|
76
72
|
* constructor. Throws an error if there was no such callback (not a "writable" computed).
|
|
77
|
-
* @param
|
|
73
|
+
* @param value - The value to pass to the write() callback.
|
|
78
74
|
*/
|
|
79
75
|
set(value) { this._write(value); }
|
|
80
76
|
/**
|
|
@@ -96,16 +92,6 @@ export class Computed extends Observable {
|
|
|
96
92
|
super.set(this._callback(use, ...args));
|
|
97
93
|
}
|
|
98
94
|
}
|
|
99
|
-
/**
|
|
100
|
-
* Creates a new Computed.
|
|
101
|
-
* @param {Observable} ...observables: The initial params, of which there may be zero or more, are
|
|
102
|
-
* observables on which this computed depends. When any of them change, the read() callback
|
|
103
|
-
* will be called with the values of these observables as arguments.
|
|
104
|
-
* @param {Function} readCallback: Read callback that will be called with (use, ...values),
|
|
105
|
-
* i.e. the `use` function and values for all of the ...observables. The callback is called
|
|
106
|
-
* immediately and whenever any dependency changes.
|
|
107
|
-
* @returns {Computed} The newly created computed observable.
|
|
108
|
-
*/
|
|
109
95
|
export function computed(...args) {
|
|
110
96
|
const readCb = args.pop();
|
|
111
97
|
return new Computed(readCb, args);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computed.js","sourceRoot":"","sources":["../../../lib/computed.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"computed.js","sourceRoot":"","sources":["../../../lib/computed.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,eAAe,EAAC,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAwB,UAAU,EAAC,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAgB,YAAY,EAAsB,MAAM,aAAa,CAAC;AAE7E,SAAS,QAAQ;IACf,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AAC1D,CAAC;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,OAAO,QAAY,SAAQ,UAAa;IA6BrC,MAAM,CAAC,MAAM,CAAI,KAA0C,EAAE,GAAG,IAAW;QAChF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1B,OAAO,eAAe,CAAC,KAAK,EAAE,IAAI,QAAQ,CAAI,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/D,CAAC;IAMD,8FAA8F;IAC9F,YAAY,QAA2C,EAAE,YAA6B;QACpF,4FAA4F;QAC5F,qFAAqF;QACrF,KAAK,CAAC,SAAgB,CAAC,CAAC;QACxB,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC;IAED;;;OAGG;IACI,WAAW;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,KAAQ,IAAU,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAElD;;;OAGG;IACI,OAAO,CAAC,SAA6B;QAC1C,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACI,OAAO;QACZ,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACpB,KAAK,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,GAAQ,EAAE,GAAG,IAAW;QACpC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAY,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC;CACF;AAkCD,MAAM,UAAU,QAAQ,CAAC,GAAG,IAAW;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC1B,OAAO,IAAI,QAAQ,CAAM,MAAM,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,kDAAkD;AAClD,2FAA2F;AAC3F,4FAA4F;AAC5F,+EAA+E;AAC/E,EAAE;AACF,yDAAyD;AACzD,wFAAwF;AACxF,oEAAoE;AACpE,qEAAqE;AACrE,uFAAuF;AACvF,kGAAkG;AAClG,oCAAoC"}
|
package/dist/esm/lib/dispose.js
CHANGED
|
@@ -1,102 +1,94 @@
|
|
|
1
|
+
import { LLink } from './emit';
|
|
2
|
+
// Internal "owner" of disposable objects which doesn't actually dispose or keep track of them. It
|
|
3
|
+
// is the effective owner when creating a Disposable with `new Foo()` rather than `Foo.create()`.
|
|
4
|
+
const _noopOwner = {
|
|
5
|
+
autoDispose(obj) { },
|
|
6
|
+
};
|
|
7
|
+
// Newly-created Disposable instances will have this as their owner. This is not a constant, it
|
|
8
|
+
// is used by create() for the safe creation of Disposables.
|
|
9
|
+
let _defaultDisposableOwner = _noopOwner;
|
|
1
10
|
/**
|
|
2
|
-
*
|
|
3
|
-
* unsubscribe from events. The motivation with examples is presented here:
|
|
11
|
+
* Base class for disposable objects that can own other objects.
|
|
4
12
|
*
|
|
5
|
-
*
|
|
13
|
+
* For background and motivation, see [Disposables](/dispose).
|
|
6
14
|
*
|
|
7
|
-
* Disposable is a class for components that need cleanup (e.g. maintain DOM, listen to events,
|
|
8
|
-
* subscribe to anything). It provides a
|
|
9
|
-
* component, and
|
|
15
|
+
* `Disposable` is a class for components that need cleanup (e.g. maintain DOM, listen to events,
|
|
16
|
+
* subscribe to anything). It provides a `.dispose()` method that should be called to destroy the
|
|
17
|
+
* component, and `.onDispose()` / `.autoDispose()` methods that the component should use to take
|
|
10
18
|
* responsibility for other pieces that require cleanup.
|
|
11
19
|
*
|
|
12
20
|
* To define a disposable class:
|
|
13
|
-
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* class Foo extends Disposable { ... }
|
|
23
|
+
* ```
|
|
14
24
|
*
|
|
15
|
-
* To create Foo
|
|
16
|
-
*
|
|
25
|
+
* To create `Foo`:
|
|
26
|
+
* ```ts
|
|
27
|
+
* const foo = Foo.create(owner, ...args);
|
|
28
|
+
* ```
|
|
17
29
|
* This is better than `new Foo` for two reasons:
|
|
18
|
-
* 1. If Foo's constructor throws an exception, any disposals registered in that constructor
|
|
30
|
+
* 1. If `Foo`'s constructor throws an exception, any disposals registered in that constructor
|
|
19
31
|
* before the exception are honored.
|
|
20
32
|
* 2. It ensures you specify the owner of the new instance (but you can use null to skip it).
|
|
21
33
|
*
|
|
22
|
-
* In Foo's constructor (or rarely methods), take ownership of other Disposable objects:
|
|
23
|
-
*
|
|
34
|
+
* In `Foo`'s constructor (or rarely methods), take ownership of other Disposable objects:
|
|
35
|
+
* ```ts
|
|
36
|
+
* this.bar = Bar.create(this, ...);
|
|
37
|
+
* ```
|
|
24
38
|
*
|
|
25
39
|
* For objects that are not instances of Disposable but have a .dispose() methods, use:
|
|
26
|
-
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* this.bar = this.autoDispose(createSomethingDisposable());
|
|
42
|
+
* ```
|
|
27
43
|
*
|
|
28
44
|
* To call a function on disposal (e.g. to add custom disposal logic):
|
|
29
|
-
*
|
|
30
|
-
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* this.onDispose(() => this.myUnsubscribeAllMethod());
|
|
47
|
+
* this.onDispose(this.myUnsubscribeAllMethod, this);
|
|
48
|
+
* ```
|
|
31
49
|
*
|
|
32
50
|
* To mark this object to be wiped out on disposal (i.e. set all properties to null):
|
|
33
|
-
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* this.wipeOnDispose();
|
|
53
|
+
* ```
|
|
34
54
|
* See the documentation of that method for more info.
|
|
35
55
|
*
|
|
36
|
-
* To dispose Foo directly:
|
|
37
|
-
* foo.dispose();
|
|
38
|
-
* To determine if an object has already been disposed:
|
|
39
|
-
* foo.isDisposed()
|
|
56
|
+
* To dispose Foo directly: `foo.dispose()`.
|
|
40
57
|
*
|
|
41
|
-
*
|
|
42
|
-
* this._holder = Holder.create(this);
|
|
43
|
-
* Bar.create(this._holder, 1); // creates new Bar(1)
|
|
44
|
-
* Bar.create(this._holder, 2); // creates new Bar(2) and disposes previous object
|
|
45
|
-
* this._holder.clear(); // disposes contained object
|
|
46
|
-
* this._holder.release(); // releases contained object
|
|
58
|
+
* To determine if an object has already been disposed: `foo.isDisposed()`.
|
|
47
59
|
*
|
|
48
|
-
* If you need
|
|
49
|
-
*
|
|
50
|
-
* Bar.create(this._mholder, 1); // create new Bar(1)
|
|
51
|
-
* Bar.create(this._mholder, 2); // create new Bar(2)
|
|
52
|
-
* this._mholder.dispose(); // disposes both objects
|
|
60
|
+
* If you need to replace an owned object, or release, or dispose it early, use a
|
|
61
|
+
* [`Holder`](#Holder) or [`MultiHolder`](#MultiHolder).
|
|
53
62
|
*
|
|
54
|
-
* If creating your own class with a dispose() method, do NOT throw exceptions from dispose()
|
|
55
|
-
* These cannot be handled properly in all cases.
|
|
56
|
-
* http://bin-login.name/ftp/pub/docs/programming_languages/cpp/cffective_cpp/MAGAZINE/SU_FRAME.HTM#destruct
|
|
63
|
+
* If creating your own class with a `dispose()` method, do NOT throw exceptions from `dispose()`.
|
|
64
|
+
* These cannot be handled properly in all cases.
|
|
57
65
|
*
|
|
58
66
|
* Using a parametrized (generic) class as a Disposable is tricky. E.g.
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* class Bar<T> extends Disposable { ... }
|
|
69
|
+
* // Bar<T>.create(...) <-- doesn't work
|
|
70
|
+
* // Bar.create<T>(...) <-- doesn't work
|
|
71
|
+
* // Bar.create(...) <-- works, but with {} for Bar's type parameters
|
|
72
|
+
* ```
|
|
63
73
|
*
|
|
64
74
|
* The solution is to expose the constructor type using a helper method:
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Internal "owner" of disposable objects which doesn't actually dispose or keep track of them. It
|
|
74
|
-
// is the effective owner when creating a Disposable with `new Foo()` rather than `Foo.create()`.
|
|
75
|
-
const _noopOwner = {
|
|
76
|
-
autoDispose(obj) { },
|
|
77
|
-
};
|
|
78
|
-
// Newly-created Disposable instances will have this as their owner. This is not a constant, it
|
|
79
|
-
// is used by create() for the safe creation of Disposables.
|
|
80
|
-
let _defaultDisposableOwner = _noopOwner;
|
|
81
|
-
/**
|
|
82
|
-
* Base class for disposable objects that can own other objects. See the module documentation.
|
|
75
|
+
* ```ts
|
|
76
|
+
* class Bar<T> extends Disposable {
|
|
77
|
+
* // Note the tuple below which must match the constructor parameters of Bar<U>.
|
|
78
|
+
* public static ctor<U>(): IDisposableCtor<Bar<U>, [U, boolean]> { return this; }
|
|
79
|
+
* constructor(a: T, b: boolean) { ... }
|
|
80
|
+
* }
|
|
81
|
+
* Bar.ctor<T>().create(...) // <-- works, creates Bar<T>, and does type-checking!
|
|
82
|
+
* ```
|
|
83
83
|
*/
|
|
84
84
|
export class Disposable {
|
|
85
|
-
constructor() {
|
|
86
|
-
this._disposalList = new DisposalList();
|
|
87
|
-
// This registers with a temp Holder when using create(), and is a no-op when using `new Foo`.
|
|
88
|
-
_defaultDisposableOwner.autoDispose(this);
|
|
89
|
-
// Be sure to reset to no-op, so that a (non-recommended) direct call like 'new Bar()', from
|
|
90
|
-
// inside Foo's constructor doesn't use the same Holder that's temporarily holding Foo.
|
|
91
|
-
_defaultDisposableOwner = _noopOwner;
|
|
92
|
-
}
|
|
93
85
|
/**
|
|
94
86
|
* Create Disposable instances using `Class.create(owner, ...)` rather than `new Class(...)`.
|
|
95
87
|
*
|
|
96
88
|
* This reminds you to provide an owner, and ensures that if the constructor throws an
|
|
97
|
-
* exception, dispose() gets called to clean up the partially-constructed object.
|
|
89
|
+
* exception, `dispose()` gets called to clean up the partially-constructed object.
|
|
98
90
|
*
|
|
99
|
-
* Owner may be null if intend to ensure disposal some other way.
|
|
91
|
+
* Owner may be `null` if you intend to ensure disposal some other way.
|
|
100
92
|
*/
|
|
101
93
|
static create(owner, ...args) {
|
|
102
94
|
const origDefaultOwner = _defaultDisposableOwner;
|
|
@@ -124,12 +116,20 @@ export class Disposable {
|
|
|
124
116
|
_defaultDisposableOwner = origDefaultOwner;
|
|
125
117
|
}
|
|
126
118
|
}
|
|
127
|
-
|
|
119
|
+
constructor() {
|
|
120
|
+
this._disposalList = new DisposalList();
|
|
121
|
+
// This registers with a temp Holder when using create(), and is a no-op when using `new Foo`.
|
|
122
|
+
_defaultDisposableOwner.autoDispose(this);
|
|
123
|
+
// Be sure to reset to no-op, so that a (non-recommended) direct call like 'new Bar()', from
|
|
124
|
+
// inside Foo's constructor doesn't use the same Holder that's temporarily holding Foo.
|
|
125
|
+
_defaultDisposableOwner = _noopOwner;
|
|
126
|
+
}
|
|
127
|
+
/** Take ownership of `obj`, and dispose it when `this.dispose()` is called. */
|
|
128
128
|
autoDispose(obj) {
|
|
129
129
|
this.onDispose(obj.dispose, obj);
|
|
130
130
|
return obj;
|
|
131
131
|
}
|
|
132
|
-
/** Call the given callback when this.dispose() is called. */
|
|
132
|
+
/** Call the given callback when `this.dispose()` is called. */
|
|
133
133
|
onDispose(callback, context) {
|
|
134
134
|
return this._disposalList.addListener(callback, context);
|
|
135
135
|
}
|
|
@@ -138,10 +138,13 @@ export class Disposable {
|
|
|
138
138
|
* recommended to call this early in the constructor.
|
|
139
139
|
*
|
|
140
140
|
* This makes disposal more costly, but has certain benefits:
|
|
141
|
+
*
|
|
141
142
|
* - If anything still refers to the object and uses it, we'll get an early error, rather than
|
|
142
143
|
* silently keep going, potentially doing useless work (or worse) and wasting resources.
|
|
144
|
+
*
|
|
143
145
|
* - If anything still refers to the object (even without using it), the fields of the object
|
|
144
146
|
* can still be garbage-collected.
|
|
147
|
+
*
|
|
145
148
|
* - If there are circular references involving this object, they get broken, making the job
|
|
146
149
|
* easier for the garbage collector.
|
|
147
150
|
*
|
|
@@ -158,7 +161,7 @@ export class Disposable {
|
|
|
158
161
|
return this._disposalList === null;
|
|
159
162
|
}
|
|
160
163
|
/**
|
|
161
|
-
* Clean up `this` by disposing all owned objects, and calling onDispose() callbacks, in reverse
|
|
164
|
+
* Clean up `this` by disposing all owned objects, and calling `onDispose()` callbacks, in reverse
|
|
162
165
|
* order to that in which they were added.
|
|
163
166
|
*/
|
|
164
167
|
dispose() {
|
|
@@ -187,17 +190,31 @@ export class Disposable {
|
|
|
187
190
|
}
|
|
188
191
|
/**
|
|
189
192
|
* Holder keeps a single disposable object. If given responsibility for another object using
|
|
190
|
-
* holder.autoDispose() or Foo.create(holder, ...)
|
|
193
|
+
* `holder.autoDispose()` or `Foo.create(holder, ...)`, it automatically disposes the currently held
|
|
191
194
|
* object. It also disposes it when the holder itself is disposed.
|
|
192
195
|
*
|
|
193
|
-
* If the object is an instance of Disposable
|
|
196
|
+
* If the object is an instance of `Disposable`, the holder will also notice when the object gets
|
|
194
197
|
* disposed from outside of it, in which case the holder will become empty again.
|
|
198
|
+
*
|
|
199
|
+
* If you need a container for multiple objects and dispose them all together, use a `MultiHolder`:
|
|
200
|
+
*
|
|
201
|
+
* :::info Example
|
|
202
|
+
* ```ts
|
|
203
|
+
* this._holder = Holder.create(this);
|
|
204
|
+
* Bar.create(this._holder, 1); // creates new Bar(1), assuming it's a Disposable
|
|
205
|
+
* Bar.create(this._holder, 2); // creates new Bar(2) and disposes previous object
|
|
206
|
+
* this._holder.clear(); // disposes contained object
|
|
207
|
+
* this._holder.release(); // releases contained object
|
|
208
|
+
* ```
|
|
209
|
+
* :::
|
|
195
210
|
*/
|
|
196
211
|
export class Holder {
|
|
197
212
|
constructor() {
|
|
213
|
+
/** @internal */
|
|
198
214
|
this._owned = null;
|
|
199
215
|
this._disposalListener = undefined;
|
|
200
216
|
}
|
|
217
|
+
/** `Holder.create(owner)` creates a new `Holder`. */
|
|
201
218
|
static create(owner) {
|
|
202
219
|
return setDisposeOwner(owner, new Holder());
|
|
203
220
|
}
|
|
@@ -246,9 +263,18 @@ export class Holder {
|
|
|
246
263
|
}
|
|
247
264
|
}
|
|
248
265
|
/**
|
|
249
|
-
* MultiHolder keeps multiple disposable
|
|
250
|
-
* itself is disposed. It's actually nothing more than the Disposable base class itself, just
|
|
266
|
+
* `MultiHolder` keeps multiple disposable objects. It disposes all held object when the holder
|
|
267
|
+
* itself is disposed. It's actually nothing more than the `Disposable` base class itself, just
|
|
251
268
|
* exposed with a clearer name that describes its purpose.
|
|
269
|
+
*
|
|
270
|
+
* :::info Example
|
|
271
|
+
* ```ts
|
|
272
|
+
* this._mholder = MultiHolder.create(null);
|
|
273
|
+
* Bar.create(this._mholder, 1); // create new Bar(1)
|
|
274
|
+
* Bar.create(this._mholder, 2); // create new Bar(2)
|
|
275
|
+
* this._mholder.dispose(); // disposes both objects
|
|
276
|
+
* ```
|
|
277
|
+
* :::
|
|
252
278
|
*/
|
|
253
279
|
export class MultiHolder extends Disposable {
|
|
254
280
|
}
|
|
@@ -296,11 +322,6 @@ class DisposalList extends LLink {
|
|
|
296
322
|
* reports and swallows erros when it calls the callbacks in the list.
|
|
297
323
|
*/
|
|
298
324
|
class DisposeListener extends LLink {
|
|
299
|
-
constructor(callback, context) {
|
|
300
|
-
super();
|
|
301
|
-
this.callback = callback;
|
|
302
|
-
this.context = context;
|
|
303
|
-
}
|
|
304
325
|
static callAll(begin, end, owner) {
|
|
305
326
|
while (begin !== end) {
|
|
306
327
|
const lis = begin;
|
|
@@ -314,6 +335,11 @@ class DisposeListener extends LLink {
|
|
|
314
335
|
begin = lis._next;
|
|
315
336
|
}
|
|
316
337
|
}
|
|
338
|
+
constructor(callback, context) {
|
|
339
|
+
super();
|
|
340
|
+
this.callback = callback;
|
|
341
|
+
this.context = context;
|
|
342
|
+
}
|
|
317
343
|
dispose() {
|
|
318
344
|
if (this.isDisposed()) {
|
|
319
345
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispose.js","sourceRoot":"","sources":["../../../lib/dispose.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dispose.js","sourceRoot":"","sources":["../../../lib/dispose.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/lib/dom.js
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dom.js provides a way to build a DOM tree easily.
|
|
3
|
-
*
|
|
4
|
-
* E.g.
|
|
5
|
-
* import {dom} from 'grainjs';
|
|
6
|
-
* dom('a#link.c1.c2', {'href': url}, 'Hello ', dom('span', 'world'));
|
|
7
|
-
* creates Node <a id="link" class="c1 c2" href={{url}}Hello <span>world</span></a>.
|
|
8
|
-
*
|
|
9
|
-
* dom.frag(dom('span', 'Hello'), ['blah', dom('div', 'world')])
|
|
10
|
-
* creates document fragment with <span>Hello</span>blah<div>world</div>.
|
|
11
|
-
*
|
|
12
|
-
* DOM can also be created and modified inline during creation:
|
|
13
|
-
* dom('a#id.c1',
|
|
14
|
-
* dom.cls('c2'), dom.attr('href', url),
|
|
15
|
-
* dom.text('Hello '), dom('span', dom.text('world')))
|
|
16
|
-
* creates Node <a id="link" class="c1 c2" href={{url}}Hello <span>world</span></a>,
|
|
17
|
-
* identical to the first example above.
|
|
18
|
-
*/
|
|
19
1
|
// We keep various dom-related functions organized in private modules, but they are exposed here.
|
|
20
2
|
export * from './domImpl';
|
|
21
3
|
export * from './domComponent';
|
|
@@ -32,6 +14,44 @@ import * as _domImpl from './domImpl';
|
|
|
32
14
|
import * as _domMethods from './domMethods';
|
|
33
15
|
import * as domevent from './domevent';
|
|
34
16
|
import { dom as _dom } from './domImpl';
|
|
17
|
+
/**
|
|
18
|
+
* `dom()` provides a way to build a DOM tree easily.
|
|
19
|
+
*
|
|
20
|
+
* The first argument is a string consisting of a lowercase tag name (e.g. `"div"`), with optional
|
|
21
|
+
* `"#foo"` suffix to add the ID `'foo'`, and zero or more `".bar"` suffixes to add a CSS class
|
|
22
|
+
* `'bar'`.
|
|
23
|
+
*
|
|
24
|
+
* The rest of the arguments are optional and may be any number, of these types:
|
|
25
|
+
*
|
|
26
|
+
* @param Nodes - become children of the created element
|
|
27
|
+
* @param strings - become text node children
|
|
28
|
+
* @param objects - Literal objects to set string attributes on the element.
|
|
29
|
+
* E.g. `{title: "foo"}`.
|
|
30
|
+
* @param null - The values `null` and `undefined` values are ignored completely.
|
|
31
|
+
* @param Arrays - flattened with each item processed recursively
|
|
32
|
+
* @param functions - called with the element being built as the argument, for a chance to modify
|
|
33
|
+
* the element as it's being created. Return values are processed recursively.
|
|
34
|
+
* @param functions - "dom methods" are a expressions such as `dom.attr('href', url)` or
|
|
35
|
+
* `dom.hide(obs)`, which are special cases of the "functions" category.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import {dom} from 'grainjs';
|
|
40
|
+
* dom('a', {href: url, className: 'myclass'}, 'Hello ', dom('strong', 'world'));
|
|
41
|
+
* ```
|
|
42
|
+
* creates HTML element `<a href={{url}} class="myclass">Hello <strong>world</strong></a>`.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* Here's an example equivalent to the one above, using dom methods `dom.cls`, `dom.attr`,
|
|
46
|
+
* `dom.text`. In reality, these methods are useful with observable values rather than constant
|
|
47
|
+
* strings.
|
|
48
|
+
* ```ts
|
|
49
|
+
* dom('a', dom.attr('href', url), dom.cls('myclass'),
|
|
50
|
+
* dom.text('Hello '), dom('strong', dom.text('world')));
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @see [DOM & Observables](/basics).
|
|
54
|
+
*/
|
|
35
55
|
// We just want to re-export _domImpl.dom, but to allow adding methods to it in a typesafe way,
|
|
36
56
|
// TypeScript wants us to declare a real function in the same file.
|
|
37
57
|
export function dom(tagString, ...args) {
|
package/dist/esm/lib/dom.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../../../lib/dom.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../../../lib/dom.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,69 @@
|
|
|
1
1
|
import { domComputedOwned } from './domComputed';
|
|
2
|
+
/**
|
|
3
|
+
* UI components that can be inserted into `dom()`.
|
|
4
|
+
*
|
|
5
|
+
* Components are created and inserted using `dom.create()`:
|
|
6
|
+
* ```ts
|
|
7
|
+
* dom('div',
|
|
8
|
+
* dom.create(MyWidget, ...myArgs), // Calls MyWidget.create(owner, ...myArgs)
|
|
9
|
+
* dom.create(createMyWidget, ...myArgs), // Calls createMyWidget(owner, ...myArgs)
|
|
10
|
+
* )
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The first argument may be a function, which is called directly, or a class with a `.create()`
|
|
14
|
+
* static method, in which case that's what gets called.
|
|
15
|
+
*
|
|
16
|
+
* In both cases, the call gets a first argument of `owner` followed by the rest of the arguments
|
|
17
|
+
* to `dom.create()`. The `owner` is a `MultiHolder` that will own this component. This works
|
|
18
|
+
* naturally with any class that derives from Disposable, since it then has a suitable static
|
|
19
|
+
* `create()` method.
|
|
20
|
+
*
|
|
21
|
+
* Function-based components may use owner to easily handle disposal. For example:
|
|
22
|
+
* ```ts
|
|
23
|
+
* dom.create(createMyWidget)
|
|
24
|
+
* function createMyWidget(owner) {
|
|
25
|
+
* const foo = Foo.create(owner);
|
|
26
|
+
* return dom('div', foo.getTitle());
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* The `owner` argument is the main benefit of `dom.create()`. Logically, the owner is the DOM where
|
|
31
|
+
* the component is attached. When the parent DOM element is disposed, so is the component.
|
|
32
|
+
*
|
|
33
|
+
* :::info Explanation
|
|
34
|
+
*
|
|
35
|
+
* To understand why the syntax is such, consider a potential alternative such as:
|
|
36
|
+
* ```ts
|
|
37
|
+
* dom('div', _insert_(new Comp1()), _insert_(new Comp2(...args)))
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* In both cases, the constructor for Comp1 runs before the constructor for Comp2. What happens
|
|
41
|
+
* when Comp2's constructor throws an exception? In the second case, nothing yet owns the
|
|
42
|
+
* created Comp1 component, and it will never get cleaned up. With `dom.create()`, the DOM
|
|
43
|
+
* gets ownership of Comp1 early enough and will dispose it.
|
|
44
|
+
*
|
|
45
|
+
* :::
|
|
46
|
+
*
|
|
47
|
+
* A function component may return DOM directly. A class component returns the class instance,
|
|
48
|
+
* which must have a `.buildDom()` method which will be called right after the constructor to get
|
|
49
|
+
* the DOM. Note that buildDom is only called once.
|
|
50
|
+
*
|
|
51
|
+
* A function component may also return an object with `.buildDom()`. So these are equivalent:
|
|
52
|
+
* ```ts
|
|
53
|
+
* dom.create(MyWidget)
|
|
54
|
+
* dom.create((owner) => MyWidget.create(owner))
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* Note that ownership should be handled using the `owner` argument. Don't do this:
|
|
58
|
+
* ```ts
|
|
59
|
+
* // NON-EXAMPLE: Nothing will dispose the created object:
|
|
60
|
+
* // dom.create(() => new MyWidget());
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* The returned DOM may includes Nodes, strings, and `domComputed()` values, as well as arrays of
|
|
64
|
+
* any of these. In other words, any `DomArg` goes except `DomMethods`. All the DOM returned will be
|
|
65
|
+
* disposed when the containing element is disposed, followed by the `owner` itself.
|
|
66
|
+
*/
|
|
2
67
|
export function create(fn, ...args) {
|
|
3
68
|
return domComputedOwned(null, (owner) => {
|
|
4
69
|
const value = ('create' in fn) ?
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domComponent.js","sourceRoot":"","sources":["../../../lib/domComponent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"domComponent.js","sourceRoot":"","sources":["../../../lib/domComponent.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,gBAAgB,EAAc,MAAM,eAAe,CAAC;AAuB5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,MAAM,UAAU,MAAM,CAAgC,EAAM,EAAE,GAAG,IAAwB;IACvF,OAAO,gBAAgB,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;QACtC,MAAM,KAAK,GAAuB,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;YACjD,EAA6B,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;YACtD,EAA4B,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,OAAO,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC;YAClE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC"}
|