@particle-academy/react-fancy 5.32.0 → 5.34.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.
- package/CHANGELOG.md +118 -0
- package/LICENSE +21 -0
- package/dist/activity-light.cjs +13 -0
- package/dist/activity-light.cjs.map +1 -0
- package/dist/activity-light.d.cts +64 -0
- package/dist/activity-light.d.ts +64 -0
- package/dist/activity-light.js +4 -0
- package/dist/activity-light.js.map +1 -0
- package/dist/button.cjs +4 -4
- package/dist/button.js +2 -2
- package/dist/{chunk-WRQBV7GU.cjs → chunk-23FTDEGR.cjs} +2 -2
- package/dist/chunk-23FTDEGR.cjs.map +1 -0
- package/dist/{chunk-ZYVWKYPO.js → chunk-2OMB3OOR.js} +3 -3
- package/dist/{chunk-ZYVWKYPO.js.map → chunk-2OMB3OOR.js.map} +1 -1
- package/dist/{chunk-WHOVPDC5.js → chunk-2XAVAPDA.js} +2 -2
- package/dist/chunk-2XAVAPDA.js.map +1 -0
- package/dist/{chunk-G6VMNSCM.js → chunk-46IPQEUP.js} +3 -3
- package/dist/{chunk-G6VMNSCM.js.map → chunk-46IPQEUP.js.map} +1 -1
- package/dist/{chunk-H6PXIB35.cjs → chunk-5Q7EQQTT.cjs} +4 -4
- package/dist/{chunk-H6PXIB35.cjs.map → chunk-5Q7EQQTT.cjs.map} +1 -1
- package/dist/{chunk-BQ77ZWFM.js → chunk-6ASZUE4T.js} +4 -4
- package/dist/{chunk-BQ77ZWFM.js.map → chunk-6ASZUE4T.js.map} +1 -1
- package/dist/{chunk-WEKQD4BQ.js → chunk-DCDW3LW6.js} +3 -3
- package/dist/{chunk-WEKQD4BQ.js.map → chunk-DCDW3LW6.js.map} +1 -1
- package/dist/{chunk-YMU3SS6D.cjs → chunk-E2SZBIAV.cjs} +16 -16
- package/dist/{chunk-YMU3SS6D.cjs.map → chunk-E2SZBIAV.cjs.map} +1 -1
- package/dist/chunk-EHNMKQ4O.cjs +71 -0
- package/dist/chunk-EHNMKQ4O.cjs.map +1 -0
- package/dist/chunk-IR6JSQ4O.cjs +233 -0
- package/dist/chunk-IR6JSQ4O.cjs.map +1 -0
- package/dist/{chunk-VRJTPXJ7.cjs → chunk-J2KYNK63.cjs} +5 -5
- package/dist/{chunk-VRJTPXJ7.cjs.map → chunk-J2KYNK63.cjs.map} +1 -1
- package/dist/{chunk-B4PFNSX7.js → chunk-KM4R4XWB.js} +3 -3
- package/dist/{chunk-B4PFNSX7.js.map → chunk-KM4R4XWB.js.map} +1 -1
- package/dist/{chunk-5TUZENNJ.cjs → chunk-MTUH2X3W.cjs} +4 -4
- package/dist/{chunk-5TUZENNJ.cjs.map → chunk-MTUH2X3W.cjs.map} +1 -1
- package/dist/chunk-PZTA6NCD.js +69 -0
- package/dist/chunk-PZTA6NCD.js.map +1 -0
- package/dist/{chunk-UWL6U3L7.cjs → chunk-RMJGF7MN.cjs} +3 -3
- package/dist/{chunk-UWL6U3L7.cjs.map → chunk-RMJGF7MN.cjs.map} +1 -1
- package/dist/{chunk-EIUS2K72.js → chunk-TX6VUYBC.js} +3 -3
- package/dist/{chunk-EIUS2K72.js.map → chunk-TX6VUYBC.js.map} +1 -1
- package/dist/{chunk-LAQGBLIU.cjs → chunk-VKXNCYLE.cjs} +14 -5
- package/dist/chunk-VKXNCYLE.cjs.map +1 -0
- package/dist/{chunk-32HCJ5GD.js → chunk-W6RDAZJC.js} +14 -5
- package/dist/chunk-W6RDAZJC.js.map +1 -0
- package/dist/{chunk-LQFQ2UKU.cjs → chunk-WWUKTXPX.cjs} +3 -3
- package/dist/{chunk-LQFQ2UKU.cjs.map → chunk-WWUKTXPX.cjs.map} +1 -1
- package/dist/chunk-Z4CW3U6C.js +231 -0
- package/dist/chunk-Z4CW3U6C.js.map +1 -0
- package/dist/composer.cjs +2 -2
- package/dist/composer.d.cts +1 -1
- package/dist/composer.d.ts +1 -1
- package/dist/composer.js +1 -1
- package/dist/editor.cjs +4 -4
- package/dist/editor.js +2 -2
- package/dist/file-upload.cjs +3 -3
- package/dist/file-upload.js +1 -1
- package/dist/{index-CDo7LyIB.d.ts → index-C8PKWA_5.d.cts} +21 -0
- package/dist/{index-CDo7LyIB.d.cts → index-C8PKWA_5.d.ts} +21 -0
- package/dist/index.cjs +154 -144
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +29 -27
- package/dist/inputs.cjs +9 -9
- package/dist/inputs.js +3 -3
- package/dist/json-editor.cjs +15 -15
- package/dist/json-editor.js +4 -4
- package/dist/magic-wand.cjs +4 -4
- package/dist/magic-wand.js +3 -3
- package/dist/prompt-input.cjs +4 -4
- package/dist/prompt-input.js +3 -3
- package/dist/reason-tag.cjs +4 -4
- package/dist/reason-tag.js +3 -3
- package/dist/virtual-list.cjs +13 -0
- package/dist/virtual-list.cjs.map +1 -0
- package/dist/virtual-list.d.cts +125 -0
- package/dist/virtual-list.d.ts +125 -0
- package/dist/virtual-list.js +4 -0
- package/dist/virtual-list.js.map +1 -0
- package/package.json +23 -2
- package/dist/chunk-32HCJ5GD.js.map +0 -1
- package/dist/chunk-LAQGBLIU.cjs.map +0 -1
- package/dist/chunk-WHOVPDC5.js.map +0 -1
- package/dist/chunk-WRQBV7GU.cjs.map +0 -1
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { cn } from './chunk-MMW2JGKZ.js';
|
|
2
|
+
import { useRef, useState, useCallback, useMemo, useEffect, useLayoutEffect } from 'react';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function VirtualList({
|
|
6
|
+
items,
|
|
7
|
+
renderRow,
|
|
8
|
+
height = "100%",
|
|
9
|
+
estimateRowHeight = 32,
|
|
10
|
+
overscan = 6,
|
|
11
|
+
followTail = false,
|
|
12
|
+
onFollowChange,
|
|
13
|
+
onUnseenChange,
|
|
14
|
+
scrollToId = null,
|
|
15
|
+
onScrollToIdResolved,
|
|
16
|
+
className,
|
|
17
|
+
ref,
|
|
18
|
+
...props
|
|
19
|
+
}) {
|
|
20
|
+
const viewportRef = useRef(null);
|
|
21
|
+
const windowRef = useRef(null);
|
|
22
|
+
const heights = useRef(/* @__PURE__ */ new Map());
|
|
23
|
+
const [measured, setMeasured] = useState(0);
|
|
24
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
25
|
+
const [clientHeight, setClientHeight] = useState(0);
|
|
26
|
+
const attachViewport = useCallback(
|
|
27
|
+
(el) => {
|
|
28
|
+
viewportRef.current = el;
|
|
29
|
+
if (typeof ref === "function") ref(el);
|
|
30
|
+
else if (ref) ref.current = el;
|
|
31
|
+
},
|
|
32
|
+
[ref]
|
|
33
|
+
);
|
|
34
|
+
const viewport = clientHeight > 0 ? clientHeight : typeof height === "number" ? height : estimateRowHeight * 20;
|
|
35
|
+
const { offsets, total } = useMemo(() => {
|
|
36
|
+
const offs = new Float64Array(items.length + 1);
|
|
37
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
38
|
+
offs[i + 1] = offs[i] + (heights.current.get(items[i].id) ?? estimateRowHeight);
|
|
39
|
+
}
|
|
40
|
+
return { offsets: offs, total: items.length === 0 ? 0 : offs[items.length] };
|
|
41
|
+
}, [items, measured, estimateRowHeight]);
|
|
42
|
+
const indexAt = useCallback(
|
|
43
|
+
(y) => {
|
|
44
|
+
let lo = 0;
|
|
45
|
+
let hi = items.length - 1;
|
|
46
|
+
while (lo < hi) {
|
|
47
|
+
const mid = lo + hi + 1 >> 1;
|
|
48
|
+
if (offsets[mid] <= y) lo = mid;
|
|
49
|
+
else hi = mid - 1;
|
|
50
|
+
}
|
|
51
|
+
return Math.max(0, lo);
|
|
52
|
+
},
|
|
53
|
+
[items.length, offsets]
|
|
54
|
+
);
|
|
55
|
+
const start = Math.max(0, indexAt(scrollTop) - overscan);
|
|
56
|
+
const end = Math.min(items.length, indexAt(scrollTop + viewport) + 1 + overscan);
|
|
57
|
+
const visible = useMemo(() => items.slice(start, end), [items, start, end]);
|
|
58
|
+
const observer = useRef(null);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
61
|
+
const ro = new ResizeObserver(() => setMeasured((v) => v + 1));
|
|
62
|
+
observer.current = ro;
|
|
63
|
+
return () => {
|
|
64
|
+
ro.disconnect();
|
|
65
|
+
observer.current = null;
|
|
66
|
+
};
|
|
67
|
+
}, []);
|
|
68
|
+
useLayoutEffect(() => {
|
|
69
|
+
const win = windowRef.current;
|
|
70
|
+
if (!win) return;
|
|
71
|
+
let changed = false;
|
|
72
|
+
for (const el of Array.from(win.children)) {
|
|
73
|
+
const id = el.getAttribute(ROW_ID_ATTR);
|
|
74
|
+
if (id === null) continue;
|
|
75
|
+
observer.current?.observe(el);
|
|
76
|
+
const h = el.getBoundingClientRect().height;
|
|
77
|
+
if (h > 0 && heights.current.get(id) !== h) {
|
|
78
|
+
heights.current.set(id, h);
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const vp = viewportRef.current;
|
|
83
|
+
if (vp && vp.clientHeight > 0 && vp.clientHeight !== clientHeight) setClientHeight(vp.clientHeight);
|
|
84
|
+
if (changed) setMeasured((v) => v + 1);
|
|
85
|
+
});
|
|
86
|
+
const anchored = useRef(null);
|
|
87
|
+
const resolved = useRef(null);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
anchored.current = null;
|
|
90
|
+
resolved.current = null;
|
|
91
|
+
}, [scrollToId]);
|
|
92
|
+
useLayoutEffect(() => {
|
|
93
|
+
if (scrollToId === null || resolved.current === scrollToId) return;
|
|
94
|
+
const index = items.findIndex((item) => item.id === scrollToId);
|
|
95
|
+
if (index < 0) {
|
|
96
|
+
resolved.current = scrollToId;
|
|
97
|
+
onScrollToIdResolved?.(scrollToId, false);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const vp = viewportRef.current;
|
|
101
|
+
if (!vp) return;
|
|
102
|
+
const target = Math.max(0, Math.min(offsets[index], Math.max(0, total - viewport)));
|
|
103
|
+
if (anchored.current !== target) {
|
|
104
|
+
anchored.current = target;
|
|
105
|
+
vp.scrollTop = target;
|
|
106
|
+
setScrollTop(target);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (Math.abs(vp.scrollTop - target) <= 1) {
|
|
110
|
+
resolved.current = scrollToId;
|
|
111
|
+
onScrollToIdResolved?.(scrollToId, true);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
const reportedFollow = useRef(followTail);
|
|
115
|
+
const programmatic = useRef(false);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
reportedFollow.current = followTail;
|
|
118
|
+
}, [followTail]);
|
|
119
|
+
const askFollow = useCallback(
|
|
120
|
+
(next) => {
|
|
121
|
+
if (reportedFollow.current === next) return;
|
|
122
|
+
reportedFollow.current = next;
|
|
123
|
+
onFollowChange?.(next);
|
|
124
|
+
},
|
|
125
|
+
[onFollowChange]
|
|
126
|
+
);
|
|
127
|
+
useLayoutEffect(() => {
|
|
128
|
+
if (!followTail) return;
|
|
129
|
+
if (scrollToId !== null && resolved.current !== scrollToId) return;
|
|
130
|
+
const vp = viewportRef.current;
|
|
131
|
+
if (!vp) return;
|
|
132
|
+
const bottom = Math.max(0, total - viewport);
|
|
133
|
+
if (Math.abs(vp.scrollTop - bottom) > 0.5) {
|
|
134
|
+
programmatic.current = true;
|
|
135
|
+
vp.scrollTop = bottom;
|
|
136
|
+
setScrollTop(bottom);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const onScroll = useCallback(() => {
|
|
140
|
+
const vp = viewportRef.current;
|
|
141
|
+
if (!vp) return;
|
|
142
|
+
const next = vp.scrollTop;
|
|
143
|
+
const previous = scrollTop;
|
|
144
|
+
setScrollTop(next);
|
|
145
|
+
const bottom = Math.max(0, total - viewport);
|
|
146
|
+
const wasProgrammatic = programmatic.current;
|
|
147
|
+
programmatic.current = false;
|
|
148
|
+
if (next <= bottom - BOTTOM_SLACK) {
|
|
149
|
+
if (!wasProgrammatic && next < previous) askFollow(false);
|
|
150
|
+
} else {
|
|
151
|
+
askFollow(true);
|
|
152
|
+
}
|
|
153
|
+
}, [scrollTop, total, viewport, askFollow]);
|
|
154
|
+
const onWheel = useCallback(
|
|
155
|
+
(e) => {
|
|
156
|
+
if (e.deltaY < 0) askFollow(false);
|
|
157
|
+
},
|
|
158
|
+
[askFollow]
|
|
159
|
+
);
|
|
160
|
+
const onKeyDown = useCallback(
|
|
161
|
+
(e) => {
|
|
162
|
+
if (UPWARD_KEYS.has(e.key)) askFollow(false);
|
|
163
|
+
},
|
|
164
|
+
[askFollow]
|
|
165
|
+
);
|
|
166
|
+
const touchStart = useRef(null);
|
|
167
|
+
const onTouchStart = useCallback((e) => {
|
|
168
|
+
touchStart.current = e.touches[0]?.clientY ?? null;
|
|
169
|
+
}, []);
|
|
170
|
+
const onTouchMove = useCallback(
|
|
171
|
+
(e) => {
|
|
172
|
+
const from = touchStart.current;
|
|
173
|
+
const y = e.touches[0]?.clientY;
|
|
174
|
+
if (from !== null && y !== void 0 && y - from > TOUCH_SLOP) askFollow(false);
|
|
175
|
+
},
|
|
176
|
+
[askFollow]
|
|
177
|
+
);
|
|
178
|
+
const seen = useRef(items.length);
|
|
179
|
+
const reportedUnseen = useRef(null);
|
|
180
|
+
useEffect(() => {
|
|
181
|
+
const unseen = followTail ? 0 : Math.max(0, items.length - seen.current);
|
|
182
|
+
if (followTail) seen.current = items.length;
|
|
183
|
+
if (reportedUnseen.current === unseen) return;
|
|
184
|
+
reportedUnseen.current = unseen;
|
|
185
|
+
onUnseenChange?.(unseen);
|
|
186
|
+
}, [items.length, followTail, onUnseenChange]);
|
|
187
|
+
return /* @__PURE__ */ jsx(
|
|
188
|
+
"div",
|
|
189
|
+
{
|
|
190
|
+
ref: attachViewport,
|
|
191
|
+
"data-react-fancy-virtual-list": "",
|
|
192
|
+
"data-react-fancy-virtual-list-following": followTail ? "true" : "false",
|
|
193
|
+
"data-react-fancy-virtual-list-rows": items.length,
|
|
194
|
+
role: "log",
|
|
195
|
+
tabIndex: 0,
|
|
196
|
+
className: cn("relative overflow-y-auto overflow-x-hidden outline-none", className),
|
|
197
|
+
style: { height },
|
|
198
|
+
onScroll,
|
|
199
|
+
onWheel,
|
|
200
|
+
onKeyDown,
|
|
201
|
+
onTouchStart,
|
|
202
|
+
onTouchMove,
|
|
203
|
+
...props,
|
|
204
|
+
children: /* @__PURE__ */ jsx(
|
|
205
|
+
"div",
|
|
206
|
+
{
|
|
207
|
+
"data-react-fancy-virtual-list-sizer": "",
|
|
208
|
+
style: { height: `${total}px`, position: "relative" },
|
|
209
|
+
children: /* @__PURE__ */ jsx(
|
|
210
|
+
"div",
|
|
211
|
+
{
|
|
212
|
+
ref: windowRef,
|
|
213
|
+
"data-react-fancy-virtual-list-window": "",
|
|
214
|
+
"data-react-fancy-virtual-list-start": start,
|
|
215
|
+
style: { transform: `translateY(${offsets[start] ?? 0}px)` },
|
|
216
|
+
children: visible.map((item, i) => /* @__PURE__ */ jsx("div", { "data-react-fancy-virtual-row-id": item.id, children: renderRow(item, start + i) }, item.id))
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
var ROW_ID_ATTR = "data-react-fancy-virtual-row-id";
|
|
225
|
+
var BOTTOM_SLACK = 4;
|
|
226
|
+
var TOUCH_SLOP = 4;
|
|
227
|
+
var UPWARD_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "PageUp", "Home"]);
|
|
228
|
+
|
|
229
|
+
export { VirtualList };
|
|
230
|
+
//# sourceMappingURL=chunk-Z4CW3U6C.js.map
|
|
231
|
+
//# sourceMappingURL=chunk-Z4CW3U6C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/VirtualList/VirtualList.tsx"],"names":[],"mappings":"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","file":"chunk-Z4CW3U6C.js","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent, TouchEvent, WheelEvent } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { VirtualListProps, VirtualListRow } from \"./VirtualList.types\";\n\n/**\n * A windowed list for ledgers that run to tens of thousands of rows — only the\n * visible slice is in the DOM, with **measured** variable row heights, deep\n * links by row id, and sticky-bottom tail-following.\n *\n * Built for an agent event stream, where every one of those three is load\n * bearing at once: message rows wrap and event rows do not, a link carries\n * `?at=<rowId>` to the exact row under discussion, and new rows arrive while\n * the human is reading older ones.\n *\n * ## Heights are measured, never predicted\n *\n * `estimateRowHeight` only seeds rows that have never been on screen. The\n * moment a row renders, its real height is measured and replaces the estimate,\n * and every offset after it moves. The alternative — a `rowHeight(item)`\n * callback — is a prediction about text wrapping, font loading and container\n * width, and when it is wrong the error accumulates down the list: rows draw in\n * the wrong place and `scrollToId` lands on a neighbour. Measuring is slower to\n * settle and correct when it does.\n *\n * ## Tail-follow breaks on INTENT, not on position\n *\n * Any upward gesture — wheel, drag, PageUp, Home — stops following\n * immediately, even when the list is still pinned to the bottom and\n * `scrollTop` has not changed yet. Position cannot tell \"the human scrolled\n * up\" from \"content grew downward\", so a position-only implementation keeps\n * following through a wheel-up and yanks the reader back on the next append.\n *\n * ## It is controlled\n *\n * `followTail` is the consumer's state. This component only ever *asks* via\n * `onFollowChange`, so the follow state is readable and writable by an agent\n * over a bridge, as the Human+ component contract requires. The `↓ n new` pill\n * is the consumer's to render, from `onUnseenChange`.\n *\n * @example\n * ```tsx\n * const [following, setFollowing] = useState(true);\n * const [unseen, setUnseen] = useState(0);\n *\n * <VirtualList\n * items={events}\n * renderRow={(e) => <StreamRow event={e} />}\n * height=\"100%\"\n * estimateRowHeight={28}\n * followTail={following}\n * onFollowChange={setFollowing}\n * onUnseenChange={setUnseen}\n * scrollToId={params.at}\n * />\n * {!following && unseen > 0 && (\n * <Button onClick={() => setFollowing(true)}>↓ {unseen} new</Button>\n * )}\n * ```\n */\nexport function VirtualList<T extends VirtualListRow>({\n items,\n renderRow,\n height = \"100%\",\n estimateRowHeight = 32,\n overscan = 6,\n followTail = false,\n onFollowChange,\n onUnseenChange,\n scrollToId = null,\n onScrollToIdResolved,\n className,\n ref,\n ...props\n}: VirtualListProps<T>) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const windowRef = useRef<HTMLDivElement | null>(null);\n\n /** Measured heights by row id, so a height follows its row across a reindex. */\n const heights = useRef(new Map<string, number>());\n /** Bumped when a measurement actually changed, to recompute offsets. */\n const [measured, setMeasured] = useState(0);\n\n const [scrollTop, setScrollTop] = useState(0);\n const [clientHeight, setClientHeight] = useState(0);\n\n const attachViewport = useCallback(\n (el: HTMLDivElement | null) => {\n viewportRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as { current: HTMLDivElement | null }).current = el;\n },\n [ref],\n );\n\n // A number `height` IS the viewport height, and is usable before the element\n // has been measured. Without one, render a conservative first window so there\n // is something on screen for the measurement pass to work from.\n const viewport =\n clientHeight > 0 ? clientHeight : typeof height === \"number\" ? height : estimateRowHeight * 20;\n\n /**\n * Prefix sums of row heights: `offsets[i]` is where row `i` starts, and the\n * last entry is the total. Rebuilt whenever rows or measurements change —\n * O(n) once per change, against O(log n) per scroll from the binary search,\n * rather than O(n) on every scroll frame.\n */\n const { offsets, total } = useMemo(() => {\n const offs = new Float64Array(items.length + 1);\n for (let i = 0; i < items.length; i += 1) {\n offs[i + 1] = offs[i] + (heights.current.get(items[i].id) ?? estimateRowHeight);\n }\n\n return { offsets: offs, total: items.length === 0 ? 0 : offs[items.length] };\n }, [items, measured, estimateRowHeight]);\n\n /** First row whose span contains `y`. */\n const indexAt = useCallback(\n (y: number): number => {\n let lo = 0;\n let hi = items.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (offsets[mid] <= y) lo = mid;\n else hi = mid - 1;\n }\n\n return Math.max(0, lo);\n },\n [items.length, offsets],\n );\n\n const start = Math.max(0, indexAt(scrollTop) - overscan);\n const end = Math.min(items.length, indexAt(scrollTop + viewport) + 1 + overscan);\n const visible = useMemo(() => items.slice(start, end), [items, start, end]);\n\n // ---------------------------------------------------------------- measuring\n\n const observer = useRef<ResizeObserver | null>(null);\n\n useEffect(() => {\n // Absent in jsdom and in older runtimes. Without it the list still measures\n // on every render; it just does not notice a row reflowing in place.\n if (typeof ResizeObserver === \"undefined\") return;\n\n const ro = new ResizeObserver(() => setMeasured((v) => v + 1));\n observer.current = ro;\n\n return () => {\n ro.disconnect();\n observer.current = null;\n };\n }, []);\n\n // No dependency array on purpose: every render re-measures what is on screen,\n // and the `changed` guard is what stops it looping. A render that learns\n // nothing new sets no state.\n useLayoutEffect(() => {\n const win = windowRef.current;\n if (!win) return;\n\n let changed = false;\n for (const el of Array.from(win.children) as HTMLElement[]) {\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n observer.current?.observe(el);\n\n const h = el.getBoundingClientRect().height;\n // A zero height is \"not laid out yet\", not a 0px row. Recording it would\n // collapse the list and make every offset after it wrong.\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n const vp = viewportRef.current;\n if (vp && vp.clientHeight > 0 && vp.clientHeight !== clientHeight) setClientHeight(vp.clientHeight);\n\n if (changed) setMeasured((v) => v + 1);\n });\n\n // ------------------------------------------------------------- deep linking\n\n /** The target this component last moved to, to detect that it has settled. */\n const anchored = useRef<number | null>(null);\n const resolved = useRef<string | null>(null);\n\n useEffect(() => {\n // A new id is a new request, even if the old one had resolved.\n anchored.current = null;\n resolved.current = null;\n }, [scrollToId]);\n\n useLayoutEffect(() => {\n if (scrollToId === null || resolved.current === scrollToId) return;\n\n const index = items.findIndex((item) => item.id === scrollToId);\n if (index < 0) {\n // A link to a row that is no longer in the list. Leaving the viewport\n // where it is and SAYING SO is the only honest outcome — scrolling to the\n // top would be indistinguishable from a successful landing.\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, false);\n\n return;\n }\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Clamped the way the browser clamps: a row inside the final viewport-height\n // of the list cannot be brought to the top, and pretending otherwise would\n // leave this waiting for a position that can never be reached.\n const target = Math.max(0, Math.min(offsets[index], Math.max(0, total - viewport)));\n\n if (anchored.current !== target) {\n anchored.current = target;\n vp.scrollTop = target;\n setScrollTop(target);\n\n return;\n }\n\n // The target stopped moving as rows above it were measured, and we are on\n // it. Only now is the deep link actually honoured.\n if (Math.abs(vp.scrollTop - target) <= 1) {\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, true);\n }\n });\n\n // ------------------------------------------------------------- tail-follow\n\n const reportedFollow = useRef(followTail);\n const programmatic = useRef(false);\n\n useEffect(() => {\n // The prop is the truth, so adopt it rather than remembering what we asked\n // for. Otherwise a consumer that declines an `onFollowChange(false)` would\n // never be asked again.\n reportedFollow.current = followTail;\n }, [followTail]);\n\n const askFollow = useCallback(\n (next: boolean) => {\n if (reportedFollow.current === next) return;\n reportedFollow.current = next;\n onFollowChange?.(next);\n },\n [onFollowChange],\n );\n\n useLayoutEffect(() => {\n if (!followTail) return;\n // A pending deep link wins while it resolves; otherwise the two would fight\n // over the same scrollTop and the link would lose.\n if (scrollToId !== null && resolved.current !== scrollToId) return;\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n const bottom = Math.max(0, total - viewport);\n if (Math.abs(vp.scrollTop - bottom) > 0.5) {\n programmatic.current = true;\n vp.scrollTop = bottom;\n setScrollTop(bottom);\n }\n });\n\n const onScroll = useCallback(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n\n const next = vp.scrollTop;\n const previous = scrollTop;\n setScrollTop(next);\n\n const bottom = Math.max(0, total - viewport);\n const wasProgrammatic = programmatic.current;\n programmatic.current = false;\n\n if (next <= bottom - BOTTOM_SLACK) {\n // Moved up, and not because we moved it.\n if (!wasProgrammatic && next < previous) askFollow(false);\n } else {\n askFollow(true);\n }\n }, [scrollTop, total, viewport, askFollow]);\n\n /** Any upward gesture is a statement of intent, whatever scrollTop says. */\n const onWheel = useCallback(\n (e: WheelEvent<HTMLDivElement>) => {\n if (e.deltaY < 0) askFollow(false);\n },\n [askFollow],\n );\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (UPWARD_KEYS.has(e.key)) askFollow(false);\n },\n [askFollow],\n );\n\n const touchStart = useRef<number | null>(null);\n\n const onTouchStart = useCallback((e: TouchEvent<HTMLDivElement>) => {\n touchStart.current = e.touches[0]?.clientY ?? null;\n }, []);\n\n const onTouchMove = useCallback(\n (e: TouchEvent<HTMLDivElement>) => {\n const from = touchStart.current;\n const y = e.touches[0]?.clientY;\n // Finger travelling DOWN drags the content down, which scrolls up.\n if (from !== null && y !== undefined && y - from > TOUCH_SLOP) askFollow(false);\n },\n [askFollow],\n );\n\n // ------------------------------------------------------------ unseen count\n\n const seen = useRef(items.length);\n const reportedUnseen = useRef<number | null>(null);\n\n useEffect(() => {\n const unseen = followTail ? 0 : Math.max(0, items.length - seen.current);\n if (followTail) seen.current = items.length;\n\n if (reportedUnseen.current === unseen) return;\n reportedUnseen.current = unseen;\n onUnseenChange?.(unseen);\n }, [items.length, followTail, onUnseenChange]);\n\n // ------------------------------------------------------------------ render\n\n return (\n <div\n ref={attachViewport}\n data-react-fancy-virtual-list=\"\"\n data-react-fancy-virtual-list-following={followTail ? \"true\" : \"false\"}\n data-react-fancy-virtual-list-rows={items.length}\n role=\"log\"\n tabIndex={0}\n className={cn(\"relative overflow-y-auto overflow-x-hidden outline-none\", className)}\n style={{ height }}\n onScroll={onScroll}\n onWheel={onWheel}\n onKeyDown={onKeyDown}\n onTouchStart={onTouchStart}\n onTouchMove={onTouchMove}\n {...props}\n >\n {/* Holds the full scroll range so the scrollbar is honest from the first\n paint, instead of growing as rows are discovered. */}\n <div\n data-react-fancy-virtual-list-sizer=\"\"\n style={{ height: `${total}px`, position: \"relative\" }}\n >\n <div\n ref={windowRef}\n data-react-fancy-virtual-list-window=\"\"\n data-react-fancy-virtual-list-start={start}\n style={{ transform: `translateY(${offsets[start] ?? 0}px)` }}\n >\n {visible.map((item, i) => (\n <div key={item.id} data-react-fancy-virtual-row-id={item.id}>\n {renderRow(item, start + i)}\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst ROW_ID_ATTR = \"data-react-fancy-virtual-row-id\";\n/** Within this many px of the bottom counts as \"at the bottom\". */\nconst BOTTOM_SLACK = 4;\n/** Ignore a touch jitter smaller than this before calling it an upward drag. */\nconst TOUCH_SLOP = 4;\nconst UPWARD_KEYS = new Set([\"ArrowUp\", \"PageUp\", \"Home\"]);\n"]}
|
package/dist/composer.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk23FTDEGR_cjs = require('./chunk-23FTDEGR.cjs');
|
|
4
4
|
require('./chunk-SNHU2AI4.cjs');
|
|
5
5
|
require('./chunk-ZU5Q77OT.cjs');
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ require('./chunk-ZU5Q77OT.cjs');
|
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(exports, "Composer", {
|
|
10
10
|
enumerable: true,
|
|
11
|
-
get: function () { return
|
|
11
|
+
get: function () { return chunk23FTDEGR_cjs.Composer; }
|
|
12
12
|
});
|
|
13
13
|
//# sourceMappingURL=composer.cjs.map
|
|
14
14
|
//# sourceMappingURL=composer.cjs.map
|
package/dist/composer.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { C as Composer, a as ComposerProps, H as HeldPaste } from './index-
|
|
1
|
+
export { C as Composer, a as ComposerProps, H as HeldPaste } from './index-C8PKWA_5.cjs';
|
|
2
2
|
import 'react';
|
package/dist/composer.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { C as Composer, a as ComposerProps, H as HeldPaste } from './index-
|
|
1
|
+
export { C as Composer, a as ComposerProps, H as HeldPaste } from './index-C8PKWA_5.js';
|
|
2
2
|
import 'react';
|
package/dist/composer.js
CHANGED
package/dist/editor.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkWWUKTXPX_cjs = require('./chunk-WWUKTXPX.cjs');
|
|
4
4
|
require('./chunk-HPNIEYSJ.cjs');
|
|
5
5
|
require('./chunk-T6YELZPJ.cjs');
|
|
6
|
+
require('./chunk-ND5D7GJB.cjs');
|
|
6
7
|
require('./chunk-G7227RTY.cjs');
|
|
7
8
|
require('./chunk-TXTRTTG7.cjs');
|
|
8
9
|
require('./chunk-3WGRVDQR.cjs');
|
|
9
|
-
require('./chunk-ND5D7GJB.cjs');
|
|
10
10
|
require('./chunk-BFKCVSNK.cjs');
|
|
11
11
|
require('./chunk-J2DI6RMR.cjs');
|
|
12
12
|
require('./chunk-L3V3BYVS.cjs');
|
|
@@ -19,11 +19,11 @@ require('./chunk-ZU5Q77OT.cjs');
|
|
|
19
19
|
|
|
20
20
|
Object.defineProperty(exports, "Editor", {
|
|
21
21
|
enumerable: true,
|
|
22
|
-
get: function () { return
|
|
22
|
+
get: function () { return chunkWWUKTXPX_cjs.Editor; }
|
|
23
23
|
});
|
|
24
24
|
Object.defineProperty(exports, "useEditor", {
|
|
25
25
|
enumerable: true,
|
|
26
|
-
get: function () { return
|
|
26
|
+
get: function () { return chunkWWUKTXPX_cjs.useEditor; }
|
|
27
27
|
});
|
|
28
28
|
//# sourceMappingURL=editor.cjs.map
|
|
29
29
|
//# sourceMappingURL=editor.cjs.map
|
package/dist/editor.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { Editor, useEditor } from './chunk-
|
|
1
|
+
export { Editor, useEditor } from './chunk-TX6VUYBC.js';
|
|
2
2
|
import './chunk-FWCREAIT.js';
|
|
3
3
|
import './chunk-CPY7NHXM.js';
|
|
4
|
+
import './chunk-25ZOOUFG.js';
|
|
4
5
|
import './chunk-FBWUXSKH.js';
|
|
5
6
|
import './chunk-5U2DZ7AV.js';
|
|
6
7
|
import './chunk-CHADHN6M.js';
|
|
7
|
-
import './chunk-25ZOOUFG.js';
|
|
8
8
|
import './chunk-XJXQG34J.js';
|
|
9
9
|
import './chunk-MQ6VGG62.js';
|
|
10
10
|
import './chunk-NRXK42LO.js';
|
package/dist/file-upload.cjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkVKXNCYLE_cjs = require('./chunk-VKXNCYLE.cjs');
|
|
4
4
|
require('./chunk-ZU5Q77OT.cjs');
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
Object.defineProperty(exports, "FileUpload", {
|
|
9
9
|
enumerable: true,
|
|
10
|
-
get: function () { return
|
|
10
|
+
get: function () { return chunkVKXNCYLE_cjs.FileUpload; }
|
|
11
11
|
});
|
|
12
12
|
Object.defineProperty(exports, "useFileUpload", {
|
|
13
13
|
enumerable: true,
|
|
14
|
-
get: function () { return
|
|
14
|
+
get: function () { return chunkVKXNCYLE_cjs.useFileUpload; }
|
|
15
15
|
});
|
|
16
16
|
//# sourceMappingURL=file-upload.cjs.map
|
|
17
17
|
//# sourceMappingURL=file-upload.cjs.map
|
package/dist/file-upload.js
CHANGED
|
@@ -90,6 +90,27 @@ interface UsePastePillsResult {
|
|
|
90
90
|
* still closed carries the text.
|
|
91
91
|
*
|
|
92
92
|
* It owns no DOM. The host renders the pills.
|
|
93
|
+
*
|
|
94
|
+
* ---------------------------------------------------------------------------
|
|
95
|
+
* Who actually calls this (as of 5.32.0): `Composer`, and nothing else
|
|
96
|
+
* ---------------------------------------------------------------------------
|
|
97
|
+
*
|
|
98
|
+
* The estate that specified it **withdrew before adopting it**, hours after it
|
|
99
|
+
* shipped — their design moved the pill INTO the message as a `[paste:id|label]`
|
|
100
|
+
* marker in the same grammar as their other object tags, which makes the message
|
|
101
|
+
* itself the record of what is attached. A parallel `heldPastes` array would then be
|
|
102
|
+
* a second place answering "what am I sending?", and the two would disagree the
|
|
103
|
+
* moment someone backspaced a badge. Their reasoning was better than the spec.
|
|
104
|
+
*
|
|
105
|
+
* Recorded because a reader should not infer an extension point under active
|
|
106
|
+
* external use. It is kept rather than withdrawn for two reasons that are about
|
|
107
|
+
* cost, not optimism: it is published API on a 5.x package, so removing it needs a
|
|
108
|
+
* major; and `Composer` calls it, so it is exercised by that component's tests on
|
|
109
|
+
* every run and cannot rot unnoticed.
|
|
110
|
+
*
|
|
111
|
+
* **It gets no further surface without a real caller.** No extra options, no second
|
|
112
|
+
* hook. An API with one hypothetical consumer is how a package accumulates surface
|
|
113
|
+
* nobody uses.
|
|
93
114
|
*/
|
|
94
115
|
declare function usePastePills({ threshold, heldPastes: controlledHeldPastes, onHeldPastesChange, }?: UsePastePillsOptions): UsePastePillsResult;
|
|
95
116
|
|
|
@@ -90,6 +90,27 @@ interface UsePastePillsResult {
|
|
|
90
90
|
* still closed carries the text.
|
|
91
91
|
*
|
|
92
92
|
* It owns no DOM. The host renders the pills.
|
|
93
|
+
*
|
|
94
|
+
* ---------------------------------------------------------------------------
|
|
95
|
+
* Who actually calls this (as of 5.32.0): `Composer`, and nothing else
|
|
96
|
+
* ---------------------------------------------------------------------------
|
|
97
|
+
*
|
|
98
|
+
* The estate that specified it **withdrew before adopting it**, hours after it
|
|
99
|
+
* shipped — their design moved the pill INTO the message as a `[paste:id|label]`
|
|
100
|
+
* marker in the same grammar as their other object tags, which makes the message
|
|
101
|
+
* itself the record of what is attached. A parallel `heldPastes` array would then be
|
|
102
|
+
* a second place answering "what am I sending?", and the two would disagree the
|
|
103
|
+
* moment someone backspaced a badge. Their reasoning was better than the spec.
|
|
104
|
+
*
|
|
105
|
+
* Recorded because a reader should not infer an extension point under active
|
|
106
|
+
* external use. It is kept rather than withdrawn for two reasons that are about
|
|
107
|
+
* cost, not optimism: it is published API on a 5.x package, so removing it needs a
|
|
108
|
+
* major; and `Composer` calls it, so it is exercised by that component's tests on
|
|
109
|
+
* every run and cannot rot unnoticed.
|
|
110
|
+
*
|
|
111
|
+
* **It gets no further surface without a real caller.** No extra options, no second
|
|
112
|
+
* hook. An API with one hypothetical consumer is how a package accumulates surface
|
|
113
|
+
* nobody uses.
|
|
93
114
|
*/
|
|
94
115
|
declare function usePastePills({ threshold, heldPastes: controlledHeldPastes, onHeldPastesChange, }?: UsePastePillsOptions): UsePastePillsResult;
|
|
95
116
|
|