@anthusai/papyrus 1.12.0 → 1.13.0

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.
@@ -1,45 +1,61 @@
1
1
  "use client";
2
2
 
3
- import { useEffect, useState } from "react";
4
- import { createPortal } from "react-dom";
3
+ import { useEffect } from "react";
5
4
 
6
5
  type Excerpt = { issueId: string; text: string };
7
6
 
8
- function CardExcerpt({ text }: { text: string }) {
9
- const [expanded, setExpanded] = useState(false);
10
- return <div className="board-card-excerpt" data-board-card-excerpt data-expanded={expanded}>
11
- <p>{text}</p>
12
- {text.length > 130 && <button aria-expanded={expanded} onClick={event => { event.stopPropagation(); setExpanded(open => !open); }} onKeyDown={event => event.stopPropagation()} type="button">{expanded ? "Less" : "More"}</button>}
13
- </div>;
7
+ const EXCERPT_TOGGLE_THRESHOLD = 130;
8
+
9
+ function buildExcerptElement(text: string): HTMLElement {
10
+ const container = document.createElement("div");
11
+ container.className = "board-card-excerpt";
12
+ container.dataset.boardCardExcerpt = "";
13
+ container.dataset.expanded = "false";
14
+ const paragraph = document.createElement("p");
15
+ paragraph.textContent = text;
16
+ container.append(paragraph);
17
+ if (text.length > EXCERPT_TOGGLE_THRESHOLD) {
18
+ const toggle = document.createElement("button");
19
+ toggle.type = "button";
20
+ toggle.textContent = "More";
21
+ toggle.setAttribute("aria-expanded", "false");
22
+ const flip = (event: Event) => {
23
+ event.stopPropagation();
24
+ const expanded = container.dataset.expanded !== "true";
25
+ container.dataset.expanded = String(expanded);
26
+ toggle.textContent = expanded ? "Less" : "More";
27
+ toggle.setAttribute("aria-expanded", String(expanded));
28
+ };
29
+ toggle.addEventListener("click", flip);
30
+ toggle.addEventListener("keydown", event => event.stopPropagation());
31
+ container.append(toggle);
32
+ }
33
+ return container;
14
34
  }
15
35
 
16
36
  /**
17
37
  * Adds a short expandable excerpt under each card's headline. The Kanbus card has no excerpt slot,
18
- * so each excerpt is rendered into the card's content block once the card is on the page.
38
+ * so each excerpt is written into the card's content block once the card is on the page.
19
39
  */
20
40
  export function BoardCardExcerpts({ excerpts, scope }: { excerpts: readonly Excerpt[]; scope: HTMLElement | null }) {
21
- const [hosts, setHosts] = useState<{ host: HTMLElement; text: string }[]>([]);
22
41
  useEffect(() => {
23
42
  if (!scope) return;
24
- const collect = () => {
25
- const found = excerpts.flatMap(({ issueId, text }) => {
26
- const card = Array.from(scope.querySelectorAll<HTMLElement>(".issue-card")).find(candidate => candidate.dataset.issueId === issueId);
27
- const content = card?.querySelector<HTMLElement>(".issue-card-content");
28
- if (!content) return [];
29
- let host = content.querySelector<HTMLElement>(":scope > [data-board-card-excerpt-host]");
30
- if (!host) {
31
- host = document.createElement("div");
32
- host.dataset.boardCardExcerptHost = "true";
33
- content.querySelector("h3")?.after(host);
34
- }
35
- return [{ host, text }];
36
- });
37
- setHosts(previous => previous.length === found.length && previous.every((entry, index) => entry.host === found[index].host && entry.text === found[index].text) ? previous : found);
43
+ const textByIssueId = new Map(excerpts.map(({ issueId, text }) => [issueId, text]));
44
+ const place = () => {
45
+ for (const card of Array.from(scope.querySelectorAll<HTMLElement>(".issue-card"))) {
46
+ const text = textByIssueId.get(card.dataset.issueId ?? "");
47
+ const content = card.querySelector<HTMLElement>(".issue-card-content");
48
+ if (!text || !content) continue;
49
+ const existing = content.querySelector<HTMLElement>(":scope > [data-board-card-excerpt]");
50
+ if (existing && existing.querySelector("p")?.textContent === text) continue;
51
+ existing?.remove();
52
+ content.querySelector("h3")?.after(buildExcerptElement(text));
53
+ }
38
54
  };
39
- collect();
40
- const observer = new MutationObserver(collect);
55
+ place();
56
+ const observer = new MutationObserver(place);
41
57
  observer.observe(scope, { childList: true, subtree: true });
42
58
  return () => observer.disconnect();
43
59
  }, [excerpts, scope]);
44
- return <>{hosts.map(({ host, text }, index) => createPortal(<CardExcerpt text={text} />, host, `${index}`))}</>;
60
+ return null;
45
61
  }
package/package.json CHANGED
@@ -374,7 +374,7 @@
374
374
  "optional": true
375
375
  }
376
376
  },
377
- "version": "1.12.0",
377
+ "version": "1.13.0",
378
378
  "dependencies": {
379
379
  "@aws-amplify/adapter-nextjs": "^1.7.3",
380
380
  "@aws-crypto/sha256-js": "5.2.0",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.12.0",
2
+ "version": "1.13.0",
3
3
  "routes": [
4
4
  {
5
5
  "file": "app/%5F_preview/[[...path]]/route.ts",