aberdeen 0.2.4 → 0.4.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.
@@ -1,9 +1,17 @@
1
1
  /**
2
- * Schedule a DOM read operation to be executed in Aberdeen's internal task queue.
2
+ * Normally, changes to `Store`s are reacted to asynchronously, in an (optimized)
3
+ * batch, after a timeout of 0s. Calling `runQueue()` will do so immediately
4
+ * and synchronously. Doing so may be helpful in cases where you need some DOM
5
+ * modification to be done synchronously.
3
6
  *
4
- * This function is used to batch DOM read operations together, avoiding unnecessary
5
- * layout recalculations and improving browser performance. A DOM read operation should
6
- * only *read* from the DOM, such as measuring element dimensions or retrieving computed styles.
7
+ * This function is re-entrant, meaning it is safe to call `runQueue` from a
8
+ * function that is called due to another (automatic) invocation of `runQueue`.
9
+ */
10
+ export declare function runQueue(): void;
11
+ /**
12
+ * A promise-like object that you can `await`. It will resolve *after* the current batch
13
+ * of DOM-write operations has completed. This is the best time to retrieve DOM properties
14
+ * that dependent on a layout being completed, such as `offsetHeight`.
7
15
  *
8
16
  * By batching DOM reads separately from DOM writes, this prevents the browser from
9
17
  * interleaving layout reads and writes, which can force additional layout recalculations.
@@ -13,15 +21,16 @@
13
21
  * Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM read
14
22
  * operations happen before any DOM writes in the same queue cycle, minimizing layout thrashing.
15
23
  *
16
- * @param func The function to be executed as a DOM read operation.
24
+ * See `transitions.js` for some examples.
17
25
  */
18
- export declare function scheduleDomReader(func: () => void): void;
26
+ export declare const DOM_READ_PHASE: {
27
+ then: (fulfilled: () => void) => any;
28
+ };
19
29
  /**
20
- * Schedule a DOM write operation to be executed in Aberdeen's internal task queue.
21
- *
22
- * This function is used to batch DOM write operations together, avoiding unnecessary
23
- * layout recalculations and improving browser performance. A DOM write operation should
24
- * only *write* to the DOM, such as modifying element properties or applying styles.
30
+ * A promise-like object that you can `await`. It will resolve *after* the current
31
+ * DOM_READ_PHASE has completed (if any) and after any DOM triggered by Aberdeen
32
+ * have completed. This is a good time to do little manual DOM tweaks that depend
33
+ * on a *read phase* first, like triggering transitions.
25
34
  *
26
35
  * By batching DOM writes separately from DOM reads, this prevents the browser from
27
36
  * interleaving layout reads and writes, which can force additional layout recalculations.
@@ -31,34 +40,35 @@ export declare function scheduleDomReader(func: () => void): void;
31
40
  * Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM write
32
41
  * operations happen after all DOM reads in the same queue cycle, minimizing layout thrashing.
33
42
  *
34
- * @param func The function to be executed as a DOM write operation.
35
- */
36
- export declare function scheduleDomWriter(func: () => void): void;
37
- /**
38
- * A data store that automatically subscribes the current scope to updates
39
- * whenever data is read from it.
40
- *
41
- * Supported data types are: `string`, `number`, `boolean`, `undefined`, `null`,
42
- * `Array`, `object` and `Map`. The latter three will always have `Store` objects as
43
- * values, creating a tree of `Store`-objects.
43
+ * See `transitions.js` for some examples.
44
44
  */
45
+ export declare const DOM_WRITE_PHASE: {
46
+ then: (fulfilled: () => void) => any;
47
+ };
48
+ export interface Store {
49
+ /**
50
+ * Return a `Store` deeper within the tree by resolving the given `path`,
51
+ * subscribing to every level.
52
+ * In case `undefined` is encountered while resolving the path, a newly
53
+ * created `Store` containing `undefined` is returned. In that case, the
54
+ * `Store`'s [[`isDetached`]] method will return `true`.
55
+ * In case something other than a collection is encountered, an error is thrown.
56
+ */
57
+ (...path: any[]): Store;
58
+ }
45
59
  export declare class Store {
46
60
  /**
47
- * Create a new store with the given `value` as its value. Defaults to `undefined` if no value is given.
48
- * When the value is a plain JavaScript object, an `Array` or a `Map`, it will be stored as a tree of
49
- * `Store`s. (Calling {@link Store.get} on the store will recreate the original data strucure, though.)
50
- *
51
- * @example
52
- * ```
53
- * let emptyStore = new Store()
54
- * let numStore = new Store(42)
55
- * let objStore = new Store({x: {alice: 1, bob: 2}, y: [9,7,5,3,1]})
56
- * ```
57
- */
61
+ * Create a new `Store` with `undefined` as its initial value.
62
+ */
58
63
  constructor();
64
+ /**
65
+ * Create a new `Store`.
66
+ * @param value The initial value. Plain objects, arrays and `Map`s, are converted
67
+ * into a tree of nested `Store`s. When another `Store` is included somewhere in that
68
+ * input tree, a reference is made.
69
+ */
59
70
  constructor(value: any);
60
71
  /**
61
- *
62
72
  * @returns The index for this Store within its parent collection. This will be a `number`
63
73
  * when the parent collection is an array, a `string` when it's an object, or any data type
64
74
  * when it's a `Map`.
@@ -67,135 +77,124 @@ export declare class Store {
67
77
  * ```
68
78
  * let store = new Store({x: 123})
69
79
  * let subStore = store.ref('x')
70
- * assert(subStore.get() === 123)
71
- * assert(subStore.index() === 'x') // <----
80
+ * subStore.get() // 123
81
+ * subStore.index() // 'x'
72
82
  * ```
73
83
  */
74
84
  index(): any;
75
85
  /**
76
- * @returns Resolves `path` and then retrieves the value that is there, subscribing
77
- * to all read `Store` values. If `path` does not exist, `undefined` is returned.
78
- * @param path - Any path terms to resolve before retrieving the value.
79
- * @example
80
- * ```
81
- * let store = new Store({a: {b: {c: {d: 42}}}})
82
- * assert('a' in store.get())
83
- * assert(store.get('a', 'b') === {c: {d: 42}})
84
- * ```
86
+ * Retrieve the value for store, subscribing the observe scope to changes.
87
+ *
88
+ * @param depth Limit the depth of the retrieved data structure to this positive integer.
89
+ * When `depth` is `1`, only a single level of the value at `path` is unpacked. This
90
+ * makes no difference for primitive values (like strings), but for objects, maps and
91
+ * arrays, it means that each *value* in the resulting data structure will be a
92
+ * reference to the `Store` for that value.
93
+ *
94
+ * @returns The resulting value (or `undefined` if the `Store` does not exist).
95
+ */
96
+ get(depth?: number): any;
97
+ /**
98
+ * Exactly like {@link Store.get}, except that when executed from an observe scope,
99
+ * we will not subscribe to changes in the data retrieved data.
85
100
  */
86
- get(...path: any[]): any;
101
+ peek(depth?: number): any;
87
102
  /**
88
- * Like {@link Store.get}, but doesn't subscribe to changes.
103
+ * Like {@link Store.get}, but with return type checking.
104
+ *
105
+ * @param expectType A string specifying what type the.get is expected to return. Options are:
106
+ * "undefined", "null", "boolean", "number", "string", "function", "array", "map"
107
+ * and "object". If the store holds a different type of value, a `TypeError`
108
+ * exception is thrown.
109
+ * @returns
89
110
  */
90
- peek(...path: any[]): any;
111
+ getTyped(expectType: String, depth?: number): any;
91
112
  /**
92
113
  * @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `number`.
93
114
  * Using this instead of just {@link Store.get} is especially useful from within TypeScript.
94
115
  */
95
- getNumber(...path: any[]): number;
116
+ getNumber(): number;
96
117
  /**
97
118
  * @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `string`.
98
119
  * Using this instead of just {@link Store.get} is especially useful from within TypeScript.
99
120
  */
100
- getString(...path: any[]): string;
121
+ getString(): string;
101
122
  /**
102
123
  * @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `boolean`.
103
124
  * Using this instead of just {@link Store.get} is especially useful from within TypeScript.
104
125
  */
105
- getBoolean(...path: any[]): boolean;
126
+ getBoolean(): boolean;
106
127
  /**
107
128
  * @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `function`.
108
129
  * Using this instead of just {@link Store.get} is especially useful from within TypeScript.
109
130
  */
110
- getFunction(...path: any[]): (Function);
131
+ getFunction(): (Function);
111
132
  /**
112
133
  * @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `array`.
113
134
  * Using this instead of just {@link Store.get} is especially useful from within TypeScript.
114
135
  */
115
- getArray(...path: any[]): any[];
136
+ getArray(depth?: number): any[];
116
137
  /**
117
138
  * @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `object`.
118
139
  * Using this instead of just {@link Store.get} is especially useful from within TypeScript.
119
140
  */
120
- getObject(...path: any[]): object;
141
+ getObject(depth?: number): object;
121
142
  /**
122
143
  * @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `map`.
123
144
  * Using this instead of just {@link Store.get} is especially useful from within TypeScript.
124
145
  */
125
- getMap(...path: any[]): Map<any, any>;
146
+ getMap(depth?: number): Map<any, any>;
126
147
  /**
127
- * Like {@link Store.get}, but the first parameter is the default value (returned when the Store
148
+ * Like {@link Store.get}, but with a default value (returned when the Store
128
149
  * contains `undefined`). This default value is also used to determine the expected type,
129
150
  * and to throw otherwise.
130
151
  *
131
152
  * @example
132
153
  * ```
133
- * let store = {x: 42}
134
- * assert(getOr(99, 'x') == 42)
135
- * assert(getOr(99, 'y') == 99)
136
- * getOr('hello', x') # throws TypeError (because 42 is not a string)
154
+ * let store = new Store({x: 42})
155
+ * store('x').getOr(99) // 42
156
+ * store('y').getOr(99) // 99
157
+ * store('x').getOr('hello') // throws TypeError (because 42 is not a string)
137
158
  * ```
138
159
  */
139
- getOr<T>(defaultValue: T, ...path: any[]): T;
140
- /** Retrieve a value, subscribing to all read `Store` values. This is a more flexible
141
- * form of the {@link Store.get} and {@link Store.peek} methods.
142
- *
143
- * @returns The resulting value, or `undefined` if the `path` does not exist.
144
- */
145
- query(opts: {
146
- /** The value for this path should be retrieved. Defaults to `[]`, meaning the entire `Store`. */
147
- path?: any[];
148
- /** A string specifying what type the query is expected to return. Options are:
149
- * "undefined", "null", "boolean", "number", "string", "function", "array", "map"
150
- * and "object". If the store holds a different type of value, a `TypeError`
151
- * exception is thrown. By default (when `type` is `undefined`) no type checking
152
- * is done.
153
- */
154
- type?: string;
155
- /** Limit the depth of the retrieved data structure to this positive integer.
156
- * When `depth` is `1`, only a single level of the value at `path` is unpacked. This
157
- * makes no difference for primitive values (like strings), but for objects, maps and
158
- * arrays, it means that each *value* in the resulting data structure will be a
159
- * reference to the `Store` for that value.
160
- */
161
- depth?: number;
162
- /** Return this value when the `path` does not exist. Defaults to `undefined`. */
163
- defaultValue?: any;
164
- /** When peek is `undefined` or `false`, the current scope will automatically be
165
- * subscribed to changes of any parts of the store being read. When `true`, no
166
- * subscribers will be performed.
167
- */
168
- peek?: boolean;
169
- }): any;
160
+ getOr<T>(defaultValue: T): T;
170
161
  /**
171
- * Checks if the specified collection is empty, and subscribes the current scope to changes of the emptiness of this collection.
162
+ * Checks if the collection held in `Store` is empty, and subscribes the current scope to changes of the emptiness of this collection.
172
163
  *
173
- * @param path Any path terms to resolve before retrieving the value.
174
- * @returns When the specified collection is not empty `true` is returned. If it is empty or if the value is undefined, `false` is returned.
164
+ * @returns When the collection is not empty `true` is returned. If it is empty or if the value is undefined, `false` is returned.
175
165
  * @throws When the value is not a collection and not undefined, an Error will be thrown.
176
166
  */
177
- isEmpty(...path: any[]): boolean;
167
+ isEmpty(): boolean;
178
168
  /**
179
- * Returns the number of items in the specified collection, and subscribes the current scope to changes in this count.
169
+ * Returns the number of items in the collection held in Store, and subscribes the current scope to changes in this count.
180
170
  *
181
- * @param path Any path terms to resolve before retrieving the value.
182
171
  * @returns The number of items contained in the collection, or 0 if the value is undefined.
183
172
  * @throws When the value is not a collection and not undefined, an Error will be thrown.
184
173
  */
185
- count(...path: any[]): number;
174
+ count(): number;
186
175
  /**
187
- * Returns a strings describing the type of the store value, subscribing to changes of this type.
176
+ * Returns a strings describing the type of the `Store` value, subscribing to changes of this type.
188
177
  * Note: this currently also subscribes to changes of primitive values, so changing a value from 3 to 4
189
178
  * would cause the scope to be rerun. This is not great, and may change in the future. This caveat does
190
179
  * not apply to changes made *inside* an object, `Array` or `Map`.
191
180
  *
192
- * @param path Any path terms to resolve before retrieving the value.
193
181
  * @returns Possible options: "undefined", "null", "boolean", "number", "string", "function", "array", "map" or "object".
194
182
  */
195
- getType(...path: any[]): string;
183
+ getType(): string;
196
184
  /**
197
- * Sets the value to the last given argument. Any earlier argument are a Store-path that is first
198
- * resolved/created using {@link Store.makeRef}.
185
+ * Returns a new `Store` that will always hold either the value of `whenTrue` or the value
186
+ * of `whenFalse` depending on whether the original `Store` is truthy or not.
187
+ *
188
+ * @param whenTrue The value set to the return-`Store` while `this` is truthy. This can be
189
+ * any type of value. If it's a `Store`, the return-`Store` will reference the same
190
+ * data (so *no* deep copy will be made).
191
+ * @param whenFalse Like `whenTrue`, but for falsy values (false, undefined, null, 0, "").
192
+ * @returns A store holding the result value. The value will keep getting updated while
193
+ * the observe context from which `if()` was called remains active.
194
+ */
195
+ if(whenTrue: any[], whenFalse?: any[]): Store;
196
+ /**
197
+ * Sets the `Store` value to the given argument.
199
198
  *
200
199
  * When a `Store` is passed in as the value, its value will be copied (subscribing to changes). In
201
200
  * case the value is an object, an `Array` or a `Map`, a *reference* to that data structure will
@@ -205,118 +204,119 @@ export declare class Store {
205
204
  *
206
205
  * If you intent to make a copy instead of a reference, call {@link Store.get} on the origin `Store`.
207
206
  *
207
+ * @returns The `Store` itself, for chaining other methods.
208
208
  *
209
209
  * @example
210
210
  * ```
211
211
  * let store = new Store() // Value is `undefined`
212
212
  *
213
- * store.set('x', 6) // Causes the store to become an object
214
- * assert(store.get() == {x: 6})
213
+ * store.set(6)
214
+ * store.get() // 6
215
215
  *
216
- * store.set('a', 'b', 'c', 'd') // Create parent path as objects
217
- * assert(store.get() == {x: 6, a: {b: {c: 'd'}}})
216
+ * store.set({}) // Change value to an empty object
217
+ * store('a', 'b', 'c').set('d') // Create parent path as objects
218
+ * store.get() // {x: 6, a: {b: {c: 'd'}}}
218
219
  *
219
220
  * store.set(42) // Overwrites all of the above
220
- * assert(store.get() == 42)
221
+ * store.get() // 42
221
222
  *
222
- * store.set('x', 6) // Throw Error (42 is not a collection)
223
+ * store('x').set(6) // Throw Error (42 is not a collection)
223
224
  * ```
224
225
  */
225
- set(...pathAndValue: any[]): void;
226
+ set(newValue: any): Store;
226
227
  /**
227
228
  * Sets the `Store` to the given `mergeValue`, but without deleting any pre-existing
228
229
  * items when a collection overwrites a similarly typed collection. This results in
229
230
  * a deep merge.
230
231
  *
232
+ * @returns The `Store` itself, for chaining other methods.
233
+ *
231
234
  * @example
232
235
  * ```
233
236
  * let store = new Store({a: {x: 1}})
234
237
  * store.merge({a: {y: 2}, b: 3})
235
- * assert(store.get() == {a: {x: 1, y: 2}, b: 3})
238
+ * store.get() // {a: {x: 1, y: 2}, b: 3}
236
239
  * ```
237
240
  */
238
- merge(...pathAndValue: any): void;
241
+ merge(mergeValue: any): Store;
239
242
  /**
240
243
  * Sets the value for the store to `undefined`, which causes it to be omitted from the map (or array, if it's at the end)
241
244
  *
245
+ * @returns The `Store` itself, for chaining other methods.
246
+ *
242
247
  * @example
243
248
  * ```
244
249
  * let store = new Store({a: 1, b: 2})
245
- * store.delete('a')
246
- * assert(store.get() == {b: 2})
250
+ * store('a').delete()
251
+ * store.get() // {b: 2}
247
252
  *
248
253
  * store = new Store(['a','b','c'])
249
- * store.delete(1)
250
- * assert(store.get() == ['a', undefined, 'c'])
251
- * store.delete(2)
252
- * assert(store.get() == ['a'])
254
+ * store(1).delete()
255
+ * store.get() // ['a', undefined, 'c']
256
+ * store(2).delete()
257
+ * store.get() // ['a']
258
+ * store.delete()
259
+ * store.get() // undefined
253
260
  * ```
254
261
  */
255
- delete(...path: any): void;
262
+ delete(): Store;
256
263
  /**
257
264
  * Pushes a value to the end of the Array that is at the specified path in the store.
258
265
  * If that store path is `undefined`, an Array is created first.
259
266
  * The last argument is the value to be added, any earlier arguments indicate the path.
260
267
  *
268
+ * @returns The index at which the item was appended.
269
+ * @throws TypeError when the store contains a primitive data type.
270
+ *
261
271
  * @example
262
272
  * ```
263
273
  * let store = new Store()
264
274
  * store.push(3) // Creates the array
265
275
  * store.push(6)
266
- * assert(store.get() == [3,6])
276
+ * store.get() // [3,6]
267
277
  *
268
278
  * store = new Store({myArray: [1,2]})
269
- * store.push('myArray', 3)
270
- * assert(store.get() == {myArray: [1,2,3]})
279
+ * store('myArray').push(3)
280
+ * store.get() // {myArray: [1,2,3]}
271
281
  * ```
272
282
  */
273
- push(...pathAndValue: any[]): number;
283
+ push(newValue: any): number;
274
284
  /**
275
285
  * {@link Store.peek} the current value, pass it through `func`, and {@link Store.set} the resulting
276
286
  * value.
277
287
  * @param func The function transforming the value.
288
+ * @returns The `Store` itself, for chaining other methods.
278
289
  */
279
- modify(func: (value: any) => any): void;
280
- /**
281
- * Return a `Store` deeper within the tree by resolving the given `path`,
282
- * subscribing to every level.
283
- * In case `undefined` is encountered while resolving the path, a newly
284
- * created `Store` containing `undefined` is returned. In that case, the
285
- * `Store`'s {@link Store.isDetached} method will return `true`.
286
- * In case something other than a collection is encountered, an error is thrown.
287
- */
288
- ref(...path: any[]): Store;
289
- /**
290
- * Similar to `ref()`, but instead of returning `undefined`, new objects are created when
291
- * a path does not exist yet. An error is still thrown when the path tries to index an invalid
292
- * type.
293
- * Unlike `ref`, `makeRef` does *not* subscribe to the path levels, as it is intended to be
294
- * a write-only operation.
295
- *
296
- * @example
297
- * ```
298
- * let store = new Store() // Value is `undefined`
299
- *
300
- * let ref = store.makeRef('a', 'b', 'c')
301
- * assert(store.get() == {a: {b: {}}}
302
- *
303
- * ref.set(42)
304
- * assert(store.get() == {a: {b: {c: 42}}}
305
- *
306
- * ref.makeRef('d') // Throw Error (42 is not a collection)
307
- * ```
308
- */
309
- makeRef(...path: any[]): Store;
290
+ modify(func: (value: any) => any): Store;
310
291
  /**
311
292
  * Iterate the specified collection (Array, Map or object), running the given code block for each item.
312
293
  * When items are added to the collection at some later point, the code block will be ran for them as well.
313
294
  * When an item is removed, the {@link Store.clean} handlers left by its code block are executed.
314
295
  *
315
- *
316
- *
317
- * @param pathAndFuncs
296
+ * @param renderer The function to be called for each item. It receives the item's `Store` object as its only argument.
297
+ * @param makeSortKey An optional function that, given an items `Store` object, returns a value to be sorted on.
298
+ * This value can be a number, a string, or an array containing a combination of both. When undefined is returned,
299
+ * the item is *not* rendered. If `makeSortKey` is not specified, the output will be sorted by `index()`.
300
+ */
301
+ onEach(renderer: (store: Store) => void, makeSortKey?: (store: Store) => any): void;
302
+ /**
303
+ * Derive a new `Store` from this `Store`, by reactively passing its value
304
+ * through the specified function.
305
+ * @param func Your function. It should accept a the input store's value, and return
306
+ * a result to be reactively set to the output store.
307
+ * @returns The output `Store`.
308
+ * @example
309
+ * ```javascript
310
+ * const store = new Store(21)
311
+ * const double = store.derive(v => v*2)
312
+ * double.get() // 42
313
+ *
314
+ * store.set(100)
315
+ * runQueue() // Or after a setTimeout 0, due to batching
316
+ * double.get() // 200
317
+ * ```
318
318
  */
319
- onEach(...pathAndFuncs: any): void;
319
+ derive(func: (value: any) => any): Store;
320
320
  /**
321
321
  * Applies a filter/map function on each item within the `Store`'s collection,
322
322
  * and reactively manages the returned `Map` `Store` to hold any results.
@@ -324,8 +324,8 @@ export declare class Store {
324
324
  * @param func - Function that transform the given store into an output value or
325
325
  * `undefined` in case this value should be skipped:
326
326
  *
327
- * @returns - A map `Store` with the values returned by `func` and the corresponding
328
- * keys from the original map, array or object `Store`.
327
+ * @returns - A array/map/object `Store` with the values returned by `func` and the
328
+ * corresponding keys from the original map, array or object `Store`.
329
329
  *
330
330
  * When items disappear from the `Store` or are changed in a way that `func` depends
331
331
  * upon, the resulting items are removed from the output `Store` as well. When multiple
@@ -349,108 +349,136 @@ export declare class Store {
349
349
  * input items produce the same output keys, this may lead to unexpected results.
350
350
  */
351
351
  multiMap(func: (store: Store) => any): Store;
352
- /**
353
- * @returns Returns `true` when the `Store` was created by {@link Store.ref}ing a path that
354
- * does not exist.
355
- */
356
- isDetached(): boolean;
357
352
  /**
358
353
  * Dump a live view of the `Store` tree as HTML text, `ul` and `li` nodes at
359
354
  * the current mount position. Meant for debugging purposes.
355
+ * @returns The `Store` itself, for chaining other methods.
360
356
  */
361
- dump(): void;
357
+ dump(): Store;
362
358
  }
363
359
  /**
364
- * Create a new DOM element, and insert it into the DOM at the position held by the current scope.
365
- * @param tag - The tag of the element to be created and optionally dot-separated class names. For example: `h1` or `p.intro.has_avatar`.
366
- * @param rest - The other arguments are flexible and interpreted based on their types:
367
- * - `string`: Used as textContent for the element.
368
- * - `object`: Used as attributes, properties or event listeners for the element. See {@link Store.prop} on how the distinction is made and to read about a couple of special keys.
369
- * - `function`: The render function used to draw the scope of the element. This function gets its own `Scope`, so that if any `Store` it reads changes, it will redraw by itself.
370
- * - `Store`: Presuming `tag` is `"input"`, `"textarea"` or `"select"`, create a two-way binding between this `Store` value and the input element. The initial value of the input will be set to the initial value of the `Store`, or the other way around if the `Store` holds `undefined`. After that, the `Store` will be updated when the input changes and vice versa.
371
- * @example
372
- * node('aside.editorial', 'Yada yada yada....', () => {
373
- * node('a', {href: '/bio'}, () => {
374
- * node('img.author', {src: '/me.jpg', alt: 'The author'})
375
- * })
360
+ * Modifies the *parent* DOM element in the current reactive scope, or adds
361
+ * new DOM elements to it.
362
+ *
363
+ * @param args - Arguments that define how to modify/create elements.
364
+ *
365
+ * ### String arguments
366
+ * Create new elements with optional classes and text content:
367
+ * ```js
368
+ * $('div.myClass') // <div class="myClass"></div>
369
+ * $('span.c1.c2:Hello') // <span class="c1 c2">Hello</span>
370
+ * $('p:Some text') // <p>Some text</p>
371
+ * $('.my-thing') // <div class="my-thing"></div>
372
+ * $('div', 'span', 'p.cls') // <div><span<p class="cls"></p></span></div>
373
+ * $(':Just some text!') // Just some text! (No new element, just a text node)
374
+ * ```
375
+ *
376
+ * ### Object arguments
377
+ * Set properties, attributes, events and special features:
378
+ * ```js
379
+ * // Classes (dot prefix)
380
+ * $('div', {'.active': true}) // Add class
381
+ * $('div', {'.hidden': false}) // Remove (or don't add) class
382
+ * $('div', {'.selected': myStore}) // Reactively add/remove class
383
+ *
384
+ * // Styles (dollar prefixed and camel-cased CSS properties)
385
+ * $('div', {$color: 'red'}) // style.color = 'red'
386
+ * $('div', {$marginTop: '10px'}) // style.marginTop = '10px'
387
+ * $('div', {$color: myColorStore}) // Reactively change color
388
+ *
389
+ * // Events (function values)
390
+ * $('button', {click: () => alert()}) // Add click handler
391
+ *
392
+ * // Properties (boolean values, `selectedIndex`, `value`)
393
+ * $('input', {disabled: true}) // el.disabled = true
394
+ * $('input', {value: 'test'}) // el.value = 'test'
395
+ * $('select', {selectedIndex: 2}) // el.selectedIndex = 2
396
+ *
397
+ * // Transitions
398
+ * $('div', {create: 'fade-in'}) // Add class on create
399
+ * $('div', {create: el => {...}}) // Run function on create
400
+ * $('div', {destroy: 'fade-out'}) // Add class before remove
401
+ * $('div', {destroy: el => {...}}) // Run function before remove
402
+ *
403
+ * // Content
404
+ * $('div', {html: '<b>Bold</b>'}) // Set innerHTML
405
+ * $('div', {text: 'Plain text'}) // Add text node
406
+ * const myElement = document.createElement('video')
407
+ * $('div', {element: myElement}) // Add existing DOM element
408
+ *
409
+ * // Regular attributes (everything else)
410
+ * $('div', {title: 'Info'}) // el.setAttribute('title', 'info')
411
+ * ```
412
+ *
413
+ * When a `Store` is passed as a value, a seperate observe-scope will
414
+ * be created for it, such that when the `Store` changes, only *that*
415
+ * UI property will need to be updated.
416
+ * So in the following example, when `colorStore` changes, only the
417
+ * `color` CSS property will be updated.
418
+ * ```js
419
+ * $('div', {
420
+ * '.active': activeStore, // Reactive class
421
+ * $color: colorStore, // Reactive style
422
+ * text: textStore // Reactive text
376
423
  * })
424
+ * ```
425
+ *
426
+ * ### Two-way input binding
427
+ * Set the initial value of an <input> <textarea> or <select> to that
428
+ * of a `Store`, and then start reflecting user changes to the former
429
+ * in the latter.
430
+ * ```js
431
+ * $('input', {bind: myStore}) // Binds input.value
432
+ * ```
433
+ * This is a special case, as changes to the `Store` will *not* be
434
+ * reflected in the UI.
435
+ *
436
+ * ### Function arguments
437
+ * Create child scopes that re-run on observed `Store` changes:
438
+ * ```js
439
+ * $('div', () => {
440
+ * $(myStore.get() ? 'span' : 'p') // Reactive element type
441
+ * })
442
+ * ```
443
+ * When *only* a function is given, `$` behaves exactly like {@link Store.observe},
444
+ * except that it will only work when we're inside a `mount`.
445
+ *
446
+ * @throws {ScopeError} If called outside an observable scope.
447
+ * @throws {Error} If invalid arguments are provided.
377
448
  */
378
- export declare function node(tag?: string | Element, ...rest: any[]): void;
379
- /**
380
- * Convert an HTML string to one or more DOM elements, and add them to the current DOM scope.
381
- * @param html - The HTML string. For example `"<section><h2>Test</h2><p>Info..</p></section>"`.
382
- */
383
- export declare function html(html: string): void;
384
- /**
385
- * Add a text node at the current Scope position.
386
- */
387
- export declare function text(text: string): void;
449
+ export declare function $(...args: (string | (() => void) | false | null | undefined | {
450
+ [key: string]: any;
451
+ })[]): void;
388
452
  /**
389
- * Set properties and attributes for the containing DOM element. Doing it this way
390
- * as opposed to setting them directly from node() allows changing them later on
391
- * without recreating the element itself. Also, code can be more readable this way.
392
- * Note that when a nested `observe()` is used, properties set this way do NOT
393
- * automatically revert to their previous values.
394
- *
395
- * Here's how properties are handled:
396
- * - If `name` is `"create"`, `value` should be either a function that gets
397
- * called with the element as its only argument immediately after creation,
398
- * or a string being the name of a CSS class that gets added immediately
399
- * after element creation, and removed shortly afterwards. This allows for
400
- * reveal animations. However, this is intentionally *not* done
401
- * for elements that are created as part of a larger (re)draw, to prevent
402
- * all elements from individually animating on page creation.
403
- * - If `name` is `"destroy"`, `value` should be a function that gets called
404
- * with the element as its only argument, *instead of* the element being
405
- * removed from the DOM (which the function will presumably need to do
406
- * eventually). This can be used for a conceal animation.
407
- * As a convenience, it's also possible to provide a string instead of
408
- * a function, which will be added to the element as a CSS class, allowing
409
- * for simple transitions. In this case, the DOM element in removed 2 seconds
410
- * later (currently not configurable).
411
- * Similar to `"create"` (and in this case doing anything else would make little
412
- * sense), this only happens when the element being is the top-level element
413
- * being removed from the DOM.
414
- * - If `value` is a function, it is registered as an event handler for the
415
- * `name` event.
416
- * - If `name` is `"class"` or `"className"` and the `value` is an
417
- * object, all keys of the object are either added or removed from `classList`,
418
- * depending on whether `value` is true-like or false-like.
419
- * - If `value` is a boolean *or* `name` is `"value"`, `"className"` or
420
- * `"selectedIndex"`, it is set as a DOM element *property*.
421
- * - If `name` is `"text"`, the `value` is set as the element's `textContent`.
422
- * - If `name` is `"style"` and `value` is an object, each of its
423
- * key/value pairs are assigned to the element's `.style`.
424
- * - In other cases, the `value` is set as the `name` HTML *attribute*.
453
+ * Set a custome error handling function, thast is called when an error occurs during rendering
454
+ * while in a reactive scope. The default implementation logs the error to the console, and then
455
+ * just returns `true`, which causes an 'Error' message to be displayed in the UI. When this function
456
+ * returns `false`, the error is suppressed. This mechanism exists because rendering errors can occur
457
+ * at any time, not just synchronous when making a call to Aberdeen, thus normal exception handling
458
+ * is not always possible.
459
+ *
460
+ * @param handler The handler function, getting an `Error` as its argument, and returning `false`
461
+ * if it does *not* want an error message to be added to the DOM.
462
+ * When `handler is `undefined`, the default error handling will be reinstated.
425
463
  *
426
464
  * @example
427
- * ```
428
- * node('input', () => {
429
- * prop('type', 'password')
430
- * prop('readOnly', true)
431
- * prop('class', 'my-class')
432
- * prop('class', {
433
- * 'my-disabled-class': false,
434
- * 'my-enabled-class': true,
435
- * })
436
- * prop({
437
- * class: 'my-class',
438
- * text: 'Here is something to read...',
439
- * style: {
440
- * backgroundColor: 'red',
441
- * fontWeight: 'bold',
442
- * },
443
- * create: aberdeen.fadeIn,
444
- * destroy: 'my-fade-out-class',
445
- * click: myClickHandler,
446
- * })
465
+ * ```javascript
466
+ * //
467
+ * setErrorHandler(error => {
468
+ * // Tell our developers about the problem.
469
+ * fancyErrorLogger(error)
470
+ * // Add custom error message to the DOM.
471
+ * try {
472
+ * $('.error:Sorry, something went wrong!')
473
+ * } catch() {} // In case there is no parent element.
474
+ * // Don't add default error message to the DOM.
475
+ * return false
447
476
  * })
448
477
  * ```
449
478
  */
450
- export declare function prop(name: string, value: any): void;
451
- export declare function prop(props: object): void;
479
+ export declare function setErrorHandler(handler?: (error: Error) => boolean | undefined): void;
452
480
  /**
453
- * Return the browser Element that `node()`s would be rendered to at this point.
481
+ * Return the browser Element that nodes would be rendered to at this point.
454
482
  * NOTE: Manually changing the DOM is not recommended in most cases. There is
455
483
  * usually a better, declarative way. Although there are no hard guarantees on
456
484
  * how your changes interact with Aberdeen, in most cases results will not be
@@ -497,10 +525,10 @@ export declare function observe(func: () => void): number | undefined;
497
525
  */
498
526
  export declare function immediateObserve(func: () => void): number | undefined;
499
527
  /**
500
- * Like {@link Store.observe}, but allow the function to create DOM elements using {@link Store.node}.
528
+ * Reactively run the function, adding any DOM-elements created using {@link $} to the given parent element.
501
529
 
502
530
  * @param func - The function to be (repeatedly) executed, possibly adding DOM elements to `parentElement`.
503
- * @param parentElement - A DOM element that will be used as the parent element for calls to `node`.
531
+ * @param parentElement - A DOM element that will be used as the parent element for calls to `$`.
504
532
  * @returns The mount id (usable for `unmount`) if this is a top-level mount.
505
533
  *
506
534
  * @example
@@ -509,7 +537,7 @@ export declare function immediateObserve(func: () => void): number | undefined;
509
537
  * setInterval(() => store.modify(v => v+1), 1000)
510
538
  *
511
539
  * mount(document.body, () => {
512
- * node('h2', `${store.get()} seconds have passed`)
540
+ * $(`h2:${store.get()} seconds have passed`)
513
541
  * })
514
542
  * ```
515
543
  *
@@ -519,21 +547,21 @@ export declare function immediateObserve(func: () => void): number | undefined;
519
547
  * let colors = new Store(new Map())
520
548
  *
521
549
  * mount(document.body, () => {
522
- * // This function will never rerun (as it does not read any `Store`s)
523
- * node('button', '<<', {click: () => selected.modify(n => n-1)})
524
- * node('button', '>>', {click: () => selected.modify(n => n+1)})
525
- *
526
- * observe(() => {
527
- * // This will rerun whenever `selected` changes, recreating the <h2> and <input>.
528
- * node('h2', '#'+selected.get())
529
- * node('input', {type: 'color', value: '#ffffff'}, colors.ref(selected.get()))
530
- * })
531
- *
532
- * observe(() => {
533
- * // This function will rerun when `selected` or the selected color changes.
534
- * // It will change the <body> background-color.
535
- * prop({style: {backgroundColor: colors.get(selected.get()) || 'white'}})
536
- * })
550
+ * // This function will never rerun (as it does not read any `Store`s)
551
+ * $('button:<<', {click: () => selected.modify(n => n-1)})
552
+ * $('button:>>', {click: () => selected.modify(n => n+1)})
553
+ *
554
+ * observe(() => {
555
+ * // This will rerun whenever `selected` changes, recreating the <h2> and <input>.
556
+ * $('h2', {text: '#' + selected.get()})
557
+ * $('input', {type: 'color', value: '#ffffff' bind: colors(selected.get())})
558
+ * })
559
+ *
560
+ * observe(() => {
561
+ * // This function will rerun when `selected` or the selected color changes.
562
+ * // It will change the <body> background-color.
563
+ * $({$backgroundColor: colors.get(selected.get()) || 'white'})
564
+ * })
537
565
  * })
538
566
  * ```
539
567
  */
@@ -566,8 +594,8 @@ export declare function unmount(id?: number): void;
566
594
  * for `count()` however.
567
595
  */
568
596
  export declare function peek<T>(func: () => T): T;
569
- /**
570
- * Run a function, while *not* causing reactive effects for any changes it makes to `Store`s.
571
- * @param func The function to be executed once immediately.
572
- */
573
- export declare function inhibitEffects(func: () => void): void;
597
+ declare global {
598
+ interface String {
599
+ replaceAll(from: string, to: string): string;
600
+ }
601
+ }