Last active
July 20, 2022 19:59
-
-
Save sukima/b08983580c367ef12f369a71533780b3 to your computer and use it in GitHub Desktop.
Statecharts iterative example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Component from '@glimmer/component'; | |
import { action } from '@ember/object'; | |
import { tracked } from '@glimmer/tracking'; | |
import mockFetch from '../utils/mock-fetch'; | |
export default class extends Component { | |
@tracked state = 'idle'; | |
@tracked isLoading = false; | |
@tracked hasSuccess = false; | |
@tracked error = null; | |
@action execute() { | |
if (this.isLoading) { return; } | |
this.isLoading = true; | |
this.updateStyle(); | |
clearTimeout(this.successTimer); | |
mockFetch().then(() => { | |
if (this.isDestroying || this.isDestroyed) { return; } | |
this.isLoading = false; | |
this.hasSuccess = true; | |
this.error = null; | |
this.updateStyle(); | |
this.successTimer = setTimeout(() => { | |
if (this.isDestroying || this.isDestroyed) { return; } | |
this.hasSuccess = false; | |
this.updateStyle(); | |
}, 3000); | |
}).catch(() => { | |
if (this.isDestroying || this.isDestroyed) { return; } | |
this.isLoading = false; | |
this.error = true; | |
this.updateStyle(); | |
}); | |
} | |
updateStyle() { | |
if (this.isLoading) { | |
this.state = 'fetching'; | |
} else if (this.error) { | |
this.state = 'error'; | |
} else if (this.hasSuccess) { | |
this.state = 'success'; | |
} else { | |
this.state = null; | |
} | |
} | |
willDestroy() { | |
super.willDestroy(...arguments); | |
clearTimeout(this.successTimer); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Component from '@glimmer/component'; | |
import { action } from '@ember/object'; | |
export default class extends Component { | |
@action highlightBlock(element) { | |
hljs.highlightBlock(element); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Component from '@glimmer/component'; | |
import { action } from '@ember/object'; | |
import { tracked } from '@glimmer/tracking'; | |
import mockFetch from '../utils/mock-fetch'; | |
function reduce(state, event, send) { | |
function doFetch() { | |
mockFetch() | |
.then(() => send('DONE')) | |
.catch(() => send('ERROR')); | |
} | |
switch (state) { | |
case 'idle': | |
switch (event) { | |
case 'CLICK': | |
doFetch(); | |
return 'fetching'; | |
default: return state; | |
} | |
case 'fetching': | |
switch (event) { | |
case 'DONE': | |
setTimeout(() => send('AFTER_DELAY'), 3000); | |
return 'success'; | |
case 'ERROR': return 'error'; | |
default: return state; | |
} | |
case 'success': | |
switch (event) { | |
case 'AFTER_DELAY': return 'idle'; | |
case 'CLICK': | |
doFetch(); | |
return 'fetching'; | |
default: return state; | |
} | |
case 'error': | |
switch (event) { | |
case 'CLICK': | |
doFetch(); | |
return 'fetching'; | |
default: return state; | |
} | |
} | |
throw new Error(`Unknown state ${state}`); | |
} | |
export default class extends Component { | |
@tracked state = 'idle'; | |
get label() { | |
switch (this.state) { | |
case 'fetching': return 'Loading…'; | |
case 'success': return 'Success ✔︎'; | |
case 'error': return 'There was an error ✘'; | |
default: return 'Click'; | |
} | |
} | |
@action send(event) { | |
if (this.isDestroying || this.isDestroyed) { return; } | |
this.state = reduce(this.state, event, this.send); | |
this.updateLabel(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Component from '@glimmer/component'; | |
import { tracked } from '@glimmer/tracking'; | |
import { action } from '@ember/object'; | |
import mockFetch from '../utils/mock-fetch'; | |
const { Machine, interpret } = XState; | |
const LABELS = { | |
idle: 'Click', | |
fetching: 'Loading…', | |
error: 'There was an error ✘', | |
success: 'Success ✔︎' | |
}; | |
const buttonMachine = Machine({ | |
id: 'button-machine', | |
initial: 'idle', | |
states: { | |
idle: { on: { CLICK: 'fetching' } }, | |
fetching: { | |
invoke: { | |
src: 'doFetch', | |
onDone: 'success', | |
onError: 'error' | |
} | |
}, | |
error: { on: { CLICK: 'fetching' } }, | |
success: { | |
after: { 3000: 'idle' }, | |
on: { CLICK: 'fetching' } | |
} | |
} | |
}); | |
export default class extends Component { | |
@tracked state; | |
machine = interpret(buttonMachine.withConfig({ | |
services: { doFetch: mockFetch } | |
})) | |
.onTransition(state => this.state = state.value) | |
.start(); | |
get label() { | |
return LABELS[this.state]; | |
} | |
willDestroy() { | |
super.willDestroy(...arguments); | |
this.machine.stop(); | |
} | |
@action send() { | |
this.machine.send(...arguments); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import EmberRouter from '@ember/routing/router'; | |
import config from './config/environment'; | |
const Router = EmberRouter.extend({ | |
location: 'none', | |
rootURL: config.rootURL | |
}); | |
Router.map(function() { | |
this.route('diagram'); | |
this.route('booleans'); | |
this.route('reducer'); | |
this.route('xstate'); | |
}); | |
export default Router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Route from '@ember/routing/route'; | |
export default class IndexRoute extends Route { | |
beforeModel() { | |
this.replaceWith('diagram'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: sans-serif; | |
background-color: white; | |
color: black; | |
} | |
.site-nav > ol { | |
display: flex; | |
justify-content: space-between; | |
min-width: 400px; | |
max-width: 600px; | |
} | |
.site-nav a { | |
padding: 10px; | |
background-color: white; | |
color: black; | |
text-decoration: none; | |
border-radius: 4px; | |
transition: background-color linear 0.1s; | |
} | |
.site-nav a:hover, | |
.site-nav a.active { | |
background-color: #e7e7e7; | |
color: black; | |
} | |
.site-nav a.active { | |
text-decoration: underline; | |
} | |
.content { | |
display: flex; | |
min-width: 400px; | |
max-width: 600px; | |
justify-content: space-between; | |
} | |
pre { | |
margin: 0; | |
padding: 0; | |
} | |
pre code { | |
border-radius: 8px; | |
max-width: 450px; | |
overflow: auto; | |
} | |
button { | |
position: relative; | |
background-color: #e7e7e7; | |
border: 2px solid #e7e7e7; | |
color: black; | |
padding: 15px 32px; | |
text-align: center; | |
text-decoration: none; | |
display: inline-block; | |
font-size: 16px; | |
border-radius: 8px; | |
transition-duration: 0.4s; | |
} | |
button[data-state="fetching"] { | |
background-color: #008CBA; | |
border-color: #008CBA; | |
color: white; | |
} | |
button[data-state="success"] { | |
background-color: #4CAF50; | |
border-color: #4CAF50; | |
color: white; | |
} | |
button[data-state="error"] { | |
background-color: #f44336; | |
border-color: #f44336; | |
color: white; | |
} | |
button[data-state="loading"]:hover, | |
button[data-state="success"]:hover, | |
button[data-state="error"]:hover, | |
button:hover { | |
background-color: white; | |
color: black; | |
} | |
button[data-state]:after { | |
content: 'state: ' attr(data-state); | |
position: absolute; | |
top: 100%; | |
left: 0; | |
margin-top: 0.5rem; | |
background-color: black; | |
font-family: monospace; | |
color: white; | |
padding: 0.25rem; | |
border-radius: inherit; | |
white-space: nowrap; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "0.17.1", | |
"EmberENV": { | |
"FEATURES": {}, | |
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false, | |
"_APPLICATION_TEMPLATE_WRAPPER": true, | |
"_JQUERY_INTEGRATION": true | |
}, | |
"options": { | |
"use_pods": false, | |
"enable-testing": false | |
}, | |
"dependencies": { | |
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js", | |
"xstate": "https://unpkg.com/xstate@4/dist/xstate.js", | |
"highlight": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/highlight.min.js", | |
"highlight_css": "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/styles/default.min.css", | |
"ember": "3.18.1", | |
"ember-template-compiler": "3.18.1", | |
"ember-testing": "3.18.1" | |
}, | |
"addons": { | |
"@glimmer/component": "1.0.0", | |
"@ember/render-modifiers": "1.0.2" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default function mockFetch() { | |
return new Promise((resolve, reject) => { | |
setTimeout(() => { | |
if (Math.random() < 0.5) { | |
resolve({ data: 'ok' }); | |
} else { | |
reject(new Error('fake fetch error')); | |
} | |
}, 1000); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment