diff options
Diffstat (limited to 'src/ui/Note.js')
| -rw-r--r-- | src/ui/Note.js | 155 |
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> + ); +}; |
