aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui/Note.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/Note.js')
-rw-r--r--src/ui/Note.js155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/ui/Note.js b/src/ui/Note.js
new file mode 100644
index 0000000..b7387a8
--- /dev/null
+++ b/src/ui/Note.js
@@ -0,0 +1,155 @@
+import { render, h, Fragment } from 'preact';
+import { useRef, useLayoutEffect } from 'preact/hooks';
+import ColorHash from 'color-hash';
+import { formatRelative } from 'date-fns';
+import css from './css';
+
+const now = new Date();
+const Time = ({ time }) => {
+ const dt = new Date(time);
+ return formatRelative(dt, now);
+};
+
+css`
+.flex-space {
+ flex: 1;
+ margin: 0;
+}
+`;
+const Space = () => <div class="flex-space" />;
+
+css`
+section > header {
+ display: flex;
+ flex-direction: row;
+
+ height: 1.75rem;
+
+ color: var(--theme-header-fg);
+ background: var(--theme-header-bg);
+
+ cursor: pointer;
+ transition: opacity 0.3s;
+}
+
+section > header:hover {
+ opacity: 0.5;
+}
+
+section > header.small {
+ height: 0.5em;
+}
+
+section > header > * {
+ margin: 0 0.5em;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: 1.75rem;
+}
+
+section > header > img {
+ height: 1.5rem;
+ width: 1.5rem;
+ flex: 0 0 1.5rem;
+ margin: 0.125rem;
+ border-radius: 0.2rem;
+}
+
+section > header > .user {
+ flex: 0 0.5 auto;
+}
+
+section > header > .time {
+ flex: 0 1 auto;
+ font-size: 0.8em;
+ color: #363636;
+}
+`;
+const Header = ({ user, published, onClick }) => {
+ const username = user.name || user.preferredUsername;
+
+ return (
+ <header onClick={onClick}>
+ <img src={new URL(user.icon.url, user.id)} />
+ <span className="user">{username}</span>
+ <Space />
+ <span className="time"><Time time={published} /></span>
+ </header>
+ );
+};
+
+css`
+section {
+ position: absolute;
+ overflow: hidden;
+
+ width: 22em;
+ color: var(--theme-note-fg);
+ background: var(--theme-note-bg);
+ box-shadow: rgba(0,0,0, 0.7) 2px 4px 5px;
+
+ border-radius: 0.4em;
+}
+
+section.type-Tombstone {
+ color: var(--theme-note-bg);
+ background: var(--theme-note-fg);
+}
+
+section > main {
+ padding: 1em;
+ font-family: serif;
+}
+
+section.tombstone > main {
+ font-family: inherit;
+}
+
+section.hidden {
+ display: none;
+}
+
+section.collapsed {
+ width: auto;
+}
+section.collapsed > main {
+ display: none;
+}
+`;
+
+const colors = new ColorHash({ lightness: 0.8 });
+export const Note = ({
+ id, type, attributedTo, published, content,
+ smallHeader = false, position,
+ onHide, onSize,
+}) => {
+ const backgroundColor = attributedTo.color || colors.hex(attributedTo.id);
+
+ const root = useRef(null);
+ useLayoutEffect(() => {
+ onSize(id, root.current.offsetWidth, root.current.offsetHeight);
+ });
+
+ return (
+ <section
+ style={{
+ '--theme-header-bg': backgroundColor,
+ 'left': position && `${position[0] - root.current.offsetWidth / 2}px`,
+ 'top': position && `${position[1] - root.current.offsetHeight / 2}px`,
+ 'visibility': position ? 'visible' : 'hidden',
+ }}
+ className={`type-${type}`}
+ data-id={id}
+ ref={root}
+ >
+ {smallHeader
+ ? <header className="small" onClick={onHide} />
+ : <Header onClick={onHide} user={attributedTo} published={published} />
+ }
+ <main>
+ {content}
+ </main>
+ </section>
+ );
+};