@dirwell/theme-ledger 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -75,7 +75,7 @@
75
75
  let offsets = $derived.by(() => {
76
76
  const values = [0];
77
77
  for (const entry of rows)
78
- values.push(values.at(-1) + (heights.get(entry.id) ?? 76));
78
+ values.push(values.at(-1) + (heights.get(entry.id) ?? 52));
79
79
  return values;
80
80
  });
81
81
  let start = $derived.by(() => {
@@ -100,14 +100,53 @@
100
100
  }
101
101
  return Math.min(rows.length, Math.max(start + 35, low + 5));
102
102
  });
103
+ function stickyLayout(node) {
104
+ const heading = node.querySelector(":scope > header");
105
+ const columns = node.querySelector(".entry-head");
106
+ const update = () => {
107
+ const height = heading.getBoundingClientRect().height;
108
+ const disabled = height > Math.min(innerHeight / 4, 192);
109
+ node.toggleAttribute("data-sticky-header-disabled", disabled);
110
+ node.style.setProperty(
111
+ "--dw-sticky-header-height",
112
+ `${disabled ? 0 : height}px`,
113
+ );
114
+ node.style.setProperty(
115
+ "--dw-sticky-columns-height",
116
+ `${columns.getBoundingClientRect().height}px`,
117
+ );
118
+ node.setAttribute("data-sticky-ready", "");
119
+ };
120
+ update();
121
+ const observer = new ResizeObserver(update);
122
+ observer.observe(heading);
123
+ observer.observe(columns);
124
+ window.addEventListener("resize", update);
125
+ return {
126
+ destroy() {
127
+ observer.disconnect();
128
+ window.removeEventListener("resize", update);
129
+ },
130
+ };
131
+ }
103
132
  function measure(node, id) {
104
133
  if (typeof ResizeObserver === "undefined") return;
105
134
  const observer = new ResizeObserver(() => {
106
135
  const value = node.getBoundingClientRect().height;
107
136
  if (value > 0 && heights.get(id) !== value) {
137
+ const focused = list?.contains(document.activeElement)
138
+ ? document.activeElement
139
+ : null;
140
+ const top = focused?.getBoundingClientRect().top;
108
141
  const next = new Map(heights);
109
142
  next.set(id, value);
110
143
  heights = next;
144
+ // Updating spacer estimates must preserve the focused link's viewport position.
145
+ if (focused)
146
+ tick().then(() => {
147
+ if (focused.isConnected && document.activeElement === focused)
148
+ window.scrollBy(0, focused.getBoundingClientRect().top - top);
149
+ });
111
150
  }
112
151
  });
113
152
  observer.observe(node);
@@ -320,16 +359,29 @@
320
359
  if (!row) return;
321
360
  selected = row.id;
322
361
  if (virtual) {
323
- scrollTop = Math.max(0, offsets[target] - 100);
362
+ const inset = list
363
+ ? parseFloat(
364
+ getComputedStyle(list).getPropertyValue(
365
+ "--dw-sticky-header-height",
366
+ ),
367
+ ) +
368
+ parseFloat(
369
+ getComputedStyle(list).getPropertyValue(
370
+ "--dw-sticky-columns-height",
371
+ ),
372
+ ) +
373
+ 8
374
+ : 0;
375
+ scrollTop = Math.max(0, offsets[target] - inset);
324
376
  if (list)
325
377
  window.scrollTo({
326
378
  top: list.getBoundingClientRect().top + window.scrollY + scrollTop,
327
379
  });
328
380
  }
329
381
  await tick();
330
- list
331
- ?.querySelector(`[data-order="${index}"] a`)
332
- ?.focus({ preventScroll: true });
382
+ const link = list?.querySelector(`[data-order="${target}"] a`);
383
+ link?.focus({ preventScroll: true });
384
+ link?.closest(".entry")?.scrollIntoView({ block: "nearest" });
333
385
  }
334
386
  function keyboard(event) {
335
387
  if (event.isComposing || event.altKey || event.ctrlKey || event.metaKey)
@@ -626,32 +678,32 @@
626
678
  if (list) scrollTop = Math.max(0, window.scrollY - list.offsetTop);
627
679
  }}
628
680
  />
629
- <header class="page-header">
630
- <div class="wordmark">
631
- {#if header}{@render header({ model })}{:else}{#if ui.brand.logoUrl}<img
632
- src={ui.brand.logoUrl}
633
- alt={ui.brand.logoAlt ?? ""}
634
- width="40"
635
- height="40"
636
- />{/if}
637
- <h1>{ui.brand.name ?? model.title}</h1>
638
- <span>{ui.labels.archive}</span>{/if}
639
- </div>
640
- <nav aria-label={ui.labels.breadcrumb}>
641
- <a
642
- href={model.rootHref}
643
- onclick={(event) =>
644
- navigateLink(event, model.rootHref, model.rootDataHref)}
645
- >{ui.labels.home}</a
646
- >{#each model.breadcrumbs as crumb}
647
- / <a
648
- href={crumb.href}
649
- onclick={(event) => navigateLink(event, crumb.href, crumb.dataHref)}
650
- >{crumb.name}</a
651
- >{/each}
652
- </nav>
653
- </header>
654
- <main data-explorer data-framework="svelte" aria-busy={busy}>
681
+ <main data-explorer data-framework="svelte" aria-busy={busy} use:stickyLayout>
682
+ <header class="page-header">
683
+ <div class="wordmark">
684
+ {#if header}{@render header({ model })}{:else}{#if ui.brand.logoUrl}<img
685
+ src={ui.brand.logoUrl}
686
+ alt={ui.brand.logoAlt ?? ""}
687
+ width="40"
688
+ height="40"
689
+ />{/if}
690
+ <h1>{ui.brand.name ?? model.title}</h1>
691
+ <span>{ui.labels.archive}</span>{/if}
692
+ </div>
693
+ <nav aria-label={ui.labels.breadcrumb}>
694
+ <a
695
+ href={model.rootHref}
696
+ onclick={(event) =>
697
+ navigateLink(event, model.rootHref, model.rootDataHref)}
698
+ >{ui.labels.home}</a
699
+ >{#each model.breadcrumbs as crumb}
700
+ / <a
701
+ href={crumb.href}
702
+ onclick={(event) => navigateLink(event, crumb.href, crumb.dataHref)}
703
+ >{crumb.name}</a
704
+ >{/each}
705
+ </nav>
706
+ </header>
655
707
  <div class="toolbar">
656
708
  <label class="search-field"
657
709
  >{ui.labels.searchDirectory}<input
@@ -755,14 +807,10 @@
755
807
  <div
756
808
  class="entry-head"
757
809
  data-custom-columns={Boolean(model.ui?.columns)}
758
- style:grid-template-columns={model.ui?.columns
810
+ style:--dw-entry-columns={model.ui?.columns
759
811
  ? ui.columns
760
812
  .map((column) =>
761
- column === "name"
762
- ? "minmax(0,1fr)"
763
- : typeof column === "object"
764
- ? "minmax(0,12rem)"
765
- : "minmax(6rem,max-content)",
813
+ column === "name" ? "minmax(0,1fr)" : "minmax(0,12rem)",
766
814
  )
767
815
  .join(" ")
768
816
  : undefined}
@@ -791,12 +839,10 @@
791
839
  data-id={entry.id}
792
840
  data-order={start + index}
793
841
  data-active={selected === entry.id}
794
- style:grid-template-columns={model.ui?.columns
842
+ style:--dw-entry-columns={model.ui?.columns
795
843
  ? ui.columns
796
844
  .map((column) =>
797
- column === "name"
798
- ? "minmax(0,1fr)"
799
- : "minmax(6rem,max-content)",
845
+ column === "name" ? "minmax(0,1fr)" : "minmax(0,12rem)",
800
846
  )
801
847
  .join(" ")
802
848
  : undefined}