176 lines
4.1 KiB
JavaScript
176 lines
4.1 KiB
JavaScript
import React, { Component } from 'react';
|
|
import styled from 'styled-components';
|
|
import is from 'styled-is';
|
|
import remcalc from 'remcalc';
|
|
import { Margin } from 'styled-components-spacing';
|
|
|
|
const Wrapper = styled.section``;
|
|
|
|
const Tabs = styled.ul`
|
|
list-style: none;
|
|
display: flex;
|
|
padding: 0;
|
|
align-items: center;
|
|
border-bottom: ${remcalc(1)} solid ${props => props.theme.textDisabled};
|
|
`;
|
|
|
|
const TabHeader = styled.li`
|
|
position: relative;
|
|
height: ${remcalc(30)};
|
|
box-sizing: border-box;
|
|
line-height: inherit;
|
|
|
|
${is('active')`
|
|
border-bottom: ${remcalc(1)} solid ${props => props.theme.primary};
|
|
`};
|
|
|
|
li:hover {
|
|
border-bottom: ${remcalc(1)} solid ${props => props.theme.primary};
|
|
}
|
|
`;
|
|
|
|
const Button = styled.button`
|
|
font-family: 'Libre Franklin';
|
|
text-transform: uppercase;
|
|
font-size: ${remcalc(15)};
|
|
color: ${props => props.theme.textDisabled};
|
|
border: none;
|
|
background: ${props => props.theme.white};
|
|
transition: color 150ms ease-in;
|
|
cursor: pointer;
|
|
text-transform: none;
|
|
padding: 0;
|
|
|
|
${is('active')`
|
|
color: ${props => props.theme.primary};
|
|
font-weight: bold;
|
|
`};
|
|
|
|
&:hover {
|
|
color: ${props => props.theme.primary};
|
|
}
|
|
`;
|
|
|
|
const Tab = styled.div`
|
|
height: 0;
|
|
opacity: 0;
|
|
overflow: hidden;
|
|
transition: opacity 150ms ease-in;
|
|
|
|
${is('active')`
|
|
height: auto;
|
|
opacity: 1;
|
|
transition-delay: 150ms;
|
|
transition: opacity 150ms ease-in;
|
|
`};
|
|
|
|
.CodeMirror-wrap {
|
|
border: none;
|
|
padding: 0;
|
|
}
|
|
`;
|
|
|
|
class Playground extends Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
tab: 'component',
|
|
states: []
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
const code = this.props.preview.props.code;
|
|
const regex = /\/\/ Tab: \w+/g;
|
|
let m;
|
|
while ((m = regex.exec(code)) !== null) {
|
|
// This is necessary to avoid infinite loops with zero-width matches
|
|
if (m.index === regex.lastIndex) {
|
|
regex.lastIndex++;
|
|
}
|
|
|
|
m.forEach((match, groupIndex) => {
|
|
// MAKE THIS SYNCRONOUS
|
|
window.setTimeout(() =>
|
|
this.setState({
|
|
states: [...this.state.states, match.split(':')[1].trim()]
|
|
})
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
changeTab = tab => {
|
|
this.setState({
|
|
tab
|
|
});
|
|
};
|
|
|
|
showPreview = (preview, code) => ({
|
|
...preview,
|
|
props: {
|
|
...preview.props,
|
|
code
|
|
}
|
|
});
|
|
|
|
render() {
|
|
const { name, preview, tabBody } = this.props;
|
|
const { tab, states } = this.state;
|
|
const propCode = this.props.preview.props.code;
|
|
const regex = /\/\/ Tab: \w+/g;
|
|
const nameRegex = /\/\/ Name: /g;
|
|
|
|
return (
|
|
<Wrapper>
|
|
<Tabs>
|
|
<Margin right={5}>
|
|
<TabHeader active={tab === 'component'}>
|
|
<Button
|
|
active={tab === 'component'}
|
|
onClick={() => this.changeTab('component')}
|
|
>
|
|
{(propCode.split(nameRegex)[1] || 'Component').split(/\s/g)[0]}
|
|
</Button>
|
|
</TabHeader>
|
|
</Margin>
|
|
{states.length
|
|
? states.map((state, i) => (
|
|
<TabHeader active={tab === state} key={`tabHeader${i}`}>
|
|
<Button
|
|
active={tab === state}
|
|
onClick={() => this.changeTab(state)}
|
|
>
|
|
{state}
|
|
</Button>
|
|
</TabHeader>
|
|
))
|
|
: null}
|
|
<TabHeader active={tab === 'code'}>
|
|
<Button
|
|
active={tab === 'code'}
|
|
onClick={() => this.changeTab('code')}
|
|
>
|
|
Code
|
|
</Button>
|
|
</TabHeader>
|
|
</Tabs>
|
|
<Tab active={tab === 'component'} data-preview={name}>
|
|
{this.showPreview(preview, propCode.split(regex)[0])}
|
|
</Tab>
|
|
{states.length
|
|
? states.map((state, i) => (
|
|
<Tab active={tab === state} data-preview={name} key={`tab${i}`}>
|
|
{this.showPreview(preview, propCode.split(regex)[i + 1])}
|
|
</Tab>
|
|
))
|
|
: null}
|
|
<Tab active={tab === 'code'}>{tabBody}</Tab>
|
|
</Wrapper>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default Playground;
|