-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
123 lines (102 loc) · 3.51 KB
/
index.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
<script type="module">
// basic Preact API
import { h, render } from 'https://unpkg.com/preact@latest?module';
// Preact hooks
import { useState, useEffect } from 'https://unpkg.com/preact@latest/hooks/dist/hooks.module.js?module';
// tagged string templates
import htm from "https://unpkg.com/htm@latest/dist/htm.module.js?module";
// initialize htm with Preact's h(yperhtml) function ~ React's createElement
const html = htm.bind(h);
// constants
const ONE_SECOND = 1000; // milliseconds
// helper functions
const formattedTimeNow = () => new Date().toLocaleTimeString();
// components:
// Shadow Root component, modified from https://github.com/developit/preact-shadow-root/blob/master/preact-shadow-root.js,
// using issue #6 and PR #7 fixes
class Shadow {
shouldComponentUpdate(nextProps) {
this.update(nextProps);
return false;
}
componentDidMount() {
let parent = this.__P;
if (parent) {
this.shadow = parent.attachShadow({ mode: 'open' });
this.update(this.props);
}
}
componentWillUnmount() {
this.update(this.props, true);
}
update(props, unrender) {
let child = props.children;
render(unrender ? null : child, this.shadow);
if (unrender) {
this.__P.remove();
}
}
render() {}
}
// use *hooks* to have stateful functional components
const DigitalClock = () => {
// init component-local state
const [time, setTime] = useState(formattedTimeNow());
// set periodic state changer in motion
useEffect(() => {
// effect after every render:
const timer = setInterval( () => setTime(formattedTimeNow()), ONE_SECOND );
// undo effect - after every render *and* on component unmount (which is what we're after here)
return () => clearInterval(timer);
});
// extract time components ...
const [hours, minutes, seconds] = time.split(':');
// ... in order to instantiate formatted time UI
// ... with encapsulated style (note outer <span> styling does not affect inner styling and vice versa!)
const Style = html `<style>
.hours {
color: red;
}
.mins {
color: green;
}
.secs {
color: blue;
}
</style>`;
return html`<p><${Shadow}>${Style}<span class="hours">${hours}</span>:<span class="mins">${minutes}</span>:<span class="secs">${seconds}</span>.</${Shadow}></p>`;
};
// *define* a pure functional component: props |-> virtual DOM:
// we can have variable tag names, too!
const MyComponent = ({name, heading = 'h1'}) => {
const [withClock, setClock] = useState(true);
const handleClick = ({target:{checked}}) => setClock(!checked);
return html`<${heading}>Hello ${name}!</${heading}>
<label>no clock<input type="checkbox" onClick=${handleClick}/></label>
<p class="cl"><span>It is</span></p><${withClock ? DigitalClock : 'br'}/>`;
};
// parametrized styling
const Styling = ({paragraphPadding='0 1em'}) => html`<style>
p {
float: left;
padding: ${paragraphPadding};
}
span {
color: #fff;
background: #000;
}
label {
float:left;
}
.cl {
clear: left;
}
br {
display: none;
}
</style>`;
// *instantiate*
const App = html`<${Styling} paragraphPadding="0 .1em"/><${MyComponent} name=${'world'} heading="h2"/>`;
// render the app under <body>
render(App, document.body);
</script>