-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathusage-in-browser.html
227 lines (181 loc) · 6.74 KB
/
usage-in-browser.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Component thing demo</title>
</head>
<body>
<div class="main">
<p class="container" id="a-unique-string">You component will end up in here!</p>
</div>
<!-- required -->
<script src="../dist/component.min.js"></script>
<!-- optional -->
<script src="../dist/tweenState.min.js"></script>
<script>
// if using tweenState, you need to set it up:
Component.tweenState = tweenState
// ------------------------------------------------
// USAGE: Defining components
// ------------------------------------------------
// Define our app state
var state = {
count: 0,
x: 0,
incrementBy: 1,
id: "foo-id",
items: [
{ name: "One" },
{ name: "Two" },
],
btnColor: 'black',
borderColor: 'grey',
text: "Text set by 'App'"
}
//
// Define the stateful main component
//
var App = new Component(state);
//
// IMPORTANT - Define a view
//
App.view = (props) => `
<div id="myapp">
${Heading('Count = ' + props.count)}
<div class="child-container">
${SubHeader({ text: props.text })}
</div>
${List(props.items)}
${Button('Add 1', `App.clickBtn(${props.incrementBy})`)}
${Button('Reset counter', `App.resetBtn()`)}
</div>`
//
// OPTIONAL - Define some events
//
App.clickBtn = (props) => App.setState({ count: App.state.count + props})
// tween "count" back down to one
App.resetBtn = props => {
const tween = App.tweenState({ count: 1 },
{
ease: "outQuint",
duration: 1000,
// we want to show an integer on the page, so don't set state on each frame...
shouldSetState: props => false,
// until we convert "count" to an integer
onUpdate: props => App.setState({ count: Number.parseInt(props.values[0], 10) })
});
console.log('tween', tween);
}
//
// OPTIONAL - Define some styles for the view
//
App.style = (props) => `
#myapp {
background-color: #eee;
border: 2px solid ${props.borderColor || 'red'};
margin: 0 auto;
max-width: 360px;
padding: 0 18px 18px 18px;
}
.btn {
background-color: ${props.btnColor || 'red'};
border: 0;
color: white;
padding: 6px;
position: relative;
left: ${props.x}px;
}`
// ------------------------------------------------
// USAGE: Defining sub-components
// ------------------------------------------------
// Define stateless sub-components:
// these are just normal functions that return a string of HTML
var Heading = (text) => `<h1>${text}</h1>`
var List = (items) => `<ul>${items.map(item => `<li>${item.name}</li>`).join('')}</ul>`
var Button = (label, fn) => `<button class="btn" onclick=${fn}>${label}</button>`
// Define a stateful sub component:
// This is a proper component with setState() and render() methods, etc
var SubHeader = new Component({ text: "initial text here" });
SubHeader.view = props => `<h2>${props.text}</h2>`
// ------------------------------------------------
// USAGE: Using the component
// ------------------------------------------------
App.debug = false // disable logging of state changes in console/DevTools
App.debug = true // enable logging of state changes in console/DevTools
App.reactive = false // disable auto re-render on state changes
App.reactive = true // enable auto re-render on state changes
// IMPORTANT: Add our app to the page
App.render('.container')
// Using setState() to trigger a full re-render
App.setState({ items: [ ...App.state.items, { name: "Three" } ] })
// New in v1.2.0
// You can also set state through the components constructor:
App({ items: [ ...App.state.items, { name: "Four" } ] })
// ...Or call any methods you defined directly
App.clickBtn(1)
// ------------------------------------------------
// OPTIONAL: Define chainable "actions", to update the state more easily
// ------------------------------------------------
// Define "actions" to update your state in specific ways
// These "actions" are like regular methods, except
// they're always chainable and tagged by name in
// your components state history
App.actions({
update: (props) => App.setState({ props }), // same as calling App.setState()
reset: (props) => App.setState({ count: 0 }),
count: (props) => App.setState({ count: props }),
plus: (props) => App.setState({ count: App.state.count + props }),
minus: (props) => App.setState({ count: App.state.count - props }),
items: (props) => App.setState({ items: props }),
addItems: (props) => App.setState({ items: [ ...App.state.items, ...props ] }),
animate: (props) => App.setState({ x: App.state.x + 1 }),
});
// If you defined some "actions", you can use them
// to update specific parts of your state
App.plus(99)
// A components "actions" can be chained
App.minus(1)
.minus(1)
.minus(1)
.minus(1)
.minus(1)
.plus(5)
.addItems([ { name: "Five" }, { name: "Six" } ])
// ------------------------------------------------
// OPTIONAL: Using the state "timeline"
// ------------------------------------------------
// Take a "snapshot" (we'll use it later)
var snapshot = App.state
App.rw() // go to initial state
App.ff() // go to latest state
App.rw(2) // rewind two steps to a previous state
App.ff(2) // fast-forward two steps to a more current state
// Set a previous state
App.setState(App.log[0].state)
// Set a "named" state, from a previous point in time
App.setState(snapshot)
// show the history of state changes
console.log('App.log')
console.log(App.log)
// Test debouncing of rendering (animation/render performance):
// - call setState 10,000 times, fast as possible
// - setState calls render() each time
// - render() should only try to access/update DOM at 60fps
App.debug = false
for (var i = 0; i < 10000; i++) {
App.plus(1)
}
// ------------------------------------------------
// OPTIONAL: Using the sub components
// ------------------------------------------------
// We can override the values set by App:
// 1. IMPORTANT: we must first re-render into our container to take control
// of its re-rendering it away from App
// 2. then we can auto re-render changes directly from the sub-component
SubHeader.render('.child-container') // remove this and the line below fail!
SubHeader({ text: "This text is overwritten (by App) when it runs or you click the button" })
// Or, we can make SubHeader match whatever App tries to set (from App.state.text)
SubHeader({ text: App.state.text })
App.setState({ text: "Pointless list:" })
</script>
</body>