<body>
<link href="https://fonts.googleapis.com/css?family=Antic+Slab" rel="stylesheet">
<style>
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
font-family: 'Antic Slab', serif;
}
hr {
width: 100%;
}
/*
toggle styles copied and modified from
https://codepen.io/mallendeo/pen/eLIiG
by Mauricio Allende (https://mallendeo.com/)
*/
.toggle-btn {
display: inline-block;
outline: 0;
width: 4em;
height: 2em;
position: relative;
cursor: pointer;
user-select: none;
background: #fbfbfb;
border-radius: 2em;
padding: 2px;
transition: all 0.4s ease;
border: 1px solid #e8eae9;
}
.toggle-btn:focus::after,
.toggle-btn:active::after {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1),
0 4px 0 rgba(0, 0, 0, 0.08),
inset 0px 0px 0px 1.5px #9c9c9c;
}
.toggle-btn::after {
left: 0;
position: relative;
display: block;
content: '';
width: 50%;
height: 100%;
border-radius: 2em;
background: #fbfbfb;
transition: all 0.3s
cubic-bezier(0.175, 0.885, 0.32, 1.275),
padding 0.3s ease, margin 0.3s ease;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1),
0 4px 0 rgba(0, 0, 0, 0.08);
}
.toggle-btn.toggle-btn-on::after {
left: 50%;
}
.toggle-btn.toggle-btn-on {
background: #86d993;
}
.toggle-btn.toggle-btn-on:active {
box-shadow: none;
}
.toggle-btn.toggle-btn-on:active::after {
margin-left: -0.8em;
}
.toggle-btn:active::after {
padding-right: 0.8em;
}
.toggle-input {
display: none;
}
</style>
<script src="https://unpkg.com/react@16.0.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.0.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/prop-types@15.6.0/prop-types.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
<script src="https://cdn.rawgit.com/kentcdodds/ea2fdfc82f79228a6be641c78dc0e1b9/raw/0f792eb8dfb3bf993bdf50c9656f12090ed344e6/hoist-non-react-statics.umd.js"></script>
<div id="βοΈ"></div>
<script type="text/babel">
const compose = (...fns) => (...args) =>
fns.forEach(fn => fn && fn(...args))
class Toggle extends React.Component {
static defaultProps = {
defaultOn: false,
onToggle: () => {},
onReset: () => {},
}
initialState = {on: this.props.defaultOn}
state = this.initialState
reset = () => {
if (this.isOnControlled()) {
this.props.onReset(!this.props.on)
} else {
this.setState(this.initialState, () =>
this.props.onReset(this.state.on),
)
}
}
toggle = () => {
if (this.isOnControlled()) {
this.props.onToggle(!this.props.on)
} else {
this.setState(
({on}) => ({on: !on}),
() => this.props.onToggle(this.state.on),
)
}
}
getTogglerProps = (
{onClick, ...props} = {},
) => ({
onClick: compose(onClick, this.toggle),
'aria-expanded': this.state.on,
...props,
})
isOnControlled() {
return this.props.on !== undefined
}
render() {
return this.props.render({
on: this.isOnControlled()
? this.props.on
: this.state.on,
toggle: this.toggle,
reset: this.reset,
getTogglerProps: this.getTogglerProps,
})
}
}
class ToggleProvider extends React.Component {
static contextName = '__toggle__'
static Renderer = class extends React.Component {
static childContextTypes = {
[ToggleProvider.contextName]:
PropTypes.object.isRequired,
}
getChildContext() {
return {
[ToggleProvider.contextName]: this.props
.toggle,
}
}
render() {
return this.props.children
}
}
render() {
const {
children,
...remainingProps
} = this.props
return (
<Toggle
{...remainingProps}
render={toggle => (
<ToggleProvider.Renderer
toggle={toggle}
children={children}
/>
)}
/>
)
}
}
function ConnectedToggle(props, context) {
return props.render(
context[ToggleProvider.contextName],
)
}
ConnectedToggle.contextTypes = {
[ToggleProvider.contextName]:
PropTypes.object.isRequired,
}
function withToggle(Component) {
function Wrapper(props, context) {
const {innerRef, ...remainingProps} = props
return (
<ConnectedToggle
render={toggle => (
<Component
{...remainingProps}
toggle={toggle}
ref={innerRef}
/>
)}
/>
)
}
Wrapper.displayName = `withToggle(${Component.displayName ||
Component.name})`
Wrapper.propTypes = {innerRef: PropTypes.func}
Wrapper.WrappedComponent = Component
return hoistNonReactStatics(Wrapper, Component)
}
const Subtitle = withToggle(
({toggle}) =>
toggle.on
? 'π©βπ« π πΆ'
: 'Teachers are awesome',
)
function App() {
return (
<ToggleProvider>
<div>
<Header />
<Post />
</div>
</ToggleProvider>
)
}
/*
*
*
* Below here are irrelevant
* implementation details...
*
*
*/
function Nav() {
return (
<ConnectedToggle
render={toggle => (
<nav style={{flex: 1}}>
<ul
style={{
display: 'flex',
justifyContent: 'space-around',
listStyle: 'none',
paddingLeft: '0',
}}
>
<li>
<a href="index.html">
{toggle.on ? 'π‘' : 'Home'}
</a>
</li>
<li>
<a href="/about/">
{toggle.on ? 'β' : 'About'}
</a>
</li>
<li>
<a href="/blog/">
{toggle.on ? 'π' : 'Blog'}
</a>
</li>
</ul>
</nav>
)}
/>
)
}
function NavSwitch() {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
textAlign: 'center',
}}
>
<div>
<ConnectedToggle
render={toggle =>
toggle.on ? 'π¦' : 'Enable Emoji'}
/>
</div>
<ConnectedToggle
render={toggle => (
<Switch
{...toggle.getTogglerProps({
on: toggle.on,
})}
/>
)}
/>
</div>
)
}
function Header() {
return (
<div>
<div
style={{
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
}}
>
<Nav />
<NavSwitch />
</div>
</div>
)
}
function Title() {
return (
<div>
<h1>
<ConnectedToggle
render={toggle =>
`Who is ${toggle.on
? 'πΆβ'
: 'awesome?'}`}
/>
</h1>
<Subtitle />
</div>
)
}
function Article() {
return (
<div>
<ConnectedToggle
render={toggle =>
[
'Once, I was in',
toggle.on ? 'π«β' : 'school',
'when I',
toggle.on ? 'π€' : 'realized',
'something...',
].join(' ')}
/>
<hr />
<ConnectedToggle
render={toggle =>
[
'Without',
toggle.on ? 'π©βπ«' : 'teachers',
`I wouldn't know anything so`,
toggle.on ? 'π' : 'thanks',
toggle.on ? 'π©βπ«βοΈ' : 'teachers!',
].join(' ')}
/>
</div>
)
}
function Post() {
return (
<div>
<Title />
<Article />
</div>
)
}
function Switch({on, className = '', ...props}) {
return (
<div className="toggle">
<input
className="toggle-input"
type="checkbox"
/>
<button
className={`${className} toggle-btn ${on
? 'toggle-btn-on'
: 'toggle-btn-off'}`}
{...props}
/>
</div>
)
}
ReactDOM.render(
<div
style={{
marginTop: 40,
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
textAlign: 'center',
}}
>
<App />
</div>,
document.getElementById('βοΈ'),
)
</script>
</body>