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.
package/README.md CHANGED
@@ -8,7 +8,7 @@ The key insight is the use of many small anonymous functions, that will automati
8
8
  - It provides a flexible and simple to understand model for reactive user-interface building.
9
9
  - It allows you to express user-interfaces in plain JavaScript (or TypeScript) in an easy to read form, without (JSX-like) compilation steps.
10
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 14kb minimized and without any run-time dependencies.
11
+ - It's lightweight, at about 16kb minimized and without any run-time dependencies.
12
12
  - It comes with batteries included, providing modules for..
13
13
  - Client-side routing.
14
14
  - Optimistic user-interface updates (predictions) while awaiting a server response.
@@ -25,25 +25,30 @@ The key insight is the use of many small anonymous functions, that will automati
25
25
  To get a quick impression of what Aberdeen code looks like, this is all of the JavaScript for the above Tic-tac-toe demo:
26
26
 
27
27
  ```javascript
28
- import {node, prop, mount, Store, text} from 'https://cdn.jsdelivr.net/npm/aberdeen/+esm';
28
+ import {$, mount, Store} from 'https://cdn.jsdelivr.net/npm/aberdeen/+esm';
29
29
 
30
- const store = new Store({
31
- squares: [],
32
- turn: 'X',
33
- history: [{}],
34
- })
30
+ // Observable data
31
+ const squares = new Store([]) // eg. ['X', undefined, 'O', 'X']
32
+ const history = new Store([[]]) // eg. [[], [undefined, undefined, undefined, X], ...]
33
+ const historyPos = new Store(null) // set while 'time traveling' our undo history
34
+
35
+ // Derived data
36
+ const winner = squares.derive(calculateWinner) // 'X', 'O' or undefined
37
+ const player = squares.derive(sq => sq.filter(v => v).length % 2 ? "O" : "X") // 'X' or 'O'
38
+
39
+ // Rendering functions
35
40
 
36
- const drawSquare = (position) => {
37
- node('button.square', () => {
38
- let value = store.get('squares', position)
39
- if (value) text(value)
40
- else prop('click', () => fillSquare(position))
41
+ function drawSquare(position) {
42
+ $('button.square', () => {
43
+ let value = squares(position).get()
44
+ if (value) $({text: value})
45
+ else $({click: () => fillSquare(position)})
41
46
  })
42
47
  }
43
48
 
44
- const drawBoard = () => {
49
+ function drawBoard() {
45
50
  for(let y=0; y<3; y++) {
46
- node('div.board-row', () => {
51
+ $('div.board-row', () => {
47
52
  for(let x=0; x<3; x++) {
48
53
  drawSquare(y*3 + x)
49
54
  }
@@ -51,52 +56,48 @@ const drawBoard = () => {
51
56
  }
52
57
  }
53
58
 
54
- const drawInfo = () => {
55
- node('div', () => {
56
- let winner = calculateWinner(store.get('squares'))
57
- if (winner) {
58
- text(`Winner: ${winner}`)
59
+ function drawInfo() {
60
+ $('div', () => {
61
+ if (winner.get()) {
62
+ $({text: `Winner: ${winner.get()}!`})
59
63
  } else {
60
- text(`Next player: ${store.get('turn')}`)
64
+ $({text: `Current player: ${player.get()}`})
61
65
  }
62
66
  })
63
- node('ol', () => {
64
- store.onEach('history', item => {
65
- node('li', () => {
66
- node('button', () => {
67
- text(item.index() ? `Go to move ${item.index()}` : `Go to game start`)
68
- prop('click', () => {
69
- store.set('historyPos', item.index())
70
- store.set('squares', item.get())
71
- })
72
- })
67
+ $('.buttons', () => {
68
+ history.onEach(item => {
69
+ $('button', {
70
+ text: item.index() ? `Go to move ${item.index()}` : `Go to game start`,
71
+ click: () => {
72
+ historyPos.set(item.index())
73
+ squares.set(item.get())
74
+ },
73
75
  })
74
76
  })
75
77
  })
76
78
  }
77
79
 
78
- const fillSquare = (position) => {
80
+ // Helper functions
81
+
82
+ function fillSquare(position) {
79
83
  // If there's already a winner, don't allow a new square to be filled
80
- if (calculateWinner(store.get('squares'))) return
84
+ if (winner.peek()) return
81
85
 
82
86
  // Fill the square
83
- store.set('squares', position, store.get('turn'))
84
-
85
- // Next player's turn
86
- store.set('turn', store.get('turn')==='X' ? 'O' : 'X')
87
+ squares(position).set(player.get())
87
88
 
88
- if (store.get('historyPos') != null) {
89
+ if (historyPos.get() != null) {
89
90
  // Truncate everything after history pos
90
- store.set('history', store.get('history').slice(0,store.get('historyPos')+1))
91
+ history.modify(h => h.slice(0, historyPos.get()+1))
91
92
  // Stop 'time traveling'
92
- store.delete('historyPos')
93
+ historyPos.set(null)
93
94
  }
94
95
 
95
96
  // Append the current squares-state to the history array
96
- store.push('history', store.get('squares'))
97
+ history.push(squares.get())
97
98
  }
98
99
 
99
- const calculateWinner = (squares) => {
100
+ function calculateWinner(squares) {
100
101
  const lines = [
101
102
  [0, 1, 2], [3, 4, 5], [6, 7, 8], // horizontal
102
103
  [0, 3, 6], [1, 4, 7], [2, 5, 8], // vertical
@@ -108,11 +109,13 @@ const calculateWinner = (squares) => {
108
109
  }
109
110
  }
110
111
  }
112
+
113
+ // Fire it up!
111
114
 
112
115
  mount(document.body, () => {
113
- node('div.game', () => {
114
- node('div.game-board', drawBoard)
115
- node('div.game-info', drawInfo)
116
+ $('.game', () => {
117
+ $('.game-board', drawBoard)
118
+ $('.game-info', drawInfo)
116
119
  })
117
120
  })
118
121
  ```