diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/dom.js | 48 | ||||
| -rw-r--r-- | src/index.js | 86 | ||||
| -rw-r--r-- | src/ui.js | 89 |
3 files changed, 154 insertions, 69 deletions
diff --git a/src/dom.js b/src/dom.js new file mode 100644 index 0000000..9e44d0a --- /dev/null +++ b/src/dom.js @@ -0,0 +1,48 @@ +export const css = ([ code] ) => { + const style = document.createElement('style'); + document.head.appendChild(style); + style.appendChild(document.createTextNode('')); + + let i = 0; + for (const v of code.split('}')) { + if (v.indexOf('{') < 0) continue; + style.sheet.insertRule(v + '}', i++); + } + + return style; +}; + +const appendChild = (parent, child) => { + if (Array.isArray(child)) { + child.forEach((nestedChild) => appendChild(parent, nestedChild)); + } else { + parent.appendChild(child.nodeType ? child : document.createTextNode(child)); + } +} + +const objectProps = { style: true, dataset: true }; + +export const createElement = (tag, props, ...children) => { + if ('function' === typeof tag) + return tag(props, children); + + const element = document.createElement(tag); + + for (const [name, value] of Object.entries(props || {})) { + if (name.startsWith('on') && name.toLowerCase() in window) { + element.addEventListener(name.toLowerCase().substr(2), value); + } else if (objectProps[name]) { + Object.assign(element[name], value); + } else { + element.setAttribute(name, value.toString()); + } + } + + for (const child of children) { + appendChild(element, child); + } + + return element; +} + +export const createFragment = (props, ...children) => children; diff --git a/src/index.js b/src/index.js index 254821a..187464b 100644 --- a/src/index.js +++ b/src/index.js @@ -2,49 +2,15 @@ import 'core-js/stable'; import 'regenerator-runtime/runtime'; import * as jsonld from 'jsonld'; import dagre from 'dagre'; -import ColorHash from 'color-hash'; +import * as dom from './dom'; +import { Note } from './ui'; -const css = ([ code] ) => { - const style = document.createElement('style'); - document.head.appendChild(style); - style.appendChild(document.createTextNode('')); - - let i = 0; - for (const v of code.split('}')) { - if (v.indexOf('{') < 0) continue; - style.sheet.insertRule(v + '}', i++); - } - - return style; -}; - -css` +dom.css` article { position: relative; } - -section { - position: absolute; - overflow: hidden; - - width: 350px; - - background: #696969; - border-radius: 8px; -} - -section > header { - padding: 0.75em 1em 0.5em; - color: #121212; -} - -section > main { - padding: 1em; - color: #eeeeee; -} `; -const colors = new ColorHash({ lightness: 0.8 }); const context = 'https://www.w3.org/ns/activitystreams'; const userCache = {}; @@ -54,6 +20,7 @@ const loadUser = async (id) => { const result = await jsonld.compact(id, context); userCache[id] = result; + result.icon.url = new URL(result.icon.url, id).href; return result; } @@ -71,31 +38,6 @@ const loadData = async () => { const article = document.createElement('article'); document.body.appendChild(article); -const createNode = (data) => { - const root = document.createElement('section'); - root.dataset.dagId = data.id; - - const header = document.createElement('header'); - header.style.backgroundColor = data.attributedTo.color || colors.hex(data.attributedTo.id); - const author = document.createElement('span'); - author.textContent = data.attributedTo.name || data.attributedTo.preferredUsername; - header.appendChild(author); - root.appendChild(header); - - const main = document.createElement('main'); - main.innerHTML = data.content; - root.appendChild(main); - - article.appendChild(root); - - return { - data, - root, - width: root.offsetWidth, - height: root.offsetHeight, - }; -}; - loadData().then(data => { window.dagData = data; const graph = new dagre.graphlib.Graph(); @@ -107,14 +49,20 @@ loadData().then(data => { graph.setGraph({}); graph.setDefaultEdgeLabel(() => ({})); - let rootNode; + let rootId; for (const data of data['@graph']) { - const node = createNode(data); - graph.setNode(data.id, node); + const dom = <Note {...data} />; + article.appendChild(dom); + + graph.setNode(data.id, { + dom, data, + width: dom.offsetWidth, + height: dom.offsetHeight, + }); if (!data.inReplyTo || !data.inReplyTo.length) - rootNode = node; + rootId = data.id; if (data.replies && !Array.isArray(data.replies)) data.replies = [ data.replies ]; @@ -124,7 +72,6 @@ loadData().then(data => { } } - console.log(rootNode); dagre.layout(graph); const info = graph.graph(); @@ -133,8 +80,8 @@ loadData().then(data => { for (const id of graph.nodes()) { const node = graph.node(id); - node.root.style.left = `${node.x - node.width / 2}px`; - node.root.style.top = `${node.y - node.height / 2}px`; + node.dom.style.left = `${node.x - node.width / 2}px`; + node.dom.style.top = `${node.y - node.height / 2}px`; } const ctx = canvas.getContext('2d'); @@ -153,5 +100,6 @@ loadData().then(data => { ctx.stroke(); } + const rootNode = graph.node(rootId); window.scrollTo(rootNode.x - window.innerWidth/2, rootNode.y); }); diff --git a/src/ui.js b/src/ui.js new file mode 100644 index 0000000..10755e6 --- /dev/null +++ b/src/ui.js @@ -0,0 +1,89 @@ +import ColorHash from 'color-hash'; +import { formatRelative } from 'date-fns'; +import * as dom from './dom'; + +dom.css` +body { + background: #1e1e1e; + font-family: sans-serif; +} +`; + +const now = new Date(); +const Time = ({ time }) => { + const dt = new Date(time); + return formatRelative(dt, now); +}; + +dom.css` +.flex-space { + flex: 1; +} +`; + +const Space = () => <div class="flex-space" />; + +dom.css` +section { + position: absolute; + overflow: hidden; + + width: 350px; + + background: #363636; + border-radius: 0.4em; +} + +section > header { + display: flex; + flex-direction: row; + + height: 2.5em; + line-height: 2.5em; + + color: #121212; +} + +section > header > * { + margin: 0 0.5em; +} + +section > header > img { + height: 2em; + width: 2em; + margin: 0.25em; + border-radius: 0.2em; +} + +section > header > .time { + font-size: 0.8em; + color: #363636; +} + +section > main { + padding: 1em; + color: #eeeeee; + font-family: serif; +} +`; + +const colors = new ColorHash({ lightness: 0.8 }); +export const Note = ({ id, attributedTo, published, content }) => { + const backgroundColor = attributedTo.color || colors.hex(attributedTo.id); + const username = attributedTo.name || attributedTo.preferredUsername; + + return ( + <section dataset={{ id }}> + <header style={{ backgroundColor }}> + <img src={attributedTo.icon.url} /> + <span class="user">{username}</span> + <Space /> + <span class="time"><Time time={published} /></span> + </header> + <main> + {content} + </main> + </section> + ); +}; + |
