diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2021-02-05 19:06:26 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2021-02-05 19:07:31 +0000 |
| commit | 083c63cec425bb2c8e6f4f9a90c3ddfec2273b36 (patch) | |
| tree | 4e7b8d21b59ff92a7ee1f2d1a7f3820f151171aa | |
| parent | clean up UI code (diff) | |
| download | fedidag-083c63cec425bb2c8e6f4f9a90c3ddfec2273b36.tar.gz fedidag-083c63cec425bb2c8e6f4f9a90c3ddfec2273b36.zip | |
reimplement collapsing
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | src/index.js | 110 | ||||
| -rw-r--r-- | src/ui/Note.js | 14 | ||||
| -rw-r--r-- | yarn.lock | 5 |
4 files changed, 79 insertions, 51 deletions
diff --git a/package.json b/package.json index d28dd72..4320201 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ ] }, "dependencies": { + "classnames": "^2.2.6", "color-hash": "^1.0.3", "core-js": "^3.8.3", "date-fns": "^2.16.1", diff --git a/src/index.js b/src/index.js index 090b67a..4b46ac7 100644 --- a/src/index.js +++ b/src/index.js @@ -95,8 +95,42 @@ class GraphContainer extends Component { } } -const App = ({ discussion }) => { - const { name, items } = discussion; +class CollapseContainer extends Component { + state = {}; + + toggle = (id) => { + const { items } = this.props; + + const state = Object.assign({}, this.state); + state[id] = state[id] ? false : 'explicit'; + + this.setState(this.update(items[id], state)); + } + + update(node, state) { + const { items } = this.props; + + if (state[node.id] !== 'explicit') { + state[node.id] = + node.inReplyTo.length + && node.inReplyTo.every(p => state[p.id]); + } + + for (const child of node.replies) + this.update(items[child.id], state); + + return state; + } + + render() { + return this.props.render({ + collapsed: this.state, + toggleCollapsed: this.toggle, + }); + } +} + +const App = ({ name, items, collapsed, toggleCollapsed }) => { const [{ width, height, positions, links }, setState] = useState({ width: 0, height: 0, @@ -109,7 +143,7 @@ const App = ({ discussion }) => { useEffect(() => { layout.render().then(setState); - }, [ items ]); + }, [ items, collapsed ]); return ( <article> @@ -118,14 +152,20 @@ const App = ({ discussion }) => { <Links width={width} height={height} links={links} /> <div> {Object.values(items).map((item) => { + const isCollapsed = collapsed[item.id]; + const hidden = + item.inReplyTo.every(p => collapsed[p.id]) + && item.replies.every(c => collapsed[c.id]); + if (hidden) + return; + const onlyParent = item.inReplyTo.length === 1 && items[item.inReplyTo[0].id]; for (const reply of item.replies) { - const to = items[reply.id]; - if (item.collapsed && to.collapsed) + if (isCollapsed && collapsed[reply.id]) continue; - layout.addLink(item.id, to.id); + layout.addLink(item.id, reply.id); } return ( @@ -136,8 +176,9 @@ const App = ({ discussion }) => { && onlyParent.attributedTo === item.attributedTo // posted by same user && onlyParent.published === item.published // in the same second } + collapsed={isCollapsed} position={positions[item.id]} - onHide={hideNote} + onHide={toggleCollapsed} onSize={setNodeSize} /> ); @@ -149,45 +190,20 @@ const App = ({ discussion }) => { }; const graph = document.location.hash ? document.location.hash.substr(1) : 'lib/graph.json'; -const app = <GraphContainer graph={graph} render={discussion => <App discussion={discussion} /> } />; +const app = ( + <GraphContainer graph={graph} render={({ name, items }) => ( + <CollapseContainer + items={items} + render={({ collapsed, toggleCollapsed }) => ( + <App + name={name} + items={items} + collapsed={collapsed} + toggleCollapsed={toggleCollapsed} + /> + )} + /> + )} /> +); render(app, document.body); window.app = app; - -// update collapsed nodes starting with id -// returns list of (potentially) affected nodes -const updateCollapsed = (graph, node, updated=[]) => { - updated.push(node.data.id); - - if (node.collapsed !== 'explicit') { - node.collapsed = - node.data.inReplyTo.length - && node.data.inReplyTo.every(p => graph[p.id].collapsed); - } - - for (const child of node.data.replies) - updateCollapsed(graph, graph[child.id], updated); - - return updated; -}; - -const hideNote = (e) => { - const clicked = graph[e.currentTarget.parentElement.dataset.id]; - clicked.collapsed = clicked.collapsed ? false : 'explicit'; - - for (const id of updateCollapsed(graph, clicked)) { - const node = graph[id]; - - if (node.collapsed) { - const noParents = node.data.inReplyTo.every(p => graph[p.id].collapsed); - const noChildren = node.data.replies.every(c => graph[c.id].collapsed); - node.dom.className = noParents && noChildren ? 'hidden' : 'collapsed'; - } else { - node.dom.className = ''; - } - } - - layoutNodes(graph) - .then(() => clicked.dom.scrollIntoView({ block: 'nearest', inline: 'nearest' })) - .catch(err => console.error(err)); -}; - diff --git a/src/ui/Note.js b/src/ui/Note.js index b7387a8..36691d4 100644 --- a/src/ui/Note.js +++ b/src/ui/Note.js @@ -2,6 +2,7 @@ import { render, h, Fragment } from 'preact'; import { useRef, useLayoutEffect } from 'preact/hooks'; import ColorHash from 'color-hash'; import { formatRelative } from 'date-fns'; +import cn from 'classnames'; import css from './css'; const now = new Date(); @@ -121,7 +122,7 @@ section.collapsed > main { const colors = new ColorHash({ lightness: 0.8 }); export const Note = ({ id, type, attributedTo, published, content, - smallHeader = false, position, + smallHeader = false, collapsed = false, position, onHide, onSize, }) => { const backgroundColor = attributedTo.color || colors.hex(attributedTo.id); @@ -131,6 +132,11 @@ export const Note = ({ onSize(id, root.current.offsetWidth, root.current.offsetHeight); }); + const onClickHeader = (e) => { + e.preventDefault(); + onHide(id); + }; + return ( <section style={{ @@ -139,13 +145,13 @@ export const Note = ({ 'top': position && `${position[1] - root.current.offsetHeight / 2}px`, 'visibility': position ? 'visible' : 'hidden', }} - className={`type-${type}`} + class={cn(`type-${type}`, collapsed && 'collapsed')} data-id={id} ref={root} > {smallHeader - ? <header className="small" onClick={onHide} /> - : <Header onClick={onHide} user={attributedTo} published={published} /> + ? <header className="small" onClick={onClickHeader} /> + : <Header onClick={onClickHeader} user={attributedTo} published={published} /> } <main> {content} @@ -1527,6 +1527,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + clean-css@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" |
