web/repo: a scroller's bands at its host's edge are css, drawn from the first frame, and scrollChrome draws the rest
Signed-off-by: dawn <dawn@tangled.org>
290 unmodified linesExpand all29129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038185 unmodified lines467468469470388389471472473474475476477125 unmodified linesExpand all290 unmodified linesExpand all[data-frame],[data-frame] * { scrollbar-width: none;}/* a scroller's bands where it meets its host's edge, the bottom one or, across, both sides, are css: drawn from the first frame, shown while there's content past the edge. the scroller names its scroll timeline (.y-edge, or .y-edge-x under a .y-edge-across host) and its host draws the band. scrollChrome.ts draws the rest, and all of them where css can't */@supports (timeline-scope: --a) { .y-edge { scroll-timeline: --y-edge y; } .y-edge-x { scroll-timeline: --y-edge-x x; } .y-edge-host { --dz-on: var(--y-card); position: relative; timeline-scope: --y-edge, --y-edge-x; } .y-edge-host::before, .y-edge-host.y-edge-across::after { content: ""; position: absolute; z-index: 34; pointer-events: none; opacity: 0; animation: y-edge-out linear both; animation-timeline: --y-edge; animation-range: calc(100% - 1px) 100%; } .y-edge-host:not(.y-edge-across)::before { right: 0; bottom: 0; left: 0; height: 12px; background: linear-gradient( to top, color-mix(in srgb, var(--y-accent) 75%, var(--dz-on)), var(--dz-on) ); mask: var(--y-dz-bottom) 0 0 / 8px 12px repeat-x; } .y-edge-host.y-edge-across::before, .y-edge-host.y-edge-across::after { top: 0; bottom: 0; width: 12px; animation-timeline: --y-edge-x; } .y-edge-host.y-edge-across::before { left: 0; background: linear-gradient( to right, color-mix(in srgb, var(--y-accent) 75%, var(--dz-on)), var(--dz-on) ); mask: var(--y-dz-left) 0 0 / 12px 8px repeat-y; animation-name: y-edge-in; animation-range: 0 1px; } .y-edge-host.y-edge-across::after { right: 0; background: linear-gradient( to left, color-mix(in srgb, var(--y-accent) 75%, var(--dz-on)), var(--dz-on) ); mask: var(--y-dz-right) 0 0 / 12px 8px repeat-y; }}@keyframes y-edge-in { from { opacity: 0; } to { opacity: 1; }}@keyframes y-edge-out { from { opacity: 1; } to { opacity: 0; }}.y-sc-layer { position: fixed; inset: 0; z-index: 40;85 unmodified lines [data-frame] > .right > .body { border-bottom-right-radius: var(--y-cut); corner-shape: bevel; } /* the scroll's corner copies the body's cut, one pixel in */ [data-frame] > .right > .body > .scroll { /* the scroll's corner, and its band's, copy the body's cut, one pixel in */ [data-frame] > .right > .body > .scroll, [data-frame] > .right > .body::before { border-bottom-right-radius: calc(var(--y-cut) - 1px); corner-shape: bevel; } }125 unmodified linesExpand all121234562 unmodified lines910111213141516171819202122232425262728293062 unmodified lines9394959686979899100101102103931041051069610710810911011156 unmodified lines16816917017117217317417517659 unmodified lines// fixed layers, because a bounce or re-render in a scroller would move them// js only, without it the scrollers keep their native bars// fixed layers, because a bounce or re-render in a scroller would move them. the bands at a// scroller's host edge are css where it can (svfe.css .y-edge), drawn from the first frame
const EDGE = 12;const SCROLLS = /auto|scroll/;
2 unmodified lines bottom: HTMLDivElement; left: HTMLDivElement; right: HTMLDivElement; thumb: HTMLDivElement; // the axis whose host edge bands css draws (svfe.css .y-edge), if any css?: "x" | "y"; timer?: ReturnType<typeof setTimeout>;}
const CSS_EDGES = typeof CSS !== "undefined" && CSS.supports("timeline-scope: --a");
const cssAxis = (el: Element): Parts["css"] => { if (!CSS_EDGES) return undefined; const cs = getComputedStyle(el); if (cs.getPropertyValue("scroll-timeline-name") === "none") return undefined; return cs.getPropertyValue("scroll-timeline-axis") === "x" ? "x" : "y";};
const scrollsY = (el: Element) => el.scrollHeight > el.clientHeight + 2 && SCROLLS.test(getComputedStyle(el).overflowY);const scrollsX = (el: Element) => el.scrollWidth > el.clientWidth + 2 && SCROLLS.test(getComputedStyle(el).overflowX);62 unmodified lines const bottom = top + el.clientHeight; const above = el.scrollTop > 1; const below = el.scrollHeight - el.scrollTop - el.clientHeight > 1; show(p.top, above && top >= c.t - 1, left, top, width); show(p.bottom, below && bottom <= c.b + 1, left, bottom - EDGE, width); show(p.bottom, p.css !== "y" && below && bottom <= c.b + 1, left, bottom - EDGE, width);
const shownTop = Math.max(top, c.t); const height = Math.min(bottom, c.b) - shownTop; const right = q.left + el.clientLeft + el.clientWidth; const before = el.scrollLeft > 1; const after = el.scrollWidth - el.scrollLeft - el.clientWidth > 1; show(p.left, before && height > 0, left, shownTop, EDGE, height); show(p.left, p.css !== "x" && before && height > 0, left, shownTop, EDGE, height); show( p.right, after && height > 0 && right <= c.r + 1, p.css !== "x" && after && height > 0 && right <= c.r + 1, right - EDGE, shownTop, EDGE, height56 unmodified lines }); parts.set(el, p); } const p = parts.get(el)!; p.css = cssAxis(el); // no bounce or scroll chaining to the page, which would move the bands if (scrollsY(el)) (el as HTMLElement).style.overscrollBehaviorY = "none"; const on = surface(el); for (const n of [p.top, p.bottom, p.left, p.right]) n.style.setProperty("--dz-on", on);59 unmodified lines101 unmodified linesExpand all10210310410510610710610710810911011142 unmodified lines15415515615715815916016116288 unmodified lines101 unmodified linesExpand all }); });</script>
<nav class="bar" aria-label="repository sections"> <span class="y-rb tabs" {@attach showCurrent}><nav class="bar y-edge-host y-edge-across" aria-label="repository sections"> <span class="y-rb tabs y-edge-x" {@attach showCurrent}> {#each tabs as tab (tab.id)} <a class="y-seg" class:y-seg-a={active === tab.id && !extra}42 unmodified lines</nav>
<style> .bar { --dz-on: var(--y-soft); display: flex; align-items: stretch; border: 1px solid var(--y-border); border-bottom: 0;88 unmodified lines11 unmodified lines121314151616171819204 unmodified lines2526272829293031323332 unmodified lines11 unmodified lines
let { title, right, header, children, class: className = "", bodyClass = "" }: Props = $props();</script>
<section class="pn {className}"><section class="pn y-edge-host {className}"> {#if header} {@render header()} {:else if title} <header class="ph">4 unmodified lines <span class="pr">{@render right()}</span> {/if} </header> {/if} <div class="pb {bodyClass}">{@render children()}</div> <div class="pb y-edge {bodyClass}">{@render children()}</div></section>
<style> .pn {32 unmodified lines328 unmodified linesExpand all329330331332333333334335335336337338339290 unmodified linesExpand all328 unmodified linesExpand all <div class="acct"><AccountCorner /></div> <!-- eslint-disable-next-line svelte/no-at-html-tags -- our own code, see SCROLL_TABS --> {@html SCROLL_TABS} </div> <div class="body"> <div class="body y-edge-host"> <div class="prog" class:on={loading} aria-hidden="true"></div> <div class="scroll" id="pane" tabindex="-1">{@render body()}</div> <div class="scroll y-edge" id="pane" tabindex="-1">{@render body()}</div> </div> </div> <!-- parsed after the pane, see .parsed --> <span class="parsed" hidden></span>290 unmodified linesExpand all47 unmodified lines48495051525253545556575859606162636460656667686936 unmodified lines10610710810911011111211311493 unmodified lines47 unmodified lines
<div class="board" data-board data-testid="ticketBoard"> {#each lanes as lane (lane.label)} {@const finished = lane.key === "done"} <section class="lane" class:finished data-testid="ticketLane" data-lane={lane.key}> <section class="lane y-edge-host" class:finished data-testid="ticketLane" data-lane={lane.key} > <h2 class="head"> <b>{lane.label}</b><span data-testid="laneCount" >{formatCount( finished ? Math.max(doneCount, lane.tickets.length) : lane.tickets.length )}</span > </h2> <div class="cards"> <div class="cards y-edge"> {#if finished} {#each days(lane.tickets) as group (group.day + group.cards[0]?.uri + group.deck[0]?.uri)} <div class="day">{group.day}</div> {#each group.cards as ticket (ticket.uri)}36 unmodified lines padding: 0.9rem 1.4rem 0; background: var(--y-lane); } .lane { --dz-on: var(--y-lane); display: flex; flex-direction: column; min-width: 0; }93 unmodified lines