aberdeen 0.0.15 → 0.0.17
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 +109 -5
- package/dist/aberdeen.d.ts +121 -33
- package/dist/aberdeen.js +225 -77
- package/dist/aberdeen.min.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
Aberdeen
|
|
2
|
-
--------
|
|
3
|
-
|
|
4
1
|
A TypeScript/JavaScript library for quickly building performant declarative user interfaces *without* the use of a virtual DOM.
|
|
5
2
|
|
|
6
3
|
The key insight is the use of many small anonymous functions, that will automatically rerun when the underlying data changes. In order to trigger updates, that data should be encapsulated in any number of `Store` objects. They can hold anything, from simple values to deeply nested data structures, in which case user-interface functions can (automatically) subscribe to just the parts they depend upon.
|
|
@@ -10,5 +7,112 @@ The key insight is the use of many small anonymous functions, that will automati
|
|
|
10
7
|
|
|
11
8
|
- It provides a flexible and simple to understand model for reactive user-interface building.
|
|
12
9
|
- It allows you to express user-interfaces in plain JavaScript (or TypeScript) in an easy to read form, without (JSX-like) compilation steps.
|
|
13
|
-
- It's fast, as it doesn't use a *virtual DOM* and only reruns small pieces of code in response to updated data.
|
|
14
|
-
- It's lightweight, at
|
|
10
|
+
- It's fast, as it doesn't use a *virtual DOM* and only reruns small pieces of code in response to updated data. It also makes displaying and updating sorted lists very easy and very fast.
|
|
11
|
+
- It's lightweight, at about 15kb minimized.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Examples
|
|
15
|
+
|
|
16
|
+
- [Tic-tac-toe demo](https://vanviegen.github.io/aberdeen/examples/tic-tac-toe/) - [Source](https://github.com/vanviegen/aberdeen/tree/master/examples/tic-tac-toe)
|
|
17
|
+
- [Input example demo](https://vanviegen.github.io/aberdeen/examples/input/) - [Source](https://github.com/vanviegen/aberdeen/tree/master/examples/input)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
To get a quick impression of what Aberdeen code looks like, this is all of the JavaScript for the above Tic-tac-toe demo:
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
import {node, prop, mount, Store, text} from 'https://cdn.jsdelivr.net/npm/aberdeen/+esm';
|
|
24
|
+
|
|
25
|
+
const store = new Store({
|
|
26
|
+
squares: [],
|
|
27
|
+
turn: 'X',
|
|
28
|
+
history: [{}],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const drawSquare = (position) => {
|
|
32
|
+
node('button.square', () => {
|
|
33
|
+
let value = store.get('squares', position)
|
|
34
|
+
if (value) text(value)
|
|
35
|
+
else prop('click', () => fillSquare(position))
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const drawBoard = () => {
|
|
40
|
+
for(let y=0; y<3; y++) {
|
|
41
|
+
node('div.board-row', () => {
|
|
42
|
+
for(let x=0; x<3; x++) {
|
|
43
|
+
drawSquare(y*3 + x)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const drawInfo = () => {
|
|
50
|
+
node('div', () => {
|
|
51
|
+
let winner = calculateWinner(store.get('squares'))
|
|
52
|
+
if (winner) {
|
|
53
|
+
text(`Winner: ${winner}`)
|
|
54
|
+
} else {
|
|
55
|
+
text(`Next player: ${store.get('turn')}`)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
node('ol', () => {
|
|
59
|
+
store.onEach('history', item => {
|
|
60
|
+
node('li', () => {
|
|
61
|
+
node('button', () => {
|
|
62
|
+
text(item.index() ? `Go to move ${item.index()}` : `Go to game start`)
|
|
63
|
+
prop('click', () => {
|
|
64
|
+
store.set('historyPos', item.index())
|
|
65
|
+
store.set('squares', item.get())
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const fillSquare = (position) => {
|
|
74
|
+
// If there's already a winner, don't allow a new square to be filled
|
|
75
|
+
if (calculateWinner(store.get('squares'))) return
|
|
76
|
+
|
|
77
|
+
// Fill the square
|
|
78
|
+
store.set('squares', position, store.get('turn'))
|
|
79
|
+
|
|
80
|
+
// Next player's turn
|
|
81
|
+
store.set('turn', store.get('turn')==='X' ? 'O' : 'X')
|
|
82
|
+
|
|
83
|
+
if (store.get('historyPos') != null) {
|
|
84
|
+
// Truncate everything after history pos
|
|
85
|
+
store.set('history', store.get('history').slice(0,store.get('historyPos')+1))
|
|
86
|
+
// Stop 'time traveling'
|
|
87
|
+
store.delete('historyPos')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Append the current squares-state to the history array
|
|
91
|
+
store.push('history', store.get('squares'))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const calculateWinner = (squares) => {
|
|
95
|
+
const lines = [
|
|
96
|
+
[0, 1, 2], [3, 4, 5], [6, 7, 8], // horizontal
|
|
97
|
+
[0, 3, 6], [1, 4, 7], [2, 5, 8], // vertical
|
|
98
|
+
[0, 4, 8], [2, 4, 6] // diagonal
|
|
99
|
+
];
|
|
100
|
+
for (const [a, b, c] of lines) {
|
|
101
|
+
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
|
|
102
|
+
return squares[a];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
mount(document.body, () => {
|
|
108
|
+
node('div.game', () => {
|
|
109
|
+
node('div.game-board', drawBoard)
|
|
110
|
+
node('div.game-info', drawInfo)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## Reference documentation
|
|
117
|
+
|
|
118
|
+
https://vanviegen.github.io/aberdeen/modules.html
|
package/dist/aberdeen.d.ts
CHANGED
|
@@ -2,6 +2,21 @@ interface QueueRunner {
|
|
|
2
2
|
queueOrder: number;
|
|
3
3
|
queueRun(): void;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* Schedule a function to be executed by Aberdeen's internal task queue. This
|
|
7
|
+
* can be useful to batch together DOM layout read operations and DOM write
|
|
8
|
+
* operations, so that we're not forcing the browser to do more layout calculations
|
|
9
|
+
* than needed. Also, unlike setTimeout or requestAnimationFrame, this doesn't
|
|
10
|
+
* give the browser the chance to render partial DOM states to the screen (which
|
|
11
|
+
* would be seen as glitches/flashes).
|
|
12
|
+
* @param func The function to be called soon.
|
|
13
|
+
* @param order Higher mean later. Defaults to 0, which would still be *after* all
|
|
14
|
+
* node/observe redraws have been handled.
|
|
15
|
+
*
|
|
16
|
+
* **EXPERIMENTAL:** There's a good chance this function will be replaced by
|
|
17
|
+
* something that explicitly addresses DOM layout reads and DOM writes.
|
|
18
|
+
*/
|
|
19
|
+
export declare function scheduleTask(func: () => void, order?: number): void;
|
|
5
20
|
type SortKeyType = number | string | Array<number | string>;
|
|
6
21
|
interface Observer {
|
|
7
22
|
onChange(index: any, newData: DatumType, oldData: DatumType): void;
|
|
@@ -16,7 +31,7 @@ declare abstract class Scope implements QueueRunner, Observer {
|
|
|
16
31
|
}>;
|
|
17
32
|
isDead: boolean;
|
|
18
33
|
constructor(parentElement: Element | undefined, precedingSibling: Node | Scope | undefined, queueOrder: number);
|
|
19
|
-
findPrecedingNode(): Node | undefined;
|
|
34
|
+
findPrecedingNode(stopAt?: Scope | Node | undefined): Node | undefined;
|
|
20
35
|
findLastNode(): Node | undefined;
|
|
21
36
|
addNode(node: Node): void;
|
|
22
37
|
remove(): void;
|
|
@@ -102,7 +117,7 @@ declare class ObsMap extends ObsCollection {
|
|
|
102
117
|
*
|
|
103
118
|
* Supported data types are: `string`, `number`, `boolean`, `undefined`, `null`,
|
|
104
119
|
* `Array`, `object` and `Map`. The latter three will always have `Store` objects as
|
|
105
|
-
* values, creating a tree of `Store
|
|
120
|
+
* values, creating a tree of `Store`-objects.
|
|
106
121
|
*/
|
|
107
122
|
export declare class Store {
|
|
108
123
|
private collection;
|
|
@@ -110,7 +125,7 @@ export declare class Store {
|
|
|
110
125
|
/**
|
|
111
126
|
* Create a new store with the given `value` as its value. Defaults to `undefined` if no value is given.
|
|
112
127
|
* When the value is a plain JavaScript object, an `Array` or a `Map`, it will be stored as a tree of
|
|
113
|
-
* `Store`s. (Calling
|
|
128
|
+
* `Store`s. (Calling {@link Store.get} on the store will recreate the original data strucure, though.)
|
|
114
129
|
*
|
|
115
130
|
* @example
|
|
116
131
|
* ```
|
|
@@ -153,46 +168,46 @@ export declare class Store {
|
|
|
153
168
|
*/
|
|
154
169
|
get(...path: any[]): any;
|
|
155
170
|
/**
|
|
156
|
-
* Like
|
|
171
|
+
* Like {@link Store.get}, but doesn't subscribe to changes.
|
|
157
172
|
*/
|
|
158
173
|
peek(...path: any[]): any;
|
|
159
174
|
/**
|
|
160
|
-
* @returns Like
|
|
161
|
-
* Using this instead of just
|
|
175
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `number`.
|
|
176
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
162
177
|
*/
|
|
163
178
|
getNumber(...path: any[]): number;
|
|
164
179
|
/**
|
|
165
|
-
* @returns Like
|
|
166
|
-
* Using this instead of just
|
|
180
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `string`.
|
|
181
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
167
182
|
*/
|
|
168
183
|
getString(...path: any[]): string;
|
|
169
184
|
/**
|
|
170
|
-
* @returns Like
|
|
171
|
-
* Using this instead of just
|
|
185
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `boolean`.
|
|
186
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
172
187
|
*/
|
|
173
188
|
getBoolean(...path: any[]): boolean;
|
|
174
189
|
/**
|
|
175
|
-
* @returns Like
|
|
176
|
-
* Using this instead of just
|
|
190
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `function`.
|
|
191
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
177
192
|
*/
|
|
178
193
|
getFunction(...path: any[]): (Function);
|
|
179
194
|
/**
|
|
180
|
-
* @returns Like
|
|
181
|
-
* Using this instead of just
|
|
195
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `array`.
|
|
196
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
182
197
|
*/
|
|
183
198
|
getArray(...path: any[]): any[];
|
|
184
199
|
/**
|
|
185
|
-
* @returns Like
|
|
186
|
-
* Using this instead of just
|
|
200
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `object`.
|
|
201
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
187
202
|
*/
|
|
188
203
|
getObject(...path: any[]): object;
|
|
189
204
|
/**
|
|
190
|
-
* @returns Like
|
|
191
|
-
* Using this instead of just
|
|
205
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `map`.
|
|
206
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
192
207
|
*/
|
|
193
208
|
getMap(...path: any[]): Map<any, any>;
|
|
194
209
|
/**
|
|
195
|
-
* Like
|
|
210
|
+
* Like {@link Store.get}, but the first parameter is the default value (returned when the Store
|
|
196
211
|
* contains `undefined`). This default value is also used to determine the expected type,
|
|
197
212
|
* and to throw otherwise.
|
|
198
213
|
*
|
|
@@ -206,7 +221,7 @@ export declare class Store {
|
|
|
206
221
|
*/
|
|
207
222
|
getOr<T>(defaultValue: T, ...path: any[]): T;
|
|
208
223
|
/** Retrieve a value, subscribing to all read `Store` values. This is a more flexible
|
|
209
|
-
* form of the
|
|
224
|
+
* form of the {@link Store.get} and {@link Store.peek} methods.
|
|
210
225
|
*
|
|
211
226
|
* @returns The resulting value, or `undefined` if the `path` does not exist.
|
|
212
227
|
*/
|
|
@@ -263,15 +278,15 @@ export declare class Store {
|
|
|
263
278
|
getType(...path: any[]): string;
|
|
264
279
|
/**
|
|
265
280
|
* Sets the value to the last given argument. Any earlier argument are a Store-path that is first
|
|
266
|
-
* resolved/created using
|
|
281
|
+
* resolved/created using {@link Store.makeRef}.
|
|
267
282
|
*
|
|
268
283
|
* When a `Store` is passed in as the value, its value will be copied (subscribing to changes). In
|
|
269
284
|
* case the value is an object, an `Array` or a `Map`, a *reference* to that data structure will
|
|
270
285
|
* be created, so that changes made through one `Store` will be reflected through the other. Be
|
|
271
286
|
* carefull not to create loops in your `Store` tree that way, as that would cause any future
|
|
272
|
-
* call to
|
|
287
|
+
* call to {@link Store.get} to throw a `RangeError` (Maximum call stack size exceeded.)
|
|
273
288
|
*
|
|
274
|
-
* If you intent to make a copy instead of a reference, call
|
|
289
|
+
* If you intent to make a copy instead of a reference, call {@link Store.get} on the origin `Store`.
|
|
275
290
|
*
|
|
276
291
|
*
|
|
277
292
|
* @example
|
|
@@ -340,7 +355,7 @@ export declare class Store {
|
|
|
340
355
|
*/
|
|
341
356
|
push(...pathAndValue: any[]): number;
|
|
342
357
|
/**
|
|
343
|
-
*
|
|
358
|
+
* {@link Store.peek} the current value, pass it through `func`, and {@link Store.set} the resulting
|
|
344
359
|
* value.
|
|
345
360
|
* @param func The function transforming the value.
|
|
346
361
|
*/
|
|
@@ -350,7 +365,7 @@ export declare class Store {
|
|
|
350
365
|
* subscribing to every level.
|
|
351
366
|
* In case `undefined` is encountered while resolving the path, a newly
|
|
352
367
|
* created `Store` containing `undefined` is returned. In that case, the
|
|
353
|
-
* `Store`'s
|
|
368
|
+
* `Store`'s {@link Store.isDetached} method will return `true`.
|
|
354
369
|
* In case something other than a collection is encountered, an error is thrown.
|
|
355
370
|
*/
|
|
356
371
|
ref(...path: any[]): Store;
|
|
@@ -375,12 +390,12 @@ export declare class Store {
|
|
|
375
390
|
* ```
|
|
376
391
|
*/
|
|
377
392
|
makeRef(...path: any[]): Store;
|
|
378
|
-
/** @
|
|
393
|
+
/** @internal */
|
|
379
394
|
_observe(): DatumType;
|
|
380
395
|
/**
|
|
381
396
|
* Iterate the specified collection (Array, Map or object), running the given code block for each item.
|
|
382
397
|
* When items are added to the collection at some later point, the code block will be ran for them as well.
|
|
383
|
-
* When an item is removed, the
|
|
398
|
+
* When an item is removed, the {@link Store.clean} handlers left by its code block are executed.
|
|
384
399
|
*
|
|
385
400
|
*
|
|
386
401
|
*
|
|
@@ -420,7 +435,7 @@ export declare class Store {
|
|
|
420
435
|
*/
|
|
421
436
|
multiMap(func: (store: Store) => any): Store;
|
|
422
437
|
/**
|
|
423
|
-
* @returns Returns `true` when the `Store` was created by
|
|
438
|
+
* @returns Returns `true` when the `Store` was created by {@link Store.ref}ing a path that
|
|
424
439
|
* does not exist.
|
|
425
440
|
*/
|
|
426
441
|
isDetached(): boolean;
|
|
@@ -431,7 +446,7 @@ export declare class Store {
|
|
|
431
446
|
* @param tag - The tag of the element to be created and optionally dot-separated class names. For example: `h1` or `p.intro.has_avatar`.
|
|
432
447
|
* @param rest - The other arguments are flexible and interpreted based on their types:
|
|
433
448
|
* - `string`: Used as textContent for the element.
|
|
434
|
-
* - `object`: Used as attributes, properties or event listeners for the element. See
|
|
449
|
+
* - `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.
|
|
435
450
|
* - `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.
|
|
436
451
|
* - `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.
|
|
437
452
|
* @example
|
|
@@ -457,8 +472,63 @@ export declare function text(text: string): void;
|
|
|
457
472
|
* without recreating the element itself. Also, code can be more readable this way.
|
|
458
473
|
* Note that when a nested `observe()` is used, properties set this way do NOT
|
|
459
474
|
* automatically revert to their previous values.
|
|
475
|
+
*
|
|
476
|
+
* Here's how properties are handled:
|
|
477
|
+
* - If `name` is `"create"`, `value` should be either a function that gets
|
|
478
|
+
* called with the element as its only argument immediately after creation,
|
|
479
|
+
* or a string being the name of a CSS class that gets added immediately
|
|
480
|
+
* after element creation, and removed shortly afterwards. This allows for
|
|
481
|
+
* reveal animations. However, this is intentionally *not* done
|
|
482
|
+
* for elements that are created as part of a larger (re)draw, to prevent
|
|
483
|
+
* all elements from individually animating on page creation.
|
|
484
|
+
* - If `name` is `"destroy"`, `value` should be a function that gets called
|
|
485
|
+
* with the element as its only argument, *instead of* the element being
|
|
486
|
+
* removed from the DOM (which the function will presumably need to do
|
|
487
|
+
* eventually). This can be used for a conceal animation.
|
|
488
|
+
* As a convenience, it's also possible to provide a string instead of
|
|
489
|
+
* a function, which will be added to the element as a CSS class, allowing
|
|
490
|
+
* for simple transitions. In this case, the DOM element in removed 2 seconds
|
|
491
|
+
* later (currently not configurable).
|
|
492
|
+
* Similar to `"create"` (and in this case doing anything else would make little
|
|
493
|
+
* sense), this only happens when the element being is the top-level element
|
|
494
|
+
* being removed from the DOM.
|
|
495
|
+
* - If `value` is a function, it is registered as an event handler for the
|
|
496
|
+
* `name` event.
|
|
497
|
+
* - If `name` is `"class"` or `"className"` and the `value` is an
|
|
498
|
+
* object, all keys of the object are either added or removed from `classList`,
|
|
499
|
+
* depending on whether `value` is true-like or false-like.
|
|
500
|
+
* - If `value` is a boolean *or* `name` is `"value"`, `"className"` or
|
|
501
|
+
* `"selectedIndex"`, it is set as a DOM element *property*.
|
|
502
|
+
* - If `name` is `"text"`, the `value` is set as the element's `textContent`.
|
|
503
|
+
* - If `name` is `"style"` and `value` is an object, each of its
|
|
504
|
+
* key/value pairs are assigned to the element's `.style`.
|
|
505
|
+
* - In other cases, the `value` is set as the `name` HTML *attribute*.
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* ```
|
|
509
|
+
* node('input', () => {
|
|
510
|
+
* prop('type', 'password')
|
|
511
|
+
* prop('readOnly', true)
|
|
512
|
+
* prop('class', 'my-class')
|
|
513
|
+
* prop('class', {
|
|
514
|
+
* 'my-disabled-class': false,
|
|
515
|
+
* 'my-enabled-class': true,
|
|
516
|
+
* })
|
|
517
|
+
* prop({
|
|
518
|
+
* class: 'my-class',
|
|
519
|
+
* text: 'Here is something to read...',
|
|
520
|
+
* style: {
|
|
521
|
+
* backgroundColor: 'red',
|
|
522
|
+
* fontWeight: 'bold',
|
|
523
|
+
* },
|
|
524
|
+
* create: aberdeen.fadeIn,
|
|
525
|
+
* destroy: 'my-fade-out-class',
|
|
526
|
+
* click: myClickHandler,
|
|
527
|
+
* })
|
|
528
|
+
* })
|
|
529
|
+
* ```
|
|
460
530
|
*/
|
|
461
|
-
export declare function prop(
|
|
531
|
+
export declare function prop(name: string, value: any): void;
|
|
462
532
|
export declare function prop(props: object): void;
|
|
463
533
|
/**
|
|
464
534
|
* Return the browser Element that `node()`s would be rendered to at this point.
|
|
@@ -497,7 +567,7 @@ export declare function clean(clean: (scope: Scope) => void): void;
|
|
|
497
567
|
*/
|
|
498
568
|
export declare function observe(func: () => void): void;
|
|
499
569
|
/**
|
|
500
|
-
* Like
|
|
570
|
+
* Like {@link Store.observe}, but allow the function to create DOM elements using {@link Store.node}.
|
|
501
571
|
|
|
502
572
|
* @param func - The function to be (repeatedly) executed, possibly adding DOM elements to `parentElement`.
|
|
503
573
|
* @param parentElement - A DOM element that will be used as the parent element for calls to `node`.
|
|
@@ -512,7 +582,7 @@ export declare function observe(func: () => void): void;
|
|
|
512
582
|
* })
|
|
513
583
|
* ```
|
|
514
584
|
*
|
|
515
|
-
* An example nesting
|
|
585
|
+
* An example nesting {@link Store.observe} within `mount`:
|
|
516
586
|
* ```
|
|
517
587
|
* let selected = new Store(0)
|
|
518
588
|
* let colors = new Store(new Map())
|
|
@@ -537,7 +607,7 @@ export declare function observe(func: () => void): void;
|
|
|
537
607
|
* ```
|
|
538
608
|
*/
|
|
539
609
|
export declare function mount(parentElement: Element | undefined, func: () => void): void;
|
|
540
|
-
/** Runs the given function, while not subscribing the current scope when reading
|
|
610
|
+
/** Runs the given function, while not subscribing the current scope when reading {@link Store.Store} values.
|
|
541
611
|
*
|
|
542
612
|
* @param func Function to be executed immediately.
|
|
543
613
|
* @returns Whatever `func()` returns.
|
|
@@ -559,4 +629,22 @@ export declare function mount(parentElement: Element | undefined, func: () => vo
|
|
|
559
629
|
* for `count()` however.
|
|
560
630
|
*/
|
|
561
631
|
export declare function peek<T>(func: () => T): T;
|
|
632
|
+
/** Do a grow transition for the given element. This is meant to be used as a
|
|
633
|
+
* handler for the `create` property.
|
|
634
|
+
*
|
|
635
|
+
* @param el The element to transition.
|
|
636
|
+
*
|
|
637
|
+
* The transition doesn't look great for table elements, and may have problems
|
|
638
|
+
* for other specific cases as well.
|
|
639
|
+
*/
|
|
640
|
+
export declare function grow(el: HTMLElement): void;
|
|
641
|
+
/** Do a shrink transition for the given element, and remove it from the DOM
|
|
642
|
+
* afterwards. This is meant to be used as a handler for the `destroy` property.
|
|
643
|
+
*
|
|
644
|
+
* @param el The element to transition and remove.
|
|
645
|
+
*
|
|
646
|
+
* The transition doesn't look great for table elements, and may have problems
|
|
647
|
+
* for other specific cases as well.
|
|
648
|
+
*/
|
|
649
|
+
export declare function shrink(el: HTMLElement): void;
|
|
562
650
|
export {};
|
package/dist/aberdeen.js
CHANGED
|
@@ -18,6 +18,7 @@ function queue(runner) {
|
|
|
18
18
|
queueSet.add(runner);
|
|
19
19
|
}
|
|
20
20
|
function runQueue() {
|
|
21
|
+
onCreateEnabled = true;
|
|
21
22
|
for (let index = 0; index < queueArray.length;) {
|
|
22
23
|
if (!queueOrdered) {
|
|
23
24
|
queueArray.splice(0, index);
|
|
@@ -36,6 +37,24 @@ function runQueue() {
|
|
|
36
37
|
}
|
|
37
38
|
queueArray.length = 0;
|
|
38
39
|
runQueueDepth = 0;
|
|
40
|
+
onCreateEnabled = false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Schedule a function to be executed by Aberdeen's internal task queue. This
|
|
44
|
+
* can be useful to batch together DOM layout read operations and DOM write
|
|
45
|
+
* operations, so that we're not forcing the browser to do more layout calculations
|
|
46
|
+
* than needed. Also, unlike setTimeout or requestAnimationFrame, this doesn't
|
|
47
|
+
* give the browser the chance to render partial DOM states to the screen (which
|
|
48
|
+
* would be seen as glitches/flashes).
|
|
49
|
+
* @param func The function to be called soon.
|
|
50
|
+
* @param order Higher mean later. Defaults to 0, which would still be *after* all
|
|
51
|
+
* node/observe redraws have been handled.
|
|
52
|
+
*
|
|
53
|
+
* **EXPERIMENTAL:** There's a good chance this function will be replaced by
|
|
54
|
+
* something that explicitly addresses DOM layout reads and DOM writes.
|
|
55
|
+
*/
|
|
56
|
+
export function scheduleTask(func, order = 0) {
|
|
57
|
+
queue({ queueOrder: 1000 + order, queueRun: func });
|
|
39
58
|
}
|
|
40
59
|
/**
|
|
41
60
|
* Given an integer number, a string or an array of these, this function returns a string that can be used
|
|
@@ -96,24 +115,27 @@ class Scope {
|
|
|
96
115
|
this.precedingSibling = precedingSibling;
|
|
97
116
|
this.queueOrder = queueOrder;
|
|
98
117
|
}
|
|
99
|
-
// Get a reference to the last Node preceding
|
|
100
|
-
findPrecedingNode() {
|
|
101
|
-
let
|
|
102
|
-
|
|
118
|
+
// Get a reference to the last Node preceding this Scope, or undefined if there is none
|
|
119
|
+
findPrecedingNode(stopAt = undefined) {
|
|
120
|
+
let cur = this;
|
|
121
|
+
let pre;
|
|
122
|
+
while ((pre = cur.precedingSibling) && pre !== stopAt) {
|
|
103
123
|
if (pre instanceof Node)
|
|
104
124
|
return pre;
|
|
105
125
|
let node = pre.findLastNode();
|
|
106
126
|
if (node)
|
|
107
127
|
return node;
|
|
108
|
-
|
|
128
|
+
cur = pre;
|
|
109
129
|
}
|
|
110
130
|
}
|
|
111
131
|
// Get a reference to the last Node within this scope and parentElement
|
|
112
132
|
findLastNode() {
|
|
113
|
-
if (this.lastChild
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
if (this.lastChild) {
|
|
134
|
+
if (this.lastChild instanceof Node)
|
|
135
|
+
return this.lastChild;
|
|
136
|
+
else
|
|
137
|
+
return this.lastChild.findLastNode() || this.lastChild.findPrecedingNode(this.precedingSibling);
|
|
138
|
+
}
|
|
117
139
|
}
|
|
118
140
|
addNode(node) {
|
|
119
141
|
if (!this.parentElement)
|
|
@@ -127,20 +149,37 @@ class Scope {
|
|
|
127
149
|
let lastNode = this.findLastNode();
|
|
128
150
|
if (lastNode) {
|
|
129
151
|
// at least one DOM node to be removed
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
152
|
+
let nextNode = this.findPrecedingNode();
|
|
153
|
+
nextNode = (nextNode ? nextNode.nextSibling : this.parentElement.firstChild);
|
|
154
|
+
this.lastChild = undefined;
|
|
155
|
+
// Keep removing DOM nodes starting at our first node, until we encounter the last node
|
|
156
|
+
while (true) {
|
|
134
157
|
/* istanbul ignore next */
|
|
135
|
-
if (!
|
|
158
|
+
if (!nextNode)
|
|
136
159
|
return internalError(1);
|
|
160
|
+
const node = nextNode;
|
|
161
|
+
nextNode = node.nextSibling || undefined;
|
|
162
|
+
let onDestroy = onDestroyMap.get(node);
|
|
163
|
+
if (onDestroy && node instanceof Element) {
|
|
164
|
+
if (onDestroy !== true) {
|
|
165
|
+
if (typeof onDestroy === 'function') {
|
|
166
|
+
onDestroy(node);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
destroyWithClass(node, onDestroy);
|
|
170
|
+
}
|
|
171
|
+
// This causes the element to be ignored from this function from now on:
|
|
172
|
+
onDestroyMap.set(node, true);
|
|
173
|
+
}
|
|
174
|
+
// Ignore the deleting element
|
|
137
175
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
176
|
+
else {
|
|
177
|
+
this.parentElement.removeChild(node);
|
|
178
|
+
}
|
|
179
|
+
if (node === lastNode)
|
|
180
|
+
break;
|
|
141
181
|
}
|
|
142
182
|
}
|
|
143
|
-
this.lastChild = undefined;
|
|
144
183
|
}
|
|
145
184
|
// run cleaners
|
|
146
185
|
this._clean();
|
|
@@ -223,6 +262,9 @@ class OnEachScope extends Scope {
|
|
|
223
262
|
this.renderer = renderer;
|
|
224
263
|
this.makeSortKey = makeSortKey;
|
|
225
264
|
}
|
|
265
|
+
// toString(): string {
|
|
266
|
+
// return `OnEachScope(collection=${this.collection})`
|
|
267
|
+
// }
|
|
226
268
|
onChange(index, newData, oldData) {
|
|
227
269
|
if (oldData === undefined) {
|
|
228
270
|
if (this.removedIndexes.has(index)) {
|
|
@@ -288,9 +330,9 @@ class OnEachScope extends Scope {
|
|
|
288
330
|
if (!scope) {
|
|
289
331
|
return internalError(6);
|
|
290
332
|
}
|
|
333
|
+
scope.remove();
|
|
291
334
|
this.byIndex.delete(itemIndex);
|
|
292
335
|
this.removeFromPosition(scope);
|
|
293
|
-
scope.remove();
|
|
294
336
|
}
|
|
295
337
|
findPosition(sortStr) {
|
|
296
338
|
// In case of duplicate `sortStr`s, this will return the first match.
|
|
@@ -315,12 +357,14 @@ class OnEachScope extends Scope {
|
|
|
315
357
|
let pos = this.findPosition(child.sortStr);
|
|
316
358
|
this.byPosition.splice(pos, 0, child);
|
|
317
359
|
// Based on the position in the list, set the precedingSibling for the new Scope
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
|
|
360
|
+
// and for the next sibling.
|
|
361
|
+
let nextSibling = this.byPosition[pos + 1];
|
|
362
|
+
if (nextSibling) {
|
|
363
|
+
child.precedingSibling = nextSibling.precedingSibling;
|
|
364
|
+
nextSibling.precedingSibling = child;
|
|
322
365
|
}
|
|
323
366
|
else {
|
|
367
|
+
child.precedingSibling = this.lastChild || this.precedingSibling;
|
|
324
368
|
this.lastChild = child;
|
|
325
369
|
}
|
|
326
370
|
}
|
|
@@ -333,10 +377,20 @@ class OnEachScope extends Scope {
|
|
|
333
377
|
// Yep, this is the right scope
|
|
334
378
|
this.byPosition.splice(pos, 1);
|
|
335
379
|
if (pos < this.byPosition.length) {
|
|
336
|
-
|
|
380
|
+
let nextSibling = this.byPosition[pos];
|
|
381
|
+
/* istanbul ignore next */
|
|
382
|
+
if (!nextSibling)
|
|
383
|
+
return internalError(8);
|
|
384
|
+
/* istanbul ignore next */
|
|
385
|
+
if (nextSibling.precedingSibling !== child)
|
|
386
|
+
return internalError(13);
|
|
387
|
+
nextSibling.precedingSibling = child.precedingSibling;
|
|
337
388
|
}
|
|
338
389
|
else {
|
|
339
|
-
|
|
390
|
+
/* istanbul ignore next */
|
|
391
|
+
if (child !== this.lastChild)
|
|
392
|
+
return internalError(12);
|
|
393
|
+
this.lastChild = child.precedingSibling === this.precedingSibling ? undefined : child.precedingSibling;
|
|
340
394
|
}
|
|
341
395
|
return;
|
|
342
396
|
}
|
|
@@ -355,6 +409,9 @@ class OnEachItemScope extends Scope {
|
|
|
355
409
|
this.parent = parent;
|
|
356
410
|
this.itemIndex = itemIndex;
|
|
357
411
|
}
|
|
412
|
+
// toString(): string {
|
|
413
|
+
// return `OnEachItemScope(itemIndex=${this.itemIndex} parentElement=${this.parentElement} parent=${this.parent} precedingSibling=${this.precedingSibling} lastChild=${this.lastChild})`
|
|
414
|
+
// }
|
|
358
415
|
queueRun() {
|
|
359
416
|
/* istanbul ignore next */
|
|
360
417
|
if (currentScope) {
|
|
@@ -412,6 +469,9 @@ class ObsCollection {
|
|
|
412
469
|
constructor() {
|
|
413
470
|
this.observers = new Map();
|
|
414
471
|
}
|
|
472
|
+
// toString(): string {
|
|
473
|
+
// return JSON.stringify(peek(() => this.getRecursive(3)))
|
|
474
|
+
// }
|
|
415
475
|
addObserver(index, observer) {
|
|
416
476
|
observer = observer;
|
|
417
477
|
let obsSet = this.observers.get(index);
|
|
@@ -639,7 +699,7 @@ class ObsObject extends ObsMap {
|
|
|
639
699
|
*
|
|
640
700
|
* Supported data types are: `string`, `number`, `boolean`, `undefined`, `null`,
|
|
641
701
|
* `Array`, `object` and `Map`. The latter three will always have `Store` objects as
|
|
642
|
-
* values, creating a tree of `Store
|
|
702
|
+
* values, creating a tree of `Store`-objects.
|
|
643
703
|
*/
|
|
644
704
|
export class Store {
|
|
645
705
|
constructor(value = undefined, index = undefined) {
|
|
@@ -694,48 +754,48 @@ export class Store {
|
|
|
694
754
|
return this.query({ path });
|
|
695
755
|
}
|
|
696
756
|
/**
|
|
697
|
-
* Like
|
|
757
|
+
* Like {@link Store.get}, but doesn't subscribe to changes.
|
|
698
758
|
*/
|
|
699
759
|
peek(...path) {
|
|
700
760
|
return this.query({ path, peek: true });
|
|
701
761
|
}
|
|
702
762
|
/**
|
|
703
|
-
* @returns Like
|
|
704
|
-
* Using this instead of just
|
|
763
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `number`.
|
|
764
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
705
765
|
*/
|
|
706
766
|
getNumber(...path) { return this.query({ path, type: 'number' }); }
|
|
707
767
|
/**
|
|
708
|
-
* @returns Like
|
|
709
|
-
* Using this instead of just
|
|
768
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `string`.
|
|
769
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
710
770
|
*/
|
|
711
771
|
getString(...path) { return this.query({ path, type: 'string' }); }
|
|
712
772
|
/**
|
|
713
|
-
* @returns Like
|
|
714
|
-
* Using this instead of just
|
|
773
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `boolean`.
|
|
774
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
715
775
|
*/
|
|
716
776
|
getBoolean(...path) { return this.query({ path, type: 'boolean' }); }
|
|
717
777
|
/**
|
|
718
|
-
* @returns Like
|
|
719
|
-
* Using this instead of just
|
|
778
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `function`.
|
|
779
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
720
780
|
*/
|
|
721
781
|
getFunction(...path) { return this.query({ path, type: 'function' }); }
|
|
722
782
|
/**
|
|
723
|
-
* @returns Like
|
|
724
|
-
* Using this instead of just
|
|
783
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `array`.
|
|
784
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
725
785
|
*/
|
|
726
786
|
getArray(...path) { return this.query({ path, type: 'array' }); }
|
|
727
787
|
/**
|
|
728
|
-
* @returns Like
|
|
729
|
-
* Using this instead of just
|
|
788
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `object`.
|
|
789
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
730
790
|
*/
|
|
731
791
|
getObject(...path) { return this.query({ path, type: 'object' }); }
|
|
732
792
|
/**
|
|
733
|
-
* @returns Like
|
|
734
|
-
* Using this instead of just
|
|
793
|
+
* @returns Like {@link Store.get}, but throws a `TypeError` if the resulting value is not of type `map`.
|
|
794
|
+
* Using this instead of just {@link Store.get} is especially useful from within TypeScript.
|
|
735
795
|
*/
|
|
736
796
|
getMap(...path) { return this.query({ path, type: 'map' }); }
|
|
737
797
|
/**
|
|
738
|
-
* Like
|
|
798
|
+
* Like {@link Store.get}, but the first parameter is the default value (returned when the Store
|
|
739
799
|
* contains `undefined`). This default value is also used to determine the expected type,
|
|
740
800
|
* and to throw otherwise.
|
|
741
801
|
*
|
|
@@ -758,7 +818,7 @@ export class Store {
|
|
|
758
818
|
return this.query({ type, defaultValue, path });
|
|
759
819
|
}
|
|
760
820
|
/** Retrieve a value, subscribing to all read `Store` values. This is a more flexible
|
|
761
|
-
* form of the
|
|
821
|
+
* form of the {@link Store.get} and {@link Store.peek} methods.
|
|
762
822
|
*
|
|
763
823
|
* @returns The resulting value, or `undefined` if the `path` does not exist.
|
|
764
824
|
*/
|
|
@@ -850,15 +910,15 @@ export class Store {
|
|
|
850
910
|
}
|
|
851
911
|
/**
|
|
852
912
|
* Sets the value to the last given argument. Any earlier argument are a Store-path that is first
|
|
853
|
-
* resolved/created using
|
|
913
|
+
* resolved/created using {@link Store.makeRef}.
|
|
854
914
|
*
|
|
855
915
|
* When a `Store` is passed in as the value, its value will be copied (subscribing to changes). In
|
|
856
916
|
* case the value is an object, an `Array` or a `Map`, a *reference* to that data structure will
|
|
857
917
|
* be created, so that changes made through one `Store` will be reflected through the other. Be
|
|
858
918
|
* carefull not to create loops in your `Store` tree that way, as that would cause any future
|
|
859
|
-
* call to
|
|
919
|
+
* call to {@link Store.get} to throw a `RangeError` (Maximum call stack size exceeded.)
|
|
860
920
|
*
|
|
861
|
-
* If you intent to make a copy instead of a reference, call
|
|
921
|
+
* If you intent to make a copy instead of a reference, call {@link Store.get} on the origin `Store`.
|
|
862
922
|
*
|
|
863
923
|
*
|
|
864
924
|
* @example
|
|
@@ -954,7 +1014,7 @@ export class Store {
|
|
|
954
1014
|
return pos;
|
|
955
1015
|
}
|
|
956
1016
|
/**
|
|
957
|
-
*
|
|
1017
|
+
* {@link Store.peek} the current value, pass it through `func`, and {@link Store.set} the resulting
|
|
958
1018
|
* value.
|
|
959
1019
|
* @param func The function transforming the value.
|
|
960
1020
|
*/
|
|
@@ -966,7 +1026,7 @@ export class Store {
|
|
|
966
1026
|
* subscribing to every level.
|
|
967
1027
|
* In case `undefined` is encountered while resolving the path, a newly
|
|
968
1028
|
* created `Store` containing `undefined` is returned. In that case, the
|
|
969
|
-
* `Store`'s
|
|
1029
|
+
* `Store`'s {@link Store.isDetached} method will return `true`.
|
|
970
1030
|
* In case something other than a collection is encountered, an error is thrown.
|
|
971
1031
|
*/
|
|
972
1032
|
ref(...path) {
|
|
@@ -1019,7 +1079,7 @@ export class Store {
|
|
|
1019
1079
|
}
|
|
1020
1080
|
return store;
|
|
1021
1081
|
}
|
|
1022
|
-
/** @
|
|
1082
|
+
/** @internal */
|
|
1023
1083
|
_observe() {
|
|
1024
1084
|
if (currentScope) {
|
|
1025
1085
|
if (this.collection.addObserver(this.idx, currentScope)) {
|
|
@@ -1031,7 +1091,7 @@ export class Store {
|
|
|
1031
1091
|
/**
|
|
1032
1092
|
* Iterate the specified collection (Array, Map or object), running the given code block for each item.
|
|
1033
1093
|
* When items are added to the collection at some later point, the code block will be ran for them as well.
|
|
1034
|
-
* When an item is removed, the
|
|
1094
|
+
* When an item is removed, the {@link Store.clean} handlers left by its code block are executed.
|
|
1035
1095
|
*
|
|
1036
1096
|
*
|
|
1037
1097
|
*
|
|
@@ -1138,7 +1198,7 @@ export class Store {
|
|
|
1138
1198
|
return out;
|
|
1139
1199
|
}
|
|
1140
1200
|
/**
|
|
1141
|
-
* @returns Returns `true` when the `Store` was created by
|
|
1201
|
+
* @returns Returns `true` when the `Store` was created by {@link Store.ref}ing a path that
|
|
1142
1202
|
* does not exist.
|
|
1143
1203
|
*/
|
|
1144
1204
|
isDetached() { return false; }
|
|
@@ -1167,12 +1227,18 @@ export class Store {
|
|
|
1167
1227
|
class DetachedStore extends Store {
|
|
1168
1228
|
isDetached() { return true; }
|
|
1169
1229
|
}
|
|
1230
|
+
let onCreateEnabled = false;
|
|
1231
|
+
let onDestroyMap = new WeakMap();
|
|
1232
|
+
function destroyWithClass(element, cls) {
|
|
1233
|
+
element.classList.add(cls);
|
|
1234
|
+
setTimeout(() => element.remove(), 2000);
|
|
1235
|
+
}
|
|
1170
1236
|
/**
|
|
1171
1237
|
* Create a new DOM element, and insert it into the DOM at the position held by the current scope.
|
|
1172
1238
|
* @param tag - The tag of the element to be created and optionally dot-separated class names. For example: `h1` or `p.intro.has_avatar`.
|
|
1173
1239
|
* @param rest - The other arguments are flexible and interpreted based on their types:
|
|
1174
1240
|
* - `string`: Used as textContent for the element.
|
|
1175
|
-
* - `object`: Used as attributes, properties or event listeners for the element. See
|
|
1241
|
+
* - `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.
|
|
1176
1242
|
* - `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.
|
|
1177
1243
|
* - `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.
|
|
1178
1244
|
* @example
|
|
@@ -1207,7 +1273,14 @@ export function node(tag = "", ...rest) {
|
|
|
1207
1273
|
let type = typeof item;
|
|
1208
1274
|
if (type === 'function') {
|
|
1209
1275
|
let scope = new SimpleScope(el, undefined, currentScope.queueOrder + 1, item);
|
|
1210
|
-
|
|
1276
|
+
if (onCreateEnabled) {
|
|
1277
|
+
onCreateEnabled = false;
|
|
1278
|
+
scope.update();
|
|
1279
|
+
onCreateEnabled = true;
|
|
1280
|
+
}
|
|
1281
|
+
else {
|
|
1282
|
+
scope.update();
|
|
1283
|
+
}
|
|
1211
1284
|
// Add it to our list of cleaners. Even if `scope` currently has
|
|
1212
1285
|
// no cleaners, it may get them in a future refresh.
|
|
1213
1286
|
currentScope.cleaners.push(scope);
|
|
@@ -1262,13 +1335,13 @@ function bindInput(el, store) {
|
|
|
1262
1335
|
};
|
|
1263
1336
|
}
|
|
1264
1337
|
else {
|
|
1338
|
+
onInputChange = () => store.set(type === 'number' || type === 'range' ? (el.value === '' ? null : +el.value) : el.value);
|
|
1265
1339
|
if (value === undefined)
|
|
1266
|
-
|
|
1340
|
+
onInputChange();
|
|
1267
1341
|
onStoreChange = value => {
|
|
1268
1342
|
if (el.value !== value)
|
|
1269
1343
|
el.value = value;
|
|
1270
1344
|
};
|
|
1271
|
-
onInputChange = () => store.set(el.value);
|
|
1272
1345
|
}
|
|
1273
1346
|
observe(() => {
|
|
1274
1347
|
onStoreChange(store.get());
|
|
@@ -1288,16 +1361,16 @@ export function text(text) {
|
|
|
1288
1361
|
return;
|
|
1289
1362
|
currentScope.addNode(document.createTextNode(text));
|
|
1290
1363
|
}
|
|
1291
|
-
export function prop(
|
|
1364
|
+
export function prop(name, value = undefined) {
|
|
1292
1365
|
if (!currentScope || !currentScope.parentElement)
|
|
1293
1366
|
throw new ScopeError(true);
|
|
1294
|
-
if (typeof
|
|
1295
|
-
for (let k in
|
|
1296
|
-
applyProp(currentScope.parentElement, k,
|
|
1367
|
+
if (typeof name === 'object') {
|
|
1368
|
+
for (let k in name) {
|
|
1369
|
+
applyProp(currentScope.parentElement, k, name[k]);
|
|
1297
1370
|
}
|
|
1298
1371
|
}
|
|
1299
1372
|
else {
|
|
1300
|
-
applyProp(currentScope.parentElement,
|
|
1373
|
+
applyProp(currentScope.parentElement, name, value);
|
|
1301
1374
|
}
|
|
1302
1375
|
}
|
|
1303
1376
|
/**
|
|
@@ -1347,7 +1420,7 @@ export function observe(func) {
|
|
|
1347
1420
|
mount(undefined, func);
|
|
1348
1421
|
}
|
|
1349
1422
|
/**
|
|
1350
|
-
* Like
|
|
1423
|
+
* Like {@link Store.observe}, but allow the function to create DOM elements using {@link Store.node}.
|
|
1351
1424
|
|
|
1352
1425
|
* @param func - The function to be (repeatedly) executed, possibly adding DOM elements to `parentElement`.
|
|
1353
1426
|
* @param parentElement - A DOM element that will be used as the parent element for calls to `node`.
|
|
@@ -1362,7 +1435,7 @@ export function observe(func) {
|
|
|
1362
1435
|
* })
|
|
1363
1436
|
* ```
|
|
1364
1437
|
*
|
|
1365
|
-
* An example nesting
|
|
1438
|
+
* An example nesting {@link Store.observe} within `mount`:
|
|
1366
1439
|
* ```
|
|
1367
1440
|
* let selected = new Store(0)
|
|
1368
1441
|
* let colors = new Store(new Map())
|
|
@@ -1403,7 +1476,7 @@ export function mount(parentElement, func) {
|
|
|
1403
1476
|
currentScope.cleaners.push(scope);
|
|
1404
1477
|
}
|
|
1405
1478
|
}
|
|
1406
|
-
/** Runs the given function, while not subscribing the current scope when reading
|
|
1479
|
+
/** Runs the given function, while not subscribing the current scope when reading {@link Store.Store} values.
|
|
1407
1480
|
*
|
|
1408
1481
|
* @param func Function to be executed immediately.
|
|
1409
1482
|
* @returns Whatever `func()` returns.
|
|
@@ -1438,33 +1511,47 @@ export function peek(func) {
|
|
|
1438
1511
|
* Helper functions
|
|
1439
1512
|
*/
|
|
1440
1513
|
function applyProp(el, prop, value) {
|
|
1441
|
-
if (
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
el.classList.remove(
|
|
1514
|
+
if (prop === 'create') {
|
|
1515
|
+
if (onCreateEnabled) {
|
|
1516
|
+
if (typeof value === 'function') {
|
|
1517
|
+
value(el);
|
|
1518
|
+
}
|
|
1519
|
+
else {
|
|
1520
|
+
el.classList.add(value);
|
|
1521
|
+
setTimeout(function () { el.classList.remove(value); }, 0);
|
|
1522
|
+
}
|
|
1449
1523
|
}
|
|
1450
1524
|
}
|
|
1451
|
-
else if (prop === '
|
|
1452
|
-
|
|
1453
|
-
el[prop] = value;
|
|
1525
|
+
else if (prop === 'destroy') {
|
|
1526
|
+
onDestroyMap.set(el, value);
|
|
1454
1527
|
}
|
|
1455
1528
|
else if (typeof value === 'function') {
|
|
1456
1529
|
// Set an event listener; remove it again on clean.
|
|
1457
1530
|
el.addEventListener(prop, value);
|
|
1458
1531
|
clean(() => el.removeEventListener(prop, value));
|
|
1459
1532
|
}
|
|
1460
|
-
else if (prop === '
|
|
1461
|
-
//
|
|
1462
|
-
|
|
1533
|
+
else if (prop === 'value' || prop === 'className' || prop === 'selectedIndex' || value === true || value === false) {
|
|
1534
|
+
// All boolean values and a few specific keys should be set as a property
|
|
1535
|
+
el[prop] = value;
|
|
1463
1536
|
}
|
|
1464
1537
|
else if (prop === 'text') {
|
|
1465
1538
|
// `text` is set as textContent
|
|
1466
1539
|
el.textContent = value;
|
|
1467
1540
|
}
|
|
1541
|
+
else if ((prop === 'class' || prop === 'className') && typeof value === 'object') {
|
|
1542
|
+
// Allow setting classes using an object where the keys are the names and
|
|
1543
|
+
// the values are booleans stating whether to set or remove.
|
|
1544
|
+
for (let name in value) {
|
|
1545
|
+
if (value[name])
|
|
1546
|
+
el.classList.add(name);
|
|
1547
|
+
else
|
|
1548
|
+
el.classList.remove(name);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
else if (prop === 'style' && typeof value === 'object') {
|
|
1552
|
+
// `style` can receive an object
|
|
1553
|
+
Object.assign(el.style, value);
|
|
1554
|
+
}
|
|
1468
1555
|
else {
|
|
1469
1556
|
// Everything else is an HTML attribute
|
|
1470
1557
|
el.setAttribute(prop, value);
|
|
@@ -1530,6 +1617,67 @@ class ScopeError extends Error {
|
|
|
1530
1617
|
super(`Operation not permitted outside of ${mount ? "a mount" : "an observe"}() scope`);
|
|
1531
1618
|
}
|
|
1532
1619
|
}
|
|
1620
|
+
const FADE_TIME = 400;
|
|
1621
|
+
const GROW_SHRINK_TRANSITION = `margin ${FADE_TIME}ms ease-out, transform ${FADE_TIME}ms ease-out`;
|
|
1622
|
+
function getGrowShrinkProps(el) {
|
|
1623
|
+
const parentStyle = el.parentElement ? getComputedStyle(el.parentElement) : {};
|
|
1624
|
+
const isHorizontal = parentStyle.display === 'flex' && (parentStyle.flexDirection || '').startsWith('row');
|
|
1625
|
+
return isHorizontal ?
|
|
1626
|
+
{ marginLeft: `-${el.offsetWidth / 2}px`, marginRight: `-${el.offsetWidth / 2}px`, transform: "scaleX(0)" } :
|
|
1627
|
+
{ marginBottom: `-${el.offsetHeight / 2}px`, marginTop: `-${el.offsetHeight / 2}px`, transform: "scaleY(0)" };
|
|
1628
|
+
}
|
|
1629
|
+
/** Do a grow transition for the given element. This is meant to be used as a
|
|
1630
|
+
* handler for the `create` property.
|
|
1631
|
+
*
|
|
1632
|
+
* @param el The element to transition.
|
|
1633
|
+
*
|
|
1634
|
+
* The transition doesn't look great for table elements, and may have problems
|
|
1635
|
+
* for other specific cases as well.
|
|
1636
|
+
*/
|
|
1637
|
+
export function grow(el) {
|
|
1638
|
+
// This timeout is to await all other elements having been added to the Dom
|
|
1639
|
+
scheduleTask(() => {
|
|
1640
|
+
// Make the element size 0 using transforms and negative margins.
|
|
1641
|
+
// This causes a browser layout, as we're querying el.offset<>.
|
|
1642
|
+
let props = getGrowShrinkProps(el);
|
|
1643
|
+
// The timeout is in order to batch all reads and then all writes when there
|
|
1644
|
+
// are multiple simultaneous grow transitions.
|
|
1645
|
+
scheduleTask(() => {
|
|
1646
|
+
Object.assign(el.style, props);
|
|
1647
|
+
// This timeout is to combine multiple transitions into a single browser layout
|
|
1648
|
+
scheduleTask(() => {
|
|
1649
|
+
// Make sure the layouting has been performed, to cause transitions to trigger
|
|
1650
|
+
el.offsetHeight;
|
|
1651
|
+
// Do the transitions
|
|
1652
|
+
el.style.transition = GROW_SHRINK_TRANSITION;
|
|
1653
|
+
for (let prop in props)
|
|
1654
|
+
el.style[prop] = "";
|
|
1655
|
+
setTimeout(() => {
|
|
1656
|
+
// Reset the element to a clean state
|
|
1657
|
+
el.style.transition = "";
|
|
1658
|
+
}, FADE_TIME);
|
|
1659
|
+
}, 2);
|
|
1660
|
+
}, 1);
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
/** Do a shrink transition for the given element, and remove it from the DOM
|
|
1664
|
+
* afterwards. This is meant to be used as a handler for the `destroy` property.
|
|
1665
|
+
*
|
|
1666
|
+
* @param el The element to transition and remove.
|
|
1667
|
+
*
|
|
1668
|
+
* The transition doesn't look great for table elements, and may have problems
|
|
1669
|
+
* for other specific cases as well.
|
|
1670
|
+
*/
|
|
1671
|
+
export function shrink(el) {
|
|
1672
|
+
const props = getGrowShrinkProps(el);
|
|
1673
|
+
// The timeout is in order to batch all reads and then all writes when there
|
|
1674
|
+
// are multiple simultaneous shrink transitions.
|
|
1675
|
+
scheduleTask(() => {
|
|
1676
|
+
el.style.transition = GROW_SHRINK_TRANSITION;
|
|
1677
|
+
Object.assign(el.style, props);
|
|
1678
|
+
setTimeout(() => el.remove(), FADE_TIME);
|
|
1679
|
+
}, 1);
|
|
1680
|
+
}
|
|
1533
1681
|
// @ts-ignore
|
|
1534
1682
|
// istanbul ignore next
|
|
1535
1683
|
if (!String.prototype.replaceAll)
|
package/dist/aberdeen.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e,t=[],i=new Set,n=!0,r=0;function s(e){if(!i.has(e)){if(r>42)throw new Error("Too many recursive updates from observes");t.length?e.queueOrder<t[t.length-1].queueOrder&&(n=!1):setTimeout(o,0),t.push(e),i.add(e)}}function o(){for(let e=0;e<t.length;){n||(t.splice(0,e),e=0,t.sort((e,t)=>e.queueOrder-t.queueOrder),n=!0);let s=t.length;for(;e<s&&n;e++){let n=t[e];i.delete(n),n.queueRun()}r++}t.length=0,r=0}function a(e){if("string"==typeof e)return e+"";{let t=function(e,t){let i="";for(;e>0;)i+=String.fromCharCode(t?65535-e%65533:2+e%65533),e=Math.floor(e/65533);return i}(Math.abs(Math.round(e)),e<0);return String.fromCharCode(128+(e>0?t.length:-t.length))+t}}class l{constructor(e,t,i){this.cleaners=[],this.isDead=!1,this.parentElement=e,this.precedingSibling=t,this.queueOrder=i}findPrecedingNode(){let e=this.precedingSibling;for(;e;){if(e instanceof Node)return e;let t=e.findLastNode();if(t)return t;e=e.precedingSibling}}findLastNode(){return this.lastChild instanceof Node?this.lastChild:this.lastChild instanceof l?this.lastChild.findLastNode()||this.lastChild.findPrecedingNode():void 0}addNode(e){if(!this.parentElement)throw new O(!0);let t=this.findLastNode()||this.findPrecedingNode();this.parentElement.insertBefore(e,t?t.nextSibling:this.parentElement.firstChild),this.lastChild=e}remove(){if(this.parentElement){let e=this.findLastNode();if(e){let t=this.findPrecedingNode();for(;e!==t;){if(!e)return E(1);let t=e.previousSibling||void 0;this.parentElement.removeChild(e),e=t}}this.lastChild=void 0}this._clean()}_clean(){this.isDead=!0;for(let e of this.cleaners)e._clean(this);this.cleaners.length=0}onChange(e,t,i){s(this)}}class h extends l{constructor(e,t,i,n){super(e,t,i),this.renderer=n}queueRun(){e&&E(2),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let t=e;e=this;try{this.renderer()}catch(e){C(e)}e=t}}class d{constructor(e,t,i){this.scope=e,this.collection=t,this.triggerCount=i,this.count=t.getCount(),t.addObserver(f,this),e.cleaners.push(this)}onChange(e,t,i){void 0===t?!this.triggerCount&&--this.count||s(this.scope):void 0===i&&(!this.triggerCount&&this.count++||s(this.scope))}_clean(){this.collection.removeObserver(f,this)}}class c extends l{constructor(e,t,i,n,r,s){super(e,t,i),this.byPosition=[],this.byIndex=new Map,this.newIndexes=new Set,this.removedIndexes=new Set,this.collection=n,this.renderer=r,this.makeSortKey=s}onChange(e,t,i){void 0===i?this.removedIndexes.has(e)?this.removedIndexes.delete(e):(this.newIndexes.add(e),s(this)):void 0===t&&(this.newIndexes.has(e)?this.newIndexes.delete(e):(this.removedIndexes.add(e),s(this)))}queueRun(){if(this.isDead)return;let e=this.removedIndexes;this.removedIndexes=new Set,e.forEach(e=>{this.removeChild(e)}),e=this.newIndexes,this.newIndexes=new Set,e.forEach(e=>{this.addChild(e)})}_clean(){super._clean(),this.collection.observers.delete(this);for(const[e,t]of this.byIndex)t._clean();this.byPosition.length=0,this.byIndex.clear()}renderInitial(){if(!e)return E(3);let t=e;this.collection.iterateIndexes(this),e=t}addChild(e){let t=new u(this.parentElement,void 0,this.queueOrder+1,this,e);this.byIndex.set(e,t),t.update()}removeChild(e){let t=this.byIndex.get(e);if(!t)return E(6);this.byIndex.delete(e),this.removeFromPosition(t),t.remove()}findPosition(e){let t=this.byPosition,i=0,n=t.length;if(!n||e>t[n-1].sortStr)return n;for(;i<n;){let r=i+n>>1;t[r].sortStr<e?i=r+1:n=r}return i}insertAtPosition(e){let t=this.findPosition(e.sortStr);this.byPosition.splice(t,0,e),e.precedingSibling=t>0?this.byPosition[t-1]:this.precedingSibling,t+1<this.byPosition.length?this.byPosition[t+1].precedingSibling=e:this.lastChild=e}removeFromPosition(e){if(""===e.sortStr)return;let t=this.findPosition(e.sortStr);for(;;){if(this.byPosition[t]===e)return this.byPosition.splice(t,1),void(t<this.byPosition.length?this.byPosition[t].precedingSibling=t>0?this.byPosition[t-1]:this.precedingSibling:this.lastChild=this.byPosition.length?this.byPosition[this.byPosition.length-1]:void 0);if(++t>=this.byPosition.length||this.byPosition[t].sortStr!==e.sortStr)return E(5)}}}class u extends l{constructor(e,t,i,n,r){super(e,t,i),this.sortStr="",this.parent=n,this.itemIndex=r}queueRun(){e&&E(4),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let t=e;e=this;let i,n=new Store(this.parent.collection,this.itemIndex);try{i=this.parent.makeSortKey(n)}catch(e){C(e)}let r=this.sortStr,s=null==i?"":(o=i)instanceof Array?o.map(a).join(""):a(o);var o;if(""!==r&&r!==s&&this.parent.removeFromPosition(this),this.sortStr=s,""!==s){s!==r&&this.parent.insertAtPosition(this);try{this.parent.renderer(n)}catch(e){C(e)}}e=t}}const f={};class p{constructor(){this.observers=new Map}addObserver(e,t){t=t;let i=this.observers.get(e);if(i){if(i.has(t))return!1;i.add(t)}else this.observers.set(e,new Set([t]));return!0}removeObserver(e,t){this.observers.get(e).delete(t)}emitChange(e,t,i){let n=this.observers.get(e);n&&n.forEach(n=>n.onChange(e,t,i)),n=this.observers.get(f),n&&n.forEach(n=>n.onChange(e,t,i))}_clean(e){this.removeObserver(f,e)}setIndex(e,t,i){const n=this.rawGet(e);if(!(n instanceof p)||t instanceof Store||!n.merge(t,i)){let i=x(t);i!==n&&(this.rawSet(e,i),this.emitChange(e,i,n))}}}class g extends p{constructor(){super(...arguments),this.data=[]}getType(){return"array"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i=[];for(let e=0;e<this.data.length;e++){let n=this.data[e];i.push(n instanceof p?t?n.getRecursive(t-1):new Store(this,e):n)}return i}rawGet(e){return this.data[e]}rawSet(e,t){if(e!==(0|e)||e<0||e>999999)throw new Error("Invalid array index "+JSON.stringify(e));for(this.data[e]=t;this.data.length>0&&void 0===this.data[this.data.length-1];)this.data.pop()}merge(e,t){if(!(e instanceof Array))return!1;for(let i=0;i<e.length;i++)this.setIndex(i,e[i],t);if(t&&this.data.length>e.length){for(let t=e.length;t<this.data.length;t++){let e=this.data[t];void 0!==e&&this.emitChange(t,void 0,e)}this.data.length=e.length}return!0}iterateIndexes(e){for(let t=0;t<this.data.length;t++)void 0!==this.data[t]&&e.addChild(t)}normalizeIndex(e){if("number"==typeof e)return e;if("string"==typeof e){let t=0|e;if(e.length&&t==e)return e}throw new Error("Invalid array index "+JSON.stringify(e))}getCount(){return this.data.length}}class v extends p{constructor(){super(...arguments),this.data=new Map}getType(){return"map"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i=new Map;return this.data.forEach((e,n)=>{i.set(n,e instanceof p?t?e.getRecursive(t-1):new Store(this,n):e)}),i}rawGet(e){return this.data.get(e)}rawSet(e,t){void 0===t?this.data.delete(e):this.data.set(e,t)}merge(e,t){return e instanceof Map&&(e.forEach((e,i)=>{this.setIndex(i,e,t)}),t&&this.data.forEach((t,i)=>{e.has(i)||this.setIndex(i,void 0,!1)}),!0)}iterateIndexes(e){this.data.forEach((t,i)=>{e.addChild(i)})}normalizeIndex(e){return e}getCount(){return this.data.size}}class y extends v{getType(){return"object"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i={};return this.data.forEach((e,n)=>{i[n]=e instanceof p?t?e.getRecursive(t-1):new Store(this,n):e}),i}merge(e,t){if(!e||e.constructor!==Object)return!1;for(let i in e)this.setIndex(i,e[i],t);return t&&this.data.forEach((t,i)=>{e.hasOwnProperty(i)||this.setIndex(i,void 0,!1)}),!0}normalizeIndex(e){let t=typeof e;if("string"===t)return e;if("number"===t)return""+e;throw new Error("Invalid object index "+JSON.stringify(e))}getCount(){let e=0;for(let t of this.data)e++;return e}}export class Store{constructor(e,t){if(void 0===t)this.collection=new g,this.idx=0,void 0!==e&&this.collection.rawSet(0,x(e));else{if(!(e instanceof p))throw new Error("1st parameter should be an ObsCollection if the 2nd is also given");this.collection=e,this.idx=t}}index(){return this.idx}_clean(e){this.collection.removeObserver(this.idx,e)}get(...e){return this.query({path:e})}peek(...e){return this.query({path:e,peek:!0})}getNumber(...e){return this.query({path:e,type:"number"})}getString(...e){return this.query({path:e,type:"string"})}getBoolean(...e){return this.query({path:e,type:"boolean"})}getFunction(...e){return this.query({path:e,type:"function"})}getArray(...e){return this.query({path:e,type:"array"})}getObject(...e){return this.query({path:e,type:"object"})}getMap(...e){return this.query({path:e,type:"map"})}getOr(e,...t){let i=typeof e;return"object"===i&&(e instanceof Map?i="map":e instanceof Array&&(i="array")),this.query({type:i,defaultValue:e,path:t})}query(t){if(t.peek&&e){let i=e;e=void 0;let n=this.query(t);return e=i,n}let i=(t.path&&t.path.length?this.ref(...t.path):this)._observe();if(t.type&&(void 0!==i||void 0===t.defaultValue)){let e=i instanceof p?i.getType():null===i?"null":typeof i;if(e!==t.type)throw new TypeError(`Expecting ${t.type} but got ${e}`)}return i instanceof p?i.getRecursive(null==t.depth?-1:t.depth-1):void 0===i?t.defaultValue:i}isEmpty(...t){let i=this.ref(...t)._observe();if(i instanceof p){if(e){return!new d(e,i,!1).count}return!i.getCount()}if(void 0===i)return!0;throw new Error("isEmpty() expects a collection or undefined, but got "+JSON.stringify(i))}count(...t){let i=this.ref(...t)._observe();if(i instanceof p){if(e){return new d(e,i,!0).count}return i.getCount()}if(void 0===i)return 0;throw new Error("count() expects a collection or undefined, but got "+JSON.stringify(i))}getType(...e){let t=this.ref(...e)._observe();return t instanceof p?t.getType():null===t?"null":typeof t}set(...e){let t=e.pop(),i=this.makeRef(...e);i.collection.setIndex(i.idx,t,!0)}merge(...e){let t=e.pop(),i=this.makeRef(...e);i.collection.setIndex(i.idx,t,!1)}delete(...e){let t=this.makeRef(...e);t.collection.setIndex(t.idx,void 0,!0)}push(...e){let t=e.pop(),i=this.makeRef(...e),n=i.collection.rawGet(i.idx);if(void 0===n)n=new g,i.collection.setIndex(i.idx,n,!0);else if(!(n instanceof g))throw new Error("push() is only allowed for an array or undefined (which would become an array)");let r=x(t),s=n.data.length;return n.data.push(r),n.emitChange(s,r,void 0),s}modify(e){this.set(e(this.query({peek:!0})))}ref(...e){let t=this;for(let i=0;i<e.length;i++){let n=t._observe();if(!(n instanceof p)){if(void 0!==n)throw new Error(`Value ${JSON.stringify(n)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(e)})`);return new m}t=new Store(n,n.normalizeIndex(e[i]))}return t}makeRef(...e){let t=this;for(let i=0;i<e.length;i++){let n=t.collection.rawGet(t.idx);if(!(n instanceof p)){if(void 0!==n)throw new Error(`Value ${JSON.stringify(n)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(e)})`);n=new y,t.collection.rawSet(t.idx,n),t.collection.emitChange(t.idx,n,void 0)}t=new Store(n,n.normalizeIndex(e[i]))}return t}_observe(){return e&&this.collection.addObserver(this.idx,e)&&e.cleaners.push(this),this.collection.rawGet(this.idx)}onEach(...t){let i=S,n=t.pop();if("function"!=typeof t[t.length-1]||"function"!=typeof n&&null!=n||(null!=n&&(i=n),n=t.pop()),"function"!=typeof n)throw new Error("onEach() expects a render function as its last argument but got "+JSON.stringify(n));if(!e)throw new O(!1);let r=this.ref(...t)._observe();if(r instanceof p){let t=new c(e.parentElement,e.lastChild||e.precedingSibling,e.queueOrder+1,r,n,i);r.addObserver(f,t),e.cleaners.push(t),e.lastChild=t,t.renderInitial()}else if(void 0!==r)throw new Error("onEach() attempted on a value that is neither a collection nor undefined")}map(e){let t=new Store(new Map);return this.onEach(i=>{let n=e(i);if(void 0!==n){let e=i.index();t.set(e,n),clean(()=>{t.delete(e)})}}),t}multiMap(e){let t=new Store(new Map);return this.onEach(i=>{let n,r=e(i);if(r.constructor===Object){for(let e in r)t.set(e,r[e]);n=Object.keys(r)}else{if(!(r instanceof Map))return;r.forEach((e,i)=>{t.set(i,e)}),n=Array.from(r.keys())}n.length&&clean(()=>{for(let e of n)t.delete(e)})}),t}isDetached(){return!1}dump(){let e=this.getType();"array"===e||"object"===e||"map"===e?(text("<"+e+">"),node("ul",()=>{this.onEach(e=>{node("li",()=>{text(JSON.stringify(e.index())+": "),e.dump()})})})):text(JSON.stringify(this.get()))}}class m extends Store{isDetached(){return!0}}export function node(t="",...i){if(!e)throw new O(!0);let n;if(t instanceof Element)n=t;else{let e,i=t.indexOf(".");i>=0&&(e=t.substr(i+1),t=t.substr(0,i)),n=document.createElement(t||"div"),e&&(n.className=e.replaceAll("."," "))}e.addNode(n);for(let t of i){let i=typeof t;if("function"===i){let i=new h(n,void 0,e.queueOrder+1,t);i.update(),e.cleaners.push(i)}else if("string"===i||"number"===i)n.textContent=t;else if("object"===i&&t&&t.constructor===Object)for(let e in t)b(n,e,t[e]);else if(t instanceof Store)w(n,t);else if(null!=t)throw new Error("Unexpected argument "+JSON.stringify(t))}}export function html(t){if(!e||!e.parentElement)throw new O(!0);let i=document.createElement(e.parentElement.tagName);for(i.innerHTML=""+t;i.firstChild;)e.addNode(i.firstChild)}function w(e,t){let i,n,r=e.getAttribute("type"),s=t.query({peek:!0});"checkbox"===r?(void 0===s&&t.set(e.checked),i=t=>e.checked=t,n=()=>t.set(e.checked)):"radio"===r?(void 0===s&&e.checked&&t.set(e.value),i=t=>e.checked=t===e.value,n=()=>{e.checked&&t.set(e.value)}):(void 0===s&&t.set(e.value),i=t=>{e.value!==t&&(e.value=t)},n=()=>t.set(e.value)),observe(()=>{i(t.get())}),e.addEventListener("input",n),clean(()=>{e.removeEventListener("input",n)})}export function text(t){if(!e)throw new O(!0);null!=t&&e.addNode(document.createTextNode(t))}export function prop(t,i){if(!e||!e.parentElement)throw new O(!0);if("object"==typeof t)for(let i in t)b(e.parentElement,i,t[i]);else b(e.parentElement,t,i)}export function getParentElement(){if(!e||!e.parentElement)throw new O(!0);return e.parentElement}export function clean(t){if(!e)throw new O(!1);e.cleaners.push({_clean:t})}export function observe(e){mount(void 0,e)}export function mount(t,i){let n;t||!e?n=new h(t,void 0,0,i):(n=new h(e.parentElement,e.lastChild||e.precedingSibling,e.queueOrder+1,i),e.lastChild=n),n.update(),e&&e.cleaners.push(n)}export function peek(t){let i=e;e=void 0;try{return t()}finally{e=i}}function b(e,t,i){if("class"!==t&&"className"!==t||"object"!=typeof i)"value"===t||"className"===t||"selectedIndex"===t||!0===i||!1===i?e[t]=i:"function"==typeof i?(e.addEventListener(t,i),clean(()=>e.removeEventListener(t,i))):"style"===t&&"object"==typeof i?Object.assign(e.style,i):"text"===t?e.textContent=i:e.setAttribute(t,i);else for(let t in i)i[t]?e.classList.add(t):e.classList.remove(t)}function x(e){if("object"==typeof e&&e){if(e instanceof Store)return e._observe();if(e instanceof Map){let t=new v;return e.forEach((e,i)=>{let n=x(e);void 0!==n&&t.rawSet(i,n)}),t}if(e instanceof Array){let t=new g;for(let i=0;i<e.length;i++){let n=x(e[i]);void 0!==n&&t.rawSet(i,n)}return t}if(e.constructor===Object){let t=new y;for(let i in e){let n=x(e[i]);void 0!==n&&t.rawSet(i,n)}return t}return e}return e}function S(e){return e.index()}function E(e){let t=new Error("Aberdeen internal error "+e);setTimeout(()=>{throw t},0)}function C(e){setTimeout(()=>{throw e},0)}class O extends Error{constructor(e){super(`Operation not permitted outside of ${e?"a mount":"an observe"}() scope`)}}String.prototype.replaceAll||(String.prototype.replaceAll=function(e,t){return this.split(e).join(t)});
|
|
1
|
+
let e,t=[],i=new Set,n=!0,r=0;function s(e){if(!i.has(e)){if(r>42)throw new Error("Too many recursive updates from observes");t.length?e.queueOrder<t[t.length-1].queueOrder&&(n=!1):setTimeout(o,0),t.push(e),i.add(e)}}function o(){w=!0;for(let e=0;e<t.length;){n||(t.splice(0,e),e=0,t.sort((e,t)=>e.queueOrder-t.queueOrder),n=!0);let s=t.length;for(;e<s&&n;e++){let n=t[e];i.delete(n),n.queueRun()}r++}t.length=0,r=0,w=!1}export function scheduleTask(e,t=0){s({queueOrder:1e3+t,queueRun:e})}function a(e){if("string"==typeof e)return e+"";{let t=function(e,t){let i="";for(;e>0;)i+=String.fromCharCode(t?65535-e%65533:2+e%65533),e=Math.floor(e/65533);return i}(Math.abs(Math.round(e)),e<0);return String.fromCharCode(128+(e>0?t.length:-t.length))+t}}class l{constructor(e,t,i){this.cleaners=[],this.isDead=!1,this.parentElement=e,this.precedingSibling=t,this.queueOrder=i}findPrecedingNode(e){let t,i=this;for(;(t=i.precedingSibling)&&t!==e;){if(t instanceof Node)return t;let e=t.findLastNode();if(e)return e;i=t}}findLastNode(){if(this.lastChild)return this.lastChild instanceof Node?this.lastChild:this.lastChild.findLastNode()||this.lastChild.findPrecedingNode(this.precedingSibling)}addNode(e){if(!this.parentElement)throw new k(!0);let t=this.findLastNode()||this.findPrecedingNode();this.parentElement.insertBefore(e,t?t.nextSibling:this.parentElement.firstChild),this.lastChild=e}remove(){if(this.parentElement){let e=this.findLastNode();if(e){let t=this.findPrecedingNode();for(t=t?t.nextSibling:this.parentElement.firstChild,this.lastChild=void 0;;){if(!t)return I(1);const i=t;t=i.nextSibling||void 0;let n=b.get(i);if(n&&i instanceof Element?!0!==n&&("function"==typeof n?n(i):x(i,n),b.set(i,!0)):this.parentElement.removeChild(i),i===e)break}}}this._clean()}_clean(){this.isDead=!0;for(let e of this.cleaners)e._clean(this);this.cleaners.length=0}onChange(e,t,i){s(this)}}class h extends l{constructor(e,t,i,n){super(e,t,i),this.renderer=n}queueRun(){e&&I(2),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let t=e;e=this;try{this.renderer()}catch(e){N(e)}e=t}}class d{constructor(e,t,i){this.scope=e,this.collection=t,this.triggerCount=i,this.count=t.getCount(),t.addObserver(f,this),e.cleaners.push(this)}onChange(e,t,i){void 0===t?!this.triggerCount&&--this.count||s(this.scope):void 0===i&&(!this.triggerCount&&this.count++||s(this.scope))}_clean(){this.collection.removeObserver(f,this)}}class c extends l{constructor(e,t,i,n,r,s){super(e,t,i),this.byPosition=[],this.byIndex=new Map,this.newIndexes=new Set,this.removedIndexes=new Set,this.collection=n,this.renderer=r,this.makeSortKey=s}onChange(e,t,i){void 0===i?this.removedIndexes.has(e)?this.removedIndexes.delete(e):(this.newIndexes.add(e),s(this)):void 0===t&&(this.newIndexes.has(e)?this.newIndexes.delete(e):(this.removedIndexes.add(e),s(this)))}queueRun(){if(this.isDead)return;let e=this.removedIndexes;this.removedIndexes=new Set,e.forEach(e=>{this.removeChild(e)}),e=this.newIndexes,this.newIndexes=new Set,e.forEach(e=>{this.addChild(e)})}_clean(){super._clean(),this.collection.observers.delete(this);for(const[e,t]of this.byIndex)t._clean();this.byPosition.length=0,this.byIndex.clear()}renderInitial(){if(!e)return I(3);let t=e;this.collection.iterateIndexes(this),e=t}addChild(e){let t=new u(this.parentElement,void 0,this.queueOrder+1,this,e);this.byIndex.set(e,t),t.update()}removeChild(e){let t=this.byIndex.get(e);if(!t)return I(6);t.remove(),this.byIndex.delete(e),this.removeFromPosition(t)}findPosition(e){let t=this.byPosition,i=0,n=t.length;if(!n||e>t[n-1].sortStr)return n;for(;i<n;){let r=i+n>>1;t[r].sortStr<e?i=r+1:n=r}return i}insertAtPosition(e){let t=this.findPosition(e.sortStr);this.byPosition.splice(t,0,e);let i=this.byPosition[t+1];i?(e.precedingSibling=i.precedingSibling,i.precedingSibling=e):(e.precedingSibling=this.lastChild||this.precedingSibling,this.lastChild=e)}removeFromPosition(e){if(""===e.sortStr)return;let t=this.findPosition(e.sortStr);for(;;){if(this.byPosition[t]===e){if(this.byPosition.splice(t,1),t<this.byPosition.length){let i=this.byPosition[t];if(!i)return I(8);if(i.precedingSibling!==e)return I(13);i.precedingSibling=e.precedingSibling}else{if(e!==this.lastChild)return I(12);this.lastChild=e.precedingSibling===this.precedingSibling?void 0:e.precedingSibling}return}if(++t>=this.byPosition.length||this.byPosition[t].sortStr!==e.sortStr)return I(5)}}}class u extends l{constructor(e,t,i,n,r){super(e,t,i),this.sortStr="",this.parent=n,this.itemIndex=r}queueRun(){e&&I(4),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let t=e;e=this;let i,n=new Store(this.parent.collection,this.itemIndex);try{i=this.parent.makeSortKey(n)}catch(e){N(e)}let r=this.sortStr,s=null==i?"":(o=i)instanceof Array?o.map(a).join(""):a(o);var o;if(""!==r&&r!==s&&this.parent.removeFromPosition(this),this.sortStr=s,""!==s){s!==r&&this.parent.insertAtPosition(this);try{this.parent.renderer(n)}catch(e){N(e)}}e=t}}const f={};class p{constructor(){this.observers=new Map}addObserver(e,t){t=t;let i=this.observers.get(e);if(i){if(i.has(t))return!1;i.add(t)}else this.observers.set(e,new Set([t]));return!0}removeObserver(e,t){this.observers.get(e).delete(t)}emitChange(e,t,i){let n=this.observers.get(e);n&&n.forEach(n=>n.onChange(e,t,i)),n=this.observers.get(f),n&&n.forEach(n=>n.onChange(e,t,i))}_clean(e){this.removeObserver(f,e)}setIndex(e,t,i){const n=this.rawGet(e);if(!(n instanceof p)||t instanceof Store||!n.merge(t,i)){let i=C(t);i!==n&&(this.rawSet(e,i),this.emitChange(e,i,n))}}}class g extends p{constructor(){super(...arguments),this.data=[]}getType(){return"array"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i=[];for(let e=0;e<this.data.length;e++){let n=this.data[e];i.push(n instanceof p?t?n.getRecursive(t-1):new Store(this,e):n)}return i}rawGet(e){return this.data[e]}rawSet(e,t){if(e!==(0|e)||e<0||e>999999)throw new Error("Invalid array index "+JSON.stringify(e));for(this.data[e]=t;this.data.length>0&&void 0===this.data[this.data.length-1];)this.data.pop()}merge(e,t){if(!(e instanceof Array))return!1;for(let i=0;i<e.length;i++)this.setIndex(i,e[i],t);if(t&&this.data.length>e.length){for(let t=e.length;t<this.data.length;t++){let e=this.data[t];void 0!==e&&this.emitChange(t,void 0,e)}this.data.length=e.length}return!0}iterateIndexes(e){for(let t=0;t<this.data.length;t++)void 0!==this.data[t]&&e.addChild(t)}normalizeIndex(e){if("number"==typeof e)return e;if("string"==typeof e){let t=0|e;if(e.length&&t==e)return e}throw new Error("Invalid array index "+JSON.stringify(e))}getCount(){return this.data.length}}class m extends p{constructor(){super(...arguments),this.data=new Map}getType(){return"map"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i=new Map;return this.data.forEach((e,n)=>{i.set(n,e instanceof p?t?e.getRecursive(t-1):new Store(this,n):e)}),i}rawGet(e){return this.data.get(e)}rawSet(e,t){void 0===t?this.data.delete(e):this.data.set(e,t)}merge(e,t){return e instanceof Map&&(e.forEach((e,i)=>{this.setIndex(i,e,t)}),t&&this.data.forEach((t,i)=>{e.has(i)||this.setIndex(i,void 0,!1)}),!0)}iterateIndexes(e){this.data.forEach((t,i)=>{e.addChild(i)})}normalizeIndex(e){return e}getCount(){return this.data.size}}class v extends m{getType(){return"object"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i={};return this.data.forEach((e,n)=>{i[n]=e instanceof p?t?e.getRecursive(t-1):new Store(this,n):e}),i}merge(e,t){if(!e||e.constructor!==Object)return!1;for(let i in e)this.setIndex(i,e[i],t);return t&&this.data.forEach((t,i)=>{e.hasOwnProperty(i)||this.setIndex(i,void 0,!1)}),!0}normalizeIndex(e){let t=typeof e;if("string"===t)return e;if("number"===t)return""+e;throw new Error("Invalid object index "+JSON.stringify(e))}getCount(){let e=0;for(let t of this.data)e++;return e}}export class Store{constructor(e,t){if(void 0===t)this.collection=new g,this.idx=0,void 0!==e&&this.collection.rawSet(0,C(e));else{if(!(e instanceof p))throw new Error("1st parameter should be an ObsCollection if the 2nd is also given");this.collection=e,this.idx=t}}index(){return this.idx}_clean(e){this.collection.removeObserver(this.idx,e)}get(...e){return this.query({path:e})}peek(...e){return this.query({path:e,peek:!0})}getNumber(...e){return this.query({path:e,type:"number"})}getString(...e){return this.query({path:e,type:"string"})}getBoolean(...e){return this.query({path:e,type:"boolean"})}getFunction(...e){return this.query({path:e,type:"function"})}getArray(...e){return this.query({path:e,type:"array"})}getObject(...e){return this.query({path:e,type:"object"})}getMap(...e){return this.query({path:e,type:"map"})}getOr(e,...t){let i=typeof e;return"object"===i&&(e instanceof Map?i="map":e instanceof Array&&(i="array")),this.query({type:i,defaultValue:e,path:t})}query(t){if(t.peek&&e){let i=e;e=void 0;let n=this.query(t);return e=i,n}let i=(t.path&&t.path.length?this.ref(...t.path):this)._observe();if(t.type&&(void 0!==i||void 0===t.defaultValue)){let e=i instanceof p?i.getType():null===i?"null":typeof i;if(e!==t.type)throw new TypeError(`Expecting ${t.type} but got ${e}`)}return i instanceof p?i.getRecursive(null==t.depth?-1:t.depth-1):void 0===i?t.defaultValue:i}isEmpty(...t){let i=this.ref(...t)._observe();if(i instanceof p){if(e){return!new d(e,i,!1).count}return!i.getCount()}if(void 0===i)return!0;throw new Error("isEmpty() expects a collection or undefined, but got "+JSON.stringify(i))}count(...t){let i=this.ref(...t)._observe();if(i instanceof p){if(e){return new d(e,i,!0).count}return i.getCount()}if(void 0===i)return 0;throw new Error("count() expects a collection or undefined, but got "+JSON.stringify(i))}getType(...e){let t=this.ref(...e)._observe();return t instanceof p?t.getType():null===t?"null":typeof t}set(...e){let t=e.pop(),i=this.makeRef(...e);i.collection.setIndex(i.idx,t,!0)}merge(...e){let t=e.pop(),i=this.makeRef(...e);i.collection.setIndex(i.idx,t,!1)}delete(...e){let t=this.makeRef(...e);t.collection.setIndex(t.idx,void 0,!0)}push(...e){let t=e.pop(),i=this.makeRef(...e),n=i.collection.rawGet(i.idx);if(void 0===n)n=new g,i.collection.setIndex(i.idx,n,!0);else if(!(n instanceof g))throw new Error("push() is only allowed for an array or undefined (which would become an array)");let r=C(t),s=n.data.length;return n.data.push(r),n.emitChange(s,r,void 0),s}modify(e){this.set(e(this.query({peek:!0})))}ref(...e){let t=this;for(let i=0;i<e.length;i++){let n=t._observe();if(!(n instanceof p)){if(void 0!==n)throw new Error(`Value ${JSON.stringify(n)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(e)})`);return new y}t=new Store(n,n.normalizeIndex(e[i]))}return t}makeRef(...e){let t=this;for(let i=0;i<e.length;i++){let n=t.collection.rawGet(t.idx);if(!(n instanceof p)){if(void 0!==n)throw new Error(`Value ${JSON.stringify(n)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(e)})`);n=new v,t.collection.rawSet(t.idx,n),t.collection.emitChange(t.idx,n,void 0)}t=new Store(n,n.normalizeIndex(e[i]))}return t}_observe(){return e&&this.collection.addObserver(this.idx,e)&&e.cleaners.push(this),this.collection.rawGet(this.idx)}onEach(...t){let i=O,n=t.pop();if("function"!=typeof t[t.length-1]||"function"!=typeof n&&null!=n||(null!=n&&(i=n),n=t.pop()),"function"!=typeof n)throw new Error("onEach() expects a render function as its last argument but got "+JSON.stringify(n));if(!e)throw new k(!1);let r=this.ref(...t)._observe();if(r instanceof p){let t=new c(e.parentElement,e.lastChild||e.precedingSibling,e.queueOrder+1,r,n,i);r.addObserver(f,t),e.cleaners.push(t),e.lastChild=t,t.renderInitial()}else if(void 0!==r)throw new Error("onEach() attempted on a value that is neither a collection nor undefined")}map(e){let t=new Store(new Map);return this.onEach(i=>{let n=e(i);if(void 0!==n){let e=i.index();t.set(e,n),clean(()=>{t.delete(e)})}}),t}multiMap(e){let t=new Store(new Map);return this.onEach(i=>{let n,r=e(i);if(r.constructor===Object){for(let e in r)t.set(e,r[e]);n=Object.keys(r)}else{if(!(r instanceof Map))return;r.forEach((e,i)=>{t.set(i,e)}),n=Array.from(r.keys())}n.length&&clean(()=>{for(let e of n)t.delete(e)})}),t}isDetached(){return!1}dump(){let e=this.getType();"array"===e||"object"===e||"map"===e?(text("<"+e+">"),node("ul",()=>{this.onEach(e=>{node("li",()=>{text(JSON.stringify(e.index())+": "),e.dump()})})})):text(JSON.stringify(this.get()))}}class y extends Store{isDetached(){return!0}}let w=!1,b=new WeakMap;function x(e,t){e.classList.add(t),setTimeout(()=>e.remove(),2e3)}export function node(t="",...i){if(!e)throw new k(!0);let n;if(t instanceof Element)n=t;else{let e,i=t.indexOf(".");i>=0&&(e=t.substr(i+1),t=t.substr(0,i)),n=document.createElement(t||"div"),e&&(n.className=e.replaceAll("."," "))}e.addNode(n);for(let t of i){let i=typeof t;if("function"===i){let i=new h(n,void 0,e.queueOrder+1,t);w?(w=!1,i.update(),w=!0):i.update(),e.cleaners.push(i)}else if("string"===i||"number"===i)n.textContent=t;else if("object"===i&&t&&t.constructor===Object)for(let e in t)E(n,e,t[e]);else if(t instanceof Store)S(n,t);else if(null!=t)throw new Error("Unexpected argument "+JSON.stringify(t))}}export function html(t){if(!e||!e.parentElement)throw new k(!0);let i=document.createElement(e.parentElement.tagName);for(i.innerHTML=""+t;i.firstChild;)e.addNode(i.firstChild)}function S(e,t){let i,n,r=e.getAttribute("type"),s=t.query({peek:!0});"checkbox"===r?(void 0===s&&t.set(e.checked),i=t=>e.checked=t,n=()=>t.set(e.checked)):"radio"===r?(void 0===s&&e.checked&&t.set(e.value),i=t=>e.checked=t===e.value,n=()=>{e.checked&&t.set(e.value)}):(n=()=>t.set("number"===r||"range"===r?""===e.value?null:+e.value:e.value),void 0===s&&n(),i=t=>{e.value!==t&&(e.value=t)}),observe(()=>{i(t.get())}),e.addEventListener("input",n),clean(()=>{e.removeEventListener("input",n)})}export function text(t){if(!e)throw new k(!0);null!=t&&e.addNode(document.createTextNode(t))}export function prop(t,i){if(!e||!e.parentElement)throw new k(!0);if("object"==typeof t)for(let i in t)E(e.parentElement,i,t[i]);else E(e.parentElement,t,i)}export function getParentElement(){if(!e||!e.parentElement)throw new k(!0);return e.parentElement}export function clean(t){if(!e)throw new k(!1);e.cleaners.push({_clean:t})}export function observe(e){mount(void 0,e)}export function mount(t,i){let n;t||!e?n=new h(t,void 0,0,i):(n=new h(e.parentElement,e.lastChild||e.precedingSibling,e.queueOrder+1,i),e.lastChild=n),n.update(),e&&e.cleaners.push(n)}export function peek(t){let i=e;e=void 0;try{return t()}finally{e=i}}function E(e,t,i){if("create"===t)w&&("function"==typeof i?i(e):(e.classList.add(i),setTimeout((function(){e.classList.remove(i)}),0)));else if("destroy"===t)b.set(e,i);else if("function"==typeof i)e.addEventListener(t,i),clean(()=>e.removeEventListener(t,i));else if("value"===t||"className"===t||"selectedIndex"===t||!0===i||!1===i)e[t]=i;else if("text"===t)e.textContent=i;else if("class"!==t&&"className"!==t||"object"!=typeof i)"style"===t&&"object"==typeof i?Object.assign(e.style,i):e.setAttribute(t,i);else for(let t in i)i[t]?e.classList.add(t):e.classList.remove(t)}function C(e){if("object"==typeof e&&e){if(e instanceof Store)return e._observe();if(e instanceof Map){let t=new m;return e.forEach((e,i)=>{let n=C(e);void 0!==n&&t.rawSet(i,n)}),t}if(e instanceof Array){let t=new g;for(let i=0;i<e.length;i++){let n=C(e[i]);void 0!==n&&t.rawSet(i,n)}return t}if(e.constructor===Object){let t=new v;for(let i in e){let n=C(e[i]);void 0!==n&&t.rawSet(i,n)}return t}return e}return e}function O(e){return e.index()}function I(e){let t=new Error("Aberdeen internal error "+e);setTimeout(()=>{throw t},0)}function N(e){setTimeout(()=>{throw e},0)}class k extends Error{constructor(e){super(`Operation not permitted outside of ${e?"a mount":"an observe"}() scope`)}}function q(e){const t=e.parentElement?getComputedStyle(e.parentElement):{};return"flex"===t.display&&(t.flexDirection||"").startsWith("row")?{marginLeft:`-${e.offsetWidth/2}px`,marginRight:`-${e.offsetWidth/2}px`,transform:"scaleX(0)"}:{marginBottom:`-${e.offsetHeight/2}px`,marginTop:`-${e.offsetHeight/2}px`,transform:"scaleY(0)"}}export function grow(e){scheduleTask(()=>{let t=q(e);scheduleTask(()=>{Object.assign(e.style,t),scheduleTask(()=>{e.offsetHeight,e.style.transition="margin 400ms ease-out, transform 400ms ease-out";for(let i in t)e.style[i]="";setTimeout(()=>{e.style.transition=""},400)},2)},1)})}export function shrink(e){const t=q(e);scheduleTask(()=>{e.style.transition="margin 400ms ease-out, transform 400ms ease-out",Object.assign(e.style,t),setTimeout(()=>e.remove(),400)},1)}String.prototype.replaceAll||(String.prototype.replaceAll=function(e,t){return this.split(e).join(t)});
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aberdeen",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "A TypeScript/JavaScript library for quickly building performant declarative user interfaces without the use of a virtual DOM.",
|
|
5
5
|
"main": "dist/aberdeen.js",
|
|
6
6
|
"types": "dist/aberdeen.d.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "$npm_execpath run build-dist && $npm_execpath run build-docs",
|
|
9
|
-
"build-dist": "tsc && terser --module --compress --mangle -- dist/aberdeen.js > dist/aberdeen.min.js",
|
|
9
|
+
"build-dist": "rm -f dist/aberdeen.js && tsc && chmod a-w dist/aberdeen.js && terser --module --compress --mangle -- dist/aberdeen.js > dist/aberdeen.min.js",
|
|
10
10
|
"build-docs": "typedoc --excludePrivate --excludeInternal src/aberdeen.ts",
|
|
11
|
-
"build-test": "[ tests/build/aberdeen.js -nt src/aberdeen.ts ] || tsc -t ES2015 -m commonjs --outdir tests/build src/aberdeen.ts",
|
|
11
|
+
"build-test": "[ tests/build/aberdeen.js -nt src/aberdeen.ts ] || ( rm -f tests/build/aberdeen.js && tsc -t ES2015 -m commonjs --outdir tests/build src/aberdeen.ts && chmod a-w tests/build/aberdeen.js )",
|
|
12
12
|
"coverage": "$npm_execpath run build-test && nyc mocha --file tests/_init.js tests/[^_]*.js",
|
|
13
13
|
"test": "$npm_execpath run build-test && mocha --file tests/_init.js tests/[^_]*.js",
|
|
14
14
|
"prepack": "$npm_execpath run test && $npm_execpath run build"
|