import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import { TransitionGroup, CSSTransition } from 'preact-transitioning'
import cn from 'classnames';
import ColorHash from 'color-hash';
import { Note } from './ui/Note.js';
import css from './ui/css';
import './ui/theme';
const colors = new ColorHash({ lightness: 0.8 });
css`
.panel-enter {
opacity: 0;
}
.panel-enter-active {
opacity: 1;
transition: opacity 500ms;
}
.panel-exit {
opacity: 1;
}
.panel-exit-active {
opacity: 0;
transition: opacity 500ms;
}
`;
css`
.page-enter {
position: absolute;
transform: translateX(100vw);
}
.page-enter-active {
transform: none;
transition: transform 500ms;
}
.page-exit {
transform: none;
}
.page-exit-active {
transform: translateX(100vw);
transition: transform 500ms;
}
.panel-up-enter,
.panel-down-enter,
.panel-up-appear,
.panel-down-appear {
height: 0;
opacity: 0;
/* transform set by JS */;
}
.panel-up-enter-active,
.panel-down-enter-active,
.panel-up-appear-active,
.panel-down-appear-active {
/* height set by JS */;
opacity: 1;
transform: none !important;
transition: all 500ms;
}
.panel-up-exit,
.panel-down-exit {
/* height set by JS */;
opacity: 1;
transform: none;
}
.panel-up-exit-active,
.panel-down-exit-active {
height: 0 !important;
opacity: 0;
/* transform set by JS */;
transition: all 500ms;
}
`;
const transitionHooks = {
'down': {
onEnter: (node) => { node.base.style.transform = `translateY(-${node.base.scrollHeight}px)`; },
onEntering: (node) => { node.base.style.height = `${node.base.scrollHeight}px`; },
onEntered: (node) => {
node.base.style.height = '';
node.base.style.transform = '';
},
onExit: (node) => {
node.base.style.height = `${node.base.scrollHeight}px`;
node.clientHeight
},
onExiting: (node) => {
node.base.style.transform = `translateY(-${node.base.scrollHeight}px)`;
},
},
'up': {
onEnter: (node) => { node.base.style.transform = `translateY(${node.base.scrollHeight}px)`; },
onEntering: (node) => { node.base.style.height = `${node.base.scrollHeight}px`; },
onEntered: (node) => {
node.base.style.height = '';
node.base.style.transform = '';
},
onExit: (node) => {
node.base.style.height = `${node.base.scrollHeight}px`;
node.clientHeight
},
onExiting: (node) => {
node.base.style.transform = `translateY(${node.base.scrollHeight}px)`;
},
},
'this': {},
};
css`
.panel {
display: flex;
flex-direction: column;
}
`;
const Panel = ({ id, focusDir, activePages, setActivePages, setFocus, groups, className }) => (
groups[pid])}
active={activePages.up}
focused={focusDir === 'this'}
attached={focusDir === 'up'}
setActivePage={(newId) => setActivePages({ ...activePages, up: newId })}
/>
groups[pid])}
active={activePages.down}
focused={focusDir === 'this'}
attached={focusDir === 'down'}
setActivePage={(newId) => setActivePages({ ...activePages, down: newId })}
/>
);
css`
.group {
display: flex;
flex-direction: column;
position: relative;
gap: 0.3rem;
z-index: 0;
}
.group::before {
position: absolute;
content: '';
inset: -0.75rem;
border-radius: 0.5rem;
background: transparent;
z-index: -1;
box-shadow: transparent 0 0 4px;
transition: all 500ms;
}
.panel-focused .group::before {
box-shadow: rgba(0,0,0, 0.3) 0 0 4px;
background: var(--theme-marker);
}
`;
const Group = ({ group: { id, items }, onClick }) => (
{ e.preventDefault(); onClick(); }) : null}
style={{ cursor: onClick && 'pointer' }}
>
{items.map(item => )}
);
css`
.page-indicator {
--link-len: 0.75rem;
--link-len-attached: 0.5rem;
font-size: 0.6rem;
font-weight: bold;
z-index: 1;
}
.panel-focused .page-indicator {
font-size: 0.75rem;
}
.page-indicator-up {
padding-bottom: var(--link-len);
}
.page-indicator-down {
padding-top: var(--link-len);
}
.page-indicator-up.page-indicator-attached {
padding-top: var(--link-len-attached);
}
.page-indicator-down.page-indicator-attached {
padding-bottom: var(--link-len-attached);
}
.page-indicator ul {
position: relative;
list-style: none;
padding: 0;
margin: 0;
height: 2em;
transition: all 500ms;
}
.page-indicator li {
position: absolute;
--offset: 0;
width: 2em;
height: 2em;
line-height: 2em;
z-index: 0;
left: 50%;
transform: translateX(calc(3em * var(--offset) - 50%));
transition: all 500ms;
}
.page-indicator button {
position: absolute;
width: 100%;
height: 100%;
box-sizing: border-box;
border: 0.3em solid var(--theme-link);
border-radius: 0.6em;
background: var(--link-color);
cursor: pointer;
font-size: inherit;
font-weight: inherit;
z-index: 0;
}
.page-indicator li.active button {
transition: transform 500ms, opacity 350ms 150ms;
}
.page-indicator-attached li.active button {
opacity: 0;
pointer-events: none;
transform: translateY(calc(1.5em + var(--link-len-attached)));
}
.page-indicator-up.page-indicator-attached li.active button {
transform: translateY(calc((1.5em + var(--link-len-attached)) * -1));
}
.page-indicator-up li::after,
.page-indicator-down li::before {
position: absolute;
content: '';
width: 2px;
background: var(--theme-link);
pointer-events: none;
left: 0;
right: 0;
margin: auto;
top: calc(var(--link-len) * -1);
bottom: 0;
z-index: -10;
}
.page-indicator-up li::after {
bottom: calc(var(--link-len) * -1);
top: 0;
}
.page-indicator-down.page-indicator-attached li.active::before {
bottom: calc(var(--link-len-attached) * -1);
}
.page-indicator-up.page-indicator-attached li.active::after {
top: calc(var(--link-len-attached) * -1);
}
.page-indicator li.active::after,
.page-indicator li.active::before {
width: 4px;
}
`;
const PageIndicator = ({ pages, dir, focused, active, attached, setActivePage }) => {
const activeIndex = pages.findIndex(g => g.id === active);
return (
);
};
css`
article.discussion {
display: flex;
flex-direction: column;
max-width: 32rem;
height: 100%;
min-height: 100vh;
margin: auto;
padding: 1rem;
gap: 1rem;
box-sizing: border-box;
background: var(--theme-backdrop);
}
article.discussion > header {
display: flex;
justify-content: space-between;
align-items: baseline;
}
article.discussion > header h1 {
margin: 0;
}
article.discussion > main {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
`
export const Discussion = ({ name, groups, first }) => {
// save/restore focus state
const [focusId, setFocusId] = useState(() => {
const id = window.location.hash.slice(1);
if (id && groups[id]) return id;
return first;
});
useEffect(() => {
window.focus = focusId;
const loc = new URL(window.location);
loc.hash = `#${focusId}`;
if (loc.href === window.location.href) return;
window.history.pushState(null, '', loc);
}, [focusId]);
useEffect(() => {
const onpop = (e) => {
console.log('popp');
const id = window.location.hash.slice(1);
if (id && groups[id]) return setFocusId(id);
};
window.addEventListener('popstate', onpop);
window.addEventListener('hashchange', onpop);
return () => {
window.removeEventListener('popstate', onpop);
window.removeEventListener('hashchange', onpop);
};
});
const focus = groups[focusId];
window.groups = groups;
const [pageStore, setPageStore] = useState(() => (
Object.fromEntries(Object.values(groups).map(g => {
const up = g.next.up[0];
const down = g.next.down[0];
return [g.id, { up, down }];
}))
));
const setActivePages = (id, activePages) => {
const nextPageStore = Object.fromEntries(Object.entries(pageStore));
nextPageStore[id] = activePages;
setPageStore(nextPageStore);
};
const panels = [];
// if (focus.next.up.length) {
if (pageStore[focusId].up) {
panels.push({
// ids: focus.next.up,
id: pageStore[focusId].up,
focusDir: 'down',
})
}
panels.push({ id: focusId, focusDir: 'this' });
// if (focus.next.down.length) {
if (pageStore[focusId].down) {
panels.push({
// ids: focus.next.down,
id: pageStore[focusId].down,
focusDir: 'up',
})
}
const getActivePages = (id, focusDir) => {
const activePages = pageStore[id];
if (focusDir === 'this') return activePages;
return { ...activePages, [focusDir]: focusId };
};
return (
{name}
{panels.map(({ id, activePages, focusDir }) => (
setActivePages(id, activePages)}
setFocus={() => {
setActivePages(id, getActivePages(id, focusDir));
setFocusId(id);
}}
groups={groups}
/>
))}
);
};