Skip to content

Instantly share code, notes, and snippets.

@zerobias
Last active July 1, 2019 20:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zerobias/3f55c88b7f80619190ea1c3cd1bae696 to your computer and use it in GitHub Desktop.
Save zerobias/3f55c88b7f80619190ea1c3cd1bae696 to your computer and use it in GitHub Desktop.
import React from 'react'
import ReactDOM from 'react-dom'
function iterate(unit) {
const source = createEvent()
const begin = createEvent()
const end = createEvent()
const iteration = createEvent()
const item = iteration.map(({value}) => value)
const {emptyList, nonEmptyList} = split(source, {
emptyList: (list) => list.length === 0,
nonEmptyList: (list) => list.length > 0,
})
forward({
from: nonEmptyList.map(list => ({list, index: 0, value: list[0]})),
to: iteration,
})
forward({from: emptyList, to: begin})
forward({from: emptyList, to: end})
forward({from: nonEmptyList, to: begin})
forwardWhen(iteration, iteration, step => {
if (step.index + 1 < step.list.length) {
step.index += 1
step.value = step.list[step.index]
return step
}
})
forwardWhen(iteration, end, step => {
if (step.index === step.list.length - 1) {
return step.list
}
})
if (unit) forward({from: unit, to: source})
return {
source,
item,
lifecycle: {
begin,
end,
},
}
}
function forwardWhen(from, to, fn) {
return forward({from: from.filterMap(fn), to})
}
function split(unit, cases) {
const result = {}
let current = is.store(unit) ? unit.updates : unit
for (const key in cases) {
result[key] = current.filter({fn: cases[key]})
current = current.filter({fn: invertCondition(cases[key])})
}
result.__ = current
return result
function invertCondition(fn) {
return (data) => !fn(data)
}
}
const url =
'https://gist.githubusercontent.com/zerobias/' +
'e01f8a66fb11544a4d8db1f2d0d44298/raw/' +
'd157ee3cbb9fa14887c0f83cd96b9de26f5013cf/' +
'button.css'
const fetchRawCSS = createEffect('fetch raw data', {
async handler(params: any) {
const req = await fetch(url)
const result = await req.text()
return result
},
})
const rawLines = fetchRawCSS.done.map(({result}) => result.split(`\n`))
const totalLines = createStore(0).on(rawLines, (_, lines) => lines.length)
const linesIterator = iterate(rawLines)
const lineTypes = split(linesIterator.item, {
empty: line => line === '',
declarationHead: line => /^\./.test(line),
declarationBody: line => /^ [a-zA-Z\-]+/.test(line),
declarationBodyClose: line => /^\}/.test(line),
commentInline: line => /^\/\*.*\*\/$/.test(line),
commentOpen: line => /^\/\*/.test(line),
commentBody: line => /^ +\w+/.test(line),
commentClose: line => /\*\/$/.test(line),
})
const lineTypeHeadGroup = lineTypes.declarationHead.map(head => {
if (head.endsWith(',')) return head.slice(0, -1)
else if (head.endsWith(' {')) return head.slice(0, -2)
return head
})
const lineTypeBodyGroup = lineTypes.declarationBody.map(body => {
const cleanLine = body.slice(2, -1)
const colon = cleanLine.indexOf(':')
return {
property: cleanLine.slice(0, colon),
value: cleanLine.slice(colon + 1),
}
})
const declarations = createStore({
list: [],
current: {
head: [],
body: [],
},
})
.on(lineTypeHeadGroup, (state, head) => {
state.current.head.push(head)
return state
})
.on(lineTypeBodyGroup, (state, body) => {
state.current.body.push(body)
return state
})
.on(lineTypes.declarationBodyClose, state => {
state.list.push({
head: state.current.head,
body: state.current.body,
})
state.current.head = []
state.current.body = []
return state
})
const declarationList = sample({
source: declarations,
clock: linesIterator.lifecycle.end,
target: createStore([]),
fn: ({list}) => list,
})
const declarationListSize = sample({
source: declarations,
clock: linesIterator.lifecycle.end,
target: createStore(0),
fn: ({list}) => list.length,
})
const nextDeclaration = sample({
source: declarations,
clock: lineTypes.declarationBodyClose,
fn: ({list}) => list[list.length - 1],
greedy: true,
})
const easyRegex = /^[a-zA-Z\-.:]+$/
const nextEasyDeclaration = nextDeclaration.map(({head}) => {
return head.filter((row) => easyRegex.test(row))
}).filter({
fn: head => head.length > 0,
})
const easyDeclarations = sample({
source: createStore([])
.on(nextEasyDeclaration, (list, item) => {
list.push(item)
return list
}),
clock: linesIterator.lifecycle.end,
target: createStore([])
})
const lineTypesStats = (() => {
const statsList = []
for (const key in lineTypes) {
const name = key === '__' ? 'other' : key
const store = createStore(0).on(lineTypes[key], (x) => x + 1)
const state = sample(store)
const View = () => <KeyVal.Item name={name} content={state} />
statsList.push(<View key={name} />)
}
return statsList
})()
const Stats = () => (
<KeyVal>
<KeyVal.Item name="declarations" content={declarationListSize} />
<KeyVal.Item name="total lines" content={totalLines} />
{lineTypesStats}
</KeyVal>
)
const LoadCss = () => (
<button onClick={fetchRawCSS} disabled={useStore(fetchRawCSS.pending)}>
<code class="name">&lt;load css&gt;</code>
</button>
)
const SourceFile = () => (
<a href={url} target="_blank" rel="noopener noreferrer">
&lt;view parsed css file&gt;
</a>
)
const Declaration = ({head, body, showBody}) => (
<>
<section class="declaration">
<header class="declaration__header">
<h4>
{head.map((row) => (
<div key={row}>{row}</div>
))}
</h4>
</header>
<footer>
{showBody &&
body.map((row, i) => (
<p key={i}>
<span class="property">{row.property}:</span>
{row.value}
</p>
))}
</footer>
</section>
</>
)
const EasyDeclarations = () =>
useStore(easyDeclarations).map((head, i) => (
<Declaration showBody={false} head={head} body={[]} key={i} />
))
const DeclarationList = () =>
useStore(declarationList).map(({head, body}, i) => (
<Declaration showBody head={head} body={body} key={i} />
))
const App = () => (
<>
<LoadCss />
<SourceFile/>
<Details>
<Details.Head>Stats</Details.Head>
<Stats />
</Details>
<Details open>
<Details.Head>Declaration list</Details.Head>
<DeclarationList />
</Details>
<Details>
<Details.Head>List of declarations with simple syntax</Details.Head>
<EasyDeclarations />
</Details>
</>
)
const theme = {
borderRadius: '0 0 0 6px',
}
styled`
.property {
color: grey;
}
.declaration + .declaration {
margin-top: 1rem;
}
.declaration footer {
border-left: solid #217ac0 1px;
border-bottom: solid #217ac0 1px;
border-radius: ${theme.borderRadius};
margin: -2px 0 0 5px;
}
.declaration p {
line-height: 1.3em;
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 7px;
}
.declaration footer:empty {
visibility: hidden;
}
.declaration p:first-of-type {
padding-top: 5px;
}
.declaration p:last-of-type {
padding-bottom: 4px;
}
.declaration h4 div {
overflow: hidden;
text-overflow: ellipsis;
}
.declaration h4 {
margin: 0;
display: inline-block;
font-family: 'Fira Sans', sans-serif;
background: #217ac0;
padding: 2px 6px 2px 6px;
border-radius: ${theme.borderRadius};
color: white;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.declaration__header {
display: flex;
}
button, a {
--button-color: #217ac0;
padding: 0.5rem 1rem;
}
button[disabled] {
--button-color: #7a7a7a;
}
.name, a {
cursor: pointer;
font-family: monospace;
font-weight: bold;
color: var(--button-color);
font-size: 14px;
text-decoration: none;
}
details {font-family: "Open Sans Light",Helvetica,Arial}
.attributes { margin-left: 22px; font-size: 90% }
.attributes p { margin-left: 16px; font-style: italic }
dd {margin-bottom: 1em;}
`
const KeyVal = ({children}) => <ul>{children}</ul>
KeyVal.Item = ({name, content}) => (
<li>
<span>{name}</span>&nbsp;
<strong>{useStore(content)}</strong>
</li>
)
const Details = ({children, open}) => <details open={open}>{children}</details>
Details.Head = ({children}) => <summary>{children}</summary>
let perfStart
linesIterator.lifecycle.begin.map(() => {
perfStart = performance.now()
})
sample(linesIterator.lifecycle.end).map(list => {
const time = performance.now() - perfStart
const ln = list.length
requestAnimationFrame(() => {
console.log(`
complete iteration over ${ln} items in ${time|0}ms
${(time/ln).toFixed(3)}ms per item
`)
})
})
ReactDOM.render(<App />, document.getElementById('root'))
setTimeout(fetchRawCSS, 2000)
function styled(tags, ...attrs) {
const value = style(tags, ...attrs)
const node = document.createElement('style')
node.id = "insertedStyle"
node.appendChild(document.createTextNode(value))
const sheet = document.getElementById('insertedStyle')
if (sheet) {
sheet.disabled = true;
sheet.parentNode.removeChild(sheet)
}
document.head.appendChild(node)
function style(tags, ...attrs) {
if (tags.length === 0) return ''
let result = ' ' + tags[0]
for (let i = 0; i < attrs.length; i++) {
result += attrs[i]
result += tags[i + 1]
}
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment