aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2021-02-05 19:06:26 +0000
committers-ol <s-ol@users.noreply.github.com>2021-02-05 19:07:31 +0000
commit083c63cec425bb2c8e6f4f9a90c3ddfec2273b36 (patch)
tree4e7b8d21b59ff92a7ee1f2d1a7f3820f151171aa
parentclean up UI code (diff)
downloadfedidag-083c63cec425bb2c8e6f4f9a90c3ddfec2273b36.tar.gz
fedidag-083c63cec425bb2c8e6f4f9a90c3ddfec2273b36.zip
reimplement collapsing
-rw-r--r--package.json1
-rw-r--r--src/index.js110
-rw-r--r--src/ui/Note.js14
-rw-r--r--yarn.lock5
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}
diff --git a/yarn.lock b/yarn.lock
index f3d27aa..95438a9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"