@tensor_1/react 2.31.0 → 2.33.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/README.md +367 -0
- package/dist/SchedulingCalendar-C4XVbqZr.d.ts +339 -0
- package/dist/attachment-labels.d.ts +16 -0
- package/dist/attachment-labels.js +9 -0
- package/dist/calendar-model.d.ts +141 -1
- package/dist/calendar-model.js +200 -11
- package/dist/chart-preset.cjs +127 -7
- package/dist/chart-preset.d.cts +2 -2
- package/dist/chart-preset.d.ts +1 -0
- package/dist/chart-preset.js +6 -1
- package/dist/chart-theme-Cl0IrkKQ.d.cts +112 -0
- package/dist/chart-theme.d.ts +51 -0
- package/dist/chart-theme.js +99 -0
- package/dist/chart.cjs +127 -7
- package/dist/chart.d.cts +2 -2
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +6 -2
- package/dist/components/Alert.d.ts +17 -1
- package/dist/components/Alert.js +9 -4
- package/dist/components/AlertDismiss.d.ts +14 -0
- package/dist/components/AlertDismiss.js +11 -0
- package/dist/components/Attachment.d.ts +15 -12
- package/dist/components/Attachment.js +17 -17
- package/dist/components/AttachmentParts.d.ts +24 -0
- package/dist/components/AttachmentParts.js +20 -0
- package/dist/components/Chart.d.ts +1 -0
- package/dist/components/Chart.js +6 -1
- package/dist/components/ChartCore.d.ts +6 -1
- package/dist/components/ChartCore.js +23 -5
- package/dist/components/Chip.d.ts +17 -2
- package/dist/components/Chip.js +35 -11
- package/dist/components/ChipRemove.d.ts +17 -0
- package/dist/components/ChipRemove.js +13 -0
- package/dist/components/CollapsibleParts.d.ts +29 -0
- package/dist/components/CollapsibleParts.js +20 -0
- package/dist/components/CollapsibleSection.d.ts +30 -3
- package/dist/components/CollapsibleSection.js +7 -11
- package/dist/components/CollapsibleSectionState.d.ts +1 -1
- package/dist/components/CollapsibleSectionState.js +54 -27
- package/dist/components/DashboardGrid.d.ts +60 -0
- package/dist/components/DashboardGrid.js +40 -0
- package/dist/components/DateRangePicker.d.ts +7 -1
- package/dist/components/DateRangePicker.js +5 -1
- package/dist/components/DescriptionList.d.ts +12 -1
- package/dist/components/DescriptionList.js +5 -5
- package/dist/components/Dialogs.d.ts +24 -3
- package/dist/components/Dialogs.js +74 -16
- package/dist/components/FileUpload.d.ts +10 -2
- package/dist/components/FileUpload.js +10 -4
- package/dist/components/FilterEditor.d.ts +4 -0
- package/dist/components/FilterEditor.js +13 -2
- package/dist/components/Heartbeat.d.ts +31 -0
- package/dist/components/Heartbeat.js +28 -0
- package/dist/components/Menu.d.ts +53 -5
- package/dist/components/Menu.js +104 -4
- package/dist/components/Meter.d.ts +7 -3
- package/dist/components/Meter.js +2 -1
- package/dist/components/Popover.d.ts +21 -2
- package/dist/components/Popover.js +94 -17
- package/dist/components/Prose.d.ts +6 -2
- package/dist/components/Prose.js +1 -1
- package/dist/components/RuleBuilder.d.ts +52 -4
- package/dist/components/RuleBuilder.js +109 -24
- package/dist/components/SchedulingCalendar.d.ts +4 -110
- package/dist/components/SchedulingCalendar.js +299 -63
- package/dist/components/SchedulingTimeGrid.d.ts +4 -0
- package/dist/components/SchedulingTimeGrid.js +521 -0
- package/dist/components/SchedulingTimeline.d.ts +2 -1
- package/dist/components/Segmented.d.ts +17 -3
- package/dist/components/Segmented.js +59 -5
- package/dist/components/Select.d.ts +66 -2
- package/dist/components/Select.js +800 -10
- package/dist/components/Spinner.d.ts +2 -1
- package/dist/components/Spinner.js +1 -1
- package/dist/components/StatusPill.d.ts +8 -2
- package/dist/components/StatusPill.js +2 -2
- package/dist/components/Steps.d.ts +28 -4
- package/dist/components/Steps.js +46 -16
- package/dist/components/TableControls.js +4 -3
- package/dist/components/Tabs.d.ts +7 -2
- package/dist/components/Tabs.js +14 -1
- package/dist/components/TextDiff.d.ts +49 -0
- package/dist/components/TextDiff.js +124 -0
- package/dist/{emoji-image-6mPZ15Zy.d.cts → emoji-image-DjCKpdwE.d.cts} +1 -1
- package/dist/emoji.cjs +169 -27
- package/dist/emoji.d.cts +3 -3
- package/dist/file-model.d.ts +22 -3
- package/dist/file-model.js +6 -4
- package/dist/filter-model.d.ts +26 -2
- package/dist/filter-model.js +13 -1
- package/dist/hooks/useAnchoredPosition.d.ts +24 -1
- package/dist/hooks/useAnchoredPosition.js +58 -8
- package/dist/hooks/useVirtualRows.d.ts +17 -2
- package/dist/hooks/useVirtualRows.js +6 -5
- package/dist/icons/Icon.d.ts +14 -14
- package/dist/index.cjs +4888 -1714
- package/dist/index.d.cts +1871 -658
- package/dist/index.d.ts +16 -9
- package/dist/index.js +67 -4
- package/dist/labels-context.d.ts +29 -1
- package/dist/{overlay-position-Cjv5E7mn.d.cts → overlay-position-2Afke51l.d.cts} +20 -0
- package/dist/overlay-position.d.ts +20 -0
- package/dist/overlay-position.js +17 -2
- package/dist/rich-text.cjs +169 -27
- package/dist/rich-text.d.cts +3 -3
- package/dist/rule-builder-model.d.ts +34 -2
- package/dist/rule-builder-model.js +48 -12
- package/dist/scheduling-calendar-model.d.ts +169 -0
- package/dist/scheduling-calendar-model.js +176 -0
- package/dist/select-model.d.ts +80 -0
- package/dist/select-model.js +109 -0
- package/dist/table-engine.d.ts +7 -0
- package/dist/table-engine.js +2 -1
- package/dist/text-diff-model.d.ts +71 -0
- package/dist/text-diff-model.js +261 -0
- package/package.json +1 -1
- package/dist/ChartCore-DzWe10bN.d.cts +0 -59
package/dist/emoji.cjs
CHANGED
|
@@ -83,7 +83,7 @@ function fitsOn(side, a, f, vp, offset, padding) {
|
|
|
83
83
|
return spaceOn(side, a, vp, offset) >= need + padding;
|
|
84
84
|
}
|
|
85
85
|
function computePosition(anchor, floating, viewport, options = {}) {
|
|
86
|
-
const { placement = "bottom-start", offset = 6, padding = 8, flip = true, shift = true, stickySide } = options;
|
|
86
|
+
const { placement = "bottom-start", offset = 6, padding = 8, flip = true, shift = true, shiftMainAxis = true, stickySide } = options;
|
|
87
87
|
const { side: preferred, align } = parsePlacement(placement);
|
|
88
88
|
let side = preferred;
|
|
89
89
|
const sticky = stickySide === preferred || stickySide === OPPOSITE[preferred] ? stickySide : void 0;
|
|
@@ -116,12 +116,27 @@ function computePosition(anchor, floating, viewport, options = {}) {
|
|
|
116
116
|
y = align === "start" ? start : align === "end" ? end : center;
|
|
117
117
|
if (shift) y = clamp(y, padding, Math.max(padding, viewport.height - floating.height - padding));
|
|
118
118
|
}
|
|
119
|
+
const mainAxisNatural = horizontal ? y : x;
|
|
120
|
+
const anchorInView = horizontal ? anchor.y + anchor.height > 0 && anchor.y < viewport.height : anchor.x + anchor.width > 0 && anchor.x < viewport.width;
|
|
121
|
+
let mainAxisRange = null;
|
|
122
|
+
if (shiftMainAxis && anchorInView) {
|
|
123
|
+
const extent = horizontal ? viewport.height - floating.height : viewport.width - floating.width;
|
|
124
|
+
mainAxisRange = [padding, Math.max(padding, extent - padding)];
|
|
125
|
+
if (horizontal) y = clamp(y, mainAxisRange[0], mainAxisRange[1]);
|
|
126
|
+
else x = clamp(x, mainAxisRange[0], mainAxisRange[1]);
|
|
127
|
+
}
|
|
119
128
|
return {
|
|
120
129
|
x,
|
|
121
130
|
y,
|
|
122
131
|
placement: align === "center" ? side : `${side}-${align}`,
|
|
123
132
|
side,
|
|
124
|
-
align
|
|
133
|
+
align,
|
|
134
|
+
mainAxisNatural,
|
|
135
|
+
mainAxisRange,
|
|
136
|
+
available: {
|
|
137
|
+
width: Math.max(0, viewport.width - 2 * padding),
|
|
138
|
+
height: Math.max(0, viewport.height - 2 * padding)
|
|
139
|
+
}
|
|
125
140
|
};
|
|
126
141
|
}
|
|
127
142
|
|
|
@@ -140,6 +155,7 @@ function supportsAnchoring() {
|
|
|
140
155
|
}
|
|
141
156
|
return anchoringSupported;
|
|
142
157
|
}
|
|
158
|
+
var ANCHOR_BAND = 64;
|
|
143
159
|
var anchorCount = 0;
|
|
144
160
|
var namesOf = (value) => value.split(",").map((n) => n.trim()).filter((n) => n.startsWith("--"));
|
|
145
161
|
function addAnchorName(el, name) {
|
|
@@ -164,9 +180,12 @@ function translationOf(el) {
|
|
|
164
180
|
}
|
|
165
181
|
var signed = (n) => `${n < 0 ? "-" : "+"} ${Math.round(Math.abs(n) * 100) / 100}px`;
|
|
166
182
|
var prepared = /* @__PURE__ */ new WeakSet();
|
|
167
|
-
function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
|
|
168
|
-
const opts = (0, import_react.useRef)({ placement, offset, padding });
|
|
169
|
-
opts.current = { placement, offset, padding };
|
|
183
|
+
function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8, shiftMainAxis = true, capHeight = false, onAnchorHidden }) {
|
|
184
|
+
const opts = (0, import_react.useRef)({ placement, offset, padding, shiftMainAxis, capHeight });
|
|
185
|
+
opts.current = { placement, offset, padding, shiftMainAxis, capHeight };
|
|
186
|
+
const onAnchorHiddenRef = (0, import_react.useRef)(onAnchorHidden);
|
|
187
|
+
onAnchorHiddenRef.current = onAnchorHidden;
|
|
188
|
+
const heightCap = (0, import_react.useRef)(null);
|
|
170
189
|
const session = (0, import_react.useRef)(false);
|
|
171
190
|
const side = (0, import_react.useRef)(null);
|
|
172
191
|
const written = (0, import_react.useRef)("");
|
|
@@ -190,6 +209,15 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
190
209
|
}
|
|
191
210
|
removeAnchorName(current.anchor, current.name);
|
|
192
211
|
}, [floatingRef]);
|
|
212
|
+
const uncap = (0, import_react.useCallback)(() => {
|
|
213
|
+
const cap = heightCap.current;
|
|
214
|
+
if (!cap) return;
|
|
215
|
+
heightCap.current = null;
|
|
216
|
+
const floating = floatingRef.current;
|
|
217
|
+
if (!floating) return;
|
|
218
|
+
floating.style.maxHeight = cap.inlineMaxHeight;
|
|
219
|
+
if (cap.setOverflow) floating.style.overflowY = cap.inlineOverflowY;
|
|
220
|
+
}, [floatingRef]);
|
|
193
221
|
const reposition = (0, import_react.useCallback)(() => {
|
|
194
222
|
const anchorEl = resolveAnchor(anchor);
|
|
195
223
|
const floating = floatingRef.current;
|
|
@@ -202,14 +230,36 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
202
230
|
prepared.add(floating);
|
|
203
231
|
written.current = "";
|
|
204
232
|
}
|
|
233
|
+
const { placement: p, offset: o, padding: pad, shiftMainAxis: keepInView, capHeight: cap } = opts.current;
|
|
234
|
+
if (cap) {
|
|
235
|
+
if (!heightCap.current) {
|
|
236
|
+
const own = getComputedStyle(floating);
|
|
237
|
+
const setOverflow = !own.overflowY || own.overflowY === "visible";
|
|
238
|
+
heightCap.current = {
|
|
239
|
+
inlineMaxHeight: s.maxHeight,
|
|
240
|
+
inlineOverflowY: s.overflowY,
|
|
241
|
+
ownMaxHeight: own.maxHeight && own.maxHeight !== "none" ? own.maxHeight : "",
|
|
242
|
+
setOverflow,
|
|
243
|
+
written: ""
|
|
244
|
+
};
|
|
245
|
+
if (setOverflow) s.overflowY = "auto";
|
|
246
|
+
}
|
|
247
|
+
const room = `${Math.max(0, window.innerHeight - 2 * pad)}px`;
|
|
248
|
+
const max = heightCap.current.ownMaxHeight ? `min(${room}, ${heightCap.current.ownMaxHeight})` : room;
|
|
249
|
+
if (max !== heightCap.current.written) {
|
|
250
|
+
s.maxHeight = max;
|
|
251
|
+
heightCap.current.written = max;
|
|
252
|
+
}
|
|
253
|
+
} else if (heightCap.current) {
|
|
254
|
+
uncap();
|
|
255
|
+
}
|
|
205
256
|
const a = anchorEl.getBoundingClientRect();
|
|
206
257
|
const size = { width: floating.offsetWidth, height: floating.offsetHeight };
|
|
207
|
-
const { placement: p, offset: o, padding: pad } = opts.current;
|
|
208
258
|
const pos = computePosition(
|
|
209
259
|
{ x: a.x, y: a.y, width: a.width, height: a.height },
|
|
210
260
|
size,
|
|
211
261
|
{ width: window.innerWidth, height: window.innerHeight },
|
|
212
|
-
{ placement: p, offset: o, padding: pad, stickySide: side.current ?? void 0 }
|
|
262
|
+
{ placement: p, offset: o, padding: pad, shiftMainAxis: keepInView, stickySide: side.current ?? void 0 }
|
|
213
263
|
);
|
|
214
264
|
const measured = size.width > 0 || size.height > 0;
|
|
215
265
|
if (measured && session.current) side.current = pos.side;
|
|
@@ -223,7 +273,10 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
223
273
|
s.top = top2;
|
|
224
274
|
written.current = key2;
|
|
225
275
|
};
|
|
226
|
-
|
|
276
|
+
const limit = pos.mainAxisRange;
|
|
277
|
+
const nearLimit = limit !== null && (pos.mainAxisNatural < limit[0] + ANCHOR_BAND || pos.mainAxisNatural > limit[1] - ANCHOR_BAND);
|
|
278
|
+
if (anchoringFailed.current || nearLimit || !supportsAnchoring()) {
|
|
279
|
+
if (anchoring.current) release(false);
|
|
227
280
|
writeFixed(pos);
|
|
228
281
|
return;
|
|
229
282
|
}
|
|
@@ -260,7 +313,7 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
260
313
|
writeFixed(pos);
|
|
261
314
|
}
|
|
262
315
|
}
|
|
263
|
-
}, [anchor, floatingRef, release]);
|
|
316
|
+
}, [anchor, floatingRef, release, uncap]);
|
|
264
317
|
useIsomorphicLayoutEffect(() => {
|
|
265
318
|
if (!open) return;
|
|
266
319
|
session.current = true;
|
|
@@ -289,8 +342,20 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
289
342
|
side.current = null;
|
|
290
343
|
anchoringFailed.current = false;
|
|
291
344
|
release(true);
|
|
345
|
+
uncap();
|
|
292
346
|
};
|
|
293
|
-
}, [open, reposition, anchor, floatingRef, release]);
|
|
347
|
+
}, [open, reposition, anchor, floatingRef, release, uncap]);
|
|
348
|
+
const watchAnchor = open && !!onAnchorHidden;
|
|
349
|
+
(0, import_react.useEffect)(() => {
|
|
350
|
+
const anchorEl = resolveAnchor(anchor);
|
|
351
|
+
if (!watchAnchor || !anchorEl || typeof IntersectionObserver !== "function") return;
|
|
352
|
+
const observer = new IntersectionObserver((entries) => {
|
|
353
|
+
const latest = entries[entries.length - 1];
|
|
354
|
+
if (latest && !latest.isIntersecting) onAnchorHiddenRef.current?.();
|
|
355
|
+
});
|
|
356
|
+
observer.observe(anchorEl);
|
|
357
|
+
return () => observer.disconnect();
|
|
358
|
+
}, [watchAnchor, anchor]);
|
|
294
359
|
(0, import_react.useEffect)(() => () => release(false), [release]);
|
|
295
360
|
return reposition;
|
|
296
361
|
}
|
|
@@ -312,16 +377,22 @@ var settle = (el) => {
|
|
|
312
377
|
el.removeAttribute(PLACING);
|
|
313
378
|
};
|
|
314
379
|
function Popover({
|
|
315
|
-
popover
|
|
380
|
+
popover: popoverProp,
|
|
316
381
|
anchor,
|
|
317
382
|
placement = "bottom-start",
|
|
318
383
|
offset = 6,
|
|
319
384
|
closeWhenAnchorHidden = false,
|
|
320
385
|
onAnchorHidden,
|
|
386
|
+
capHeight = false,
|
|
387
|
+
openOnHover = false,
|
|
321
388
|
className,
|
|
322
389
|
children,
|
|
323
390
|
...props
|
|
324
391
|
}) {
|
|
392
|
+
const hover = !!openOnHover && !!anchor;
|
|
393
|
+
const popover = popoverProp ?? (hover ? "manual" : "auto");
|
|
394
|
+
const openDelay = typeof openOnHover === "object" ? openOnHover.openDelay ?? 300 : 300;
|
|
395
|
+
const closeDelay = typeof openOnHover === "object" ? openOnHover.closeDelay ?? 150 : 150;
|
|
325
396
|
const ref = (0, import_react2.useRef)(null);
|
|
326
397
|
const [open, setOpen] = (0, import_react2.useState)(false);
|
|
327
398
|
const openRef = (0, import_react2.useRef)(false);
|
|
@@ -331,7 +402,16 @@ function Popover({
|
|
|
331
402
|
const reposition = useAnchoredPosition(anchor ?? null, ref, {
|
|
332
403
|
open: open && !!anchor,
|
|
333
404
|
placement,
|
|
334
|
-
offset
|
|
405
|
+
offset,
|
|
406
|
+
capHeight,
|
|
407
|
+
onAnchorHidden: closeWhenAnchorHidden ? () => {
|
|
408
|
+
const el = ref.current;
|
|
409
|
+
if (!el || !isPopoverOpen(el)) return;
|
|
410
|
+
onAnchorHiddenRef.current?.();
|
|
411
|
+
const active = el.ownerDocument.activeElement;
|
|
412
|
+
if (active && el.contains(active)) active.blur();
|
|
413
|
+
el.hidePopover();
|
|
414
|
+
} : void 0
|
|
335
415
|
});
|
|
336
416
|
(0, import_react2.useEffect)(() => {
|
|
337
417
|
const el = ref.current;
|
|
@@ -366,25 +446,87 @@ function Popover({
|
|
|
366
446
|
el.removeAttribute(PLACING);
|
|
367
447
|
};
|
|
368
448
|
}, [anchor, reposition]);
|
|
449
|
+
(0, import_react2.useEffect)(() => {
|
|
450
|
+
const el = ref.current;
|
|
451
|
+
const anchorEl = resolveAnchor(anchor);
|
|
452
|
+
if (!hover || !el || !anchorEl) return;
|
|
453
|
+
let timer;
|
|
454
|
+
const inside = (node) => !!node && (anchorEl.contains(node) || el.contains(node));
|
|
455
|
+
const stillWanted = () => {
|
|
456
|
+
try {
|
|
457
|
+
return anchorEl.matches(":hover") || el.matches(":hover") || inside(el.ownerDocument.activeElement);
|
|
458
|
+
} catch {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
const show = (delay) => {
|
|
463
|
+
window.clearTimeout(timer);
|
|
464
|
+
timer = window.setTimeout(() => {
|
|
465
|
+
if (!isPopoverOpen(el)) {
|
|
466
|
+
try {
|
|
467
|
+
el.showPopover?.();
|
|
468
|
+
} catch {
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}, delay);
|
|
472
|
+
};
|
|
473
|
+
const hide = (delay) => {
|
|
474
|
+
window.clearTimeout(timer);
|
|
475
|
+
timer = window.setTimeout(() => {
|
|
476
|
+
if (isPopoverOpen(el) && !stillWanted()) {
|
|
477
|
+
try {
|
|
478
|
+
el.hidePopover?.();
|
|
479
|
+
} catch {
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}, delay);
|
|
483
|
+
};
|
|
484
|
+
const onEnter = (event) => {
|
|
485
|
+
if (event.pointerType !== "touch") show(openDelay);
|
|
486
|
+
};
|
|
487
|
+
const onLeave = (event) => {
|
|
488
|
+
if (event.pointerType !== "touch") hide(closeDelay);
|
|
489
|
+
};
|
|
490
|
+
const onFocusIn = () => show(0);
|
|
491
|
+
const onFocusOut = (event) => {
|
|
492
|
+
if (!inside(event.relatedTarget)) hide(closeDelay);
|
|
493
|
+
};
|
|
494
|
+
const onKeyDown = (event) => {
|
|
495
|
+
if (event.key !== "Escape" || !isPopoverOpen(el)) return;
|
|
496
|
+
window.clearTimeout(timer);
|
|
497
|
+
const hadFocus = el.contains(el.ownerDocument.activeElement);
|
|
498
|
+
try {
|
|
499
|
+
el.hidePopover?.();
|
|
500
|
+
} catch {
|
|
501
|
+
}
|
|
502
|
+
if (hadFocus) {
|
|
503
|
+
anchorEl.removeEventListener("focusin", onFocusIn);
|
|
504
|
+
anchorEl.focus();
|
|
505
|
+
anchorEl.addEventListener("focusin", onFocusIn);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
for (const node of [anchorEl, el]) {
|
|
509
|
+
node.addEventListener("pointerenter", onEnter);
|
|
510
|
+
node.addEventListener("pointerleave", onLeave);
|
|
511
|
+
node.addEventListener("focusout", onFocusOut);
|
|
512
|
+
node.addEventListener("keydown", onKeyDown);
|
|
513
|
+
}
|
|
514
|
+
anchorEl.addEventListener("focusin", onFocusIn);
|
|
515
|
+
return () => {
|
|
516
|
+
window.clearTimeout(timer);
|
|
517
|
+
for (const node of [anchorEl, el]) {
|
|
518
|
+
node.removeEventListener("pointerenter", onEnter);
|
|
519
|
+
node.removeEventListener("pointerleave", onLeave);
|
|
520
|
+
node.removeEventListener("focusout", onFocusOut);
|
|
521
|
+
node.removeEventListener("keydown", onKeyDown);
|
|
522
|
+
}
|
|
523
|
+
anchorEl.removeEventListener("focusin", onFocusIn);
|
|
524
|
+
};
|
|
525
|
+
}, [hover, anchor, openDelay, closeDelay]);
|
|
369
526
|
useIsomorphicLayoutEffect2(() => {
|
|
370
527
|
openRef.current = open;
|
|
371
528
|
if (open && ref.current) settle(ref.current);
|
|
372
529
|
}, [open]);
|
|
373
|
-
(0, import_react2.useEffect)(() => {
|
|
374
|
-
const el = ref.current;
|
|
375
|
-
const anchorEl = resolveAnchor(anchor);
|
|
376
|
-
if (!closeWhenAnchorHidden || !open || !el || !anchorEl || typeof IntersectionObserver !== "function") return;
|
|
377
|
-
const observer = new IntersectionObserver((entries) => {
|
|
378
|
-
const latest = entries[entries.length - 1];
|
|
379
|
-
if (!latest || latest.isIntersecting || !isPopoverOpen(el)) return;
|
|
380
|
-
onAnchorHiddenRef.current?.();
|
|
381
|
-
const active = el.ownerDocument.activeElement;
|
|
382
|
-
if (active && el.contains(active)) active.blur();
|
|
383
|
-
el.hidePopover();
|
|
384
|
-
});
|
|
385
|
-
observer.observe(anchorEl);
|
|
386
|
-
return () => observer.disconnect();
|
|
387
|
-
}, [closeWhenAnchorHidden, open, anchor]);
|
|
388
530
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, className: cx("uix-popover", className), ...popoverAttr, ...props, children });
|
|
389
531
|
}
|
|
390
532
|
|
package/dist/emoji.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { E as EmojiPickerLabels, a as EmojiLocale, b as EmojiDataLoader } from './emoji-image-
|
|
2
|
-
export { C as CompactEmoji, D as DEFAULT_EMOJI_PICKER_LABELS, c as EMOJI_GRID_COLUMNS, d as EmojiData, e as EmojiEntry, f as EmojiGroup, g as EmojiMessages, h as EmojiPicker, i as EmojiPickerProps, j as buildEmojiData, k as canRenderEmoji, l as emojiImageFileName, m as loadEmojiData, n as normalizeEmojiImageBaseUrl, s as searchEmoji } from './emoji-image-
|
|
3
|
-
export { A as Align, P as Placement, S as Side } from './overlay-position-
|
|
1
|
+
import { E as EmojiPickerLabels, a as EmojiLocale, b as EmojiDataLoader } from './emoji-image-DjCKpdwE.cjs';
|
|
2
|
+
export { C as CompactEmoji, D as DEFAULT_EMOJI_PICKER_LABELS, c as EMOJI_GRID_COLUMNS, d as EmojiData, e as EmojiEntry, f as EmojiGroup, g as EmojiMessages, h as EmojiPicker, i as EmojiPickerProps, j as buildEmojiData, k as canRenderEmoji, l as emojiImageFileName, m as loadEmojiData, n as normalizeEmojiImageBaseUrl, s as searchEmoji } from './emoji-image-DjCKpdwE.cjs';
|
|
3
|
+
export { A as Align, P as Placement, S as Side } from './overlay-position-2Afke51l.cjs';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
|
|
6
6
|
interface ReactionSummary {
|
package/dist/file-model.d.ts
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
/** File helpers shared by `FileUpload` and `Attachment` (HAR-984/985). Pure. */
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
interface FileSizeOptions {
|
|
3
|
+
/**
|
|
4
|
+
* `1000` (default): decimal, as file managers on macOS and most of the web show sizes
|
|
5
|
+
* (1 MB = 1,000,000 bytes). `1024`: binary, as Windows and many servers count them
|
|
6
|
+
* (1 MB = 1,048,576 bytes) — use it when the rest of the product does, so one file never
|
|
7
|
+
* shows two sizes (HAR-1630).
|
|
8
|
+
*/
|
|
9
|
+
base?: 1000 | 1024;
|
|
10
|
+
/**
|
|
11
|
+
* The five unit labels, bytes first. Default `['B', 'KB', 'MB', 'GB', 'TB']` for both bases;
|
|
12
|
+
* pass `['B', 'KiB', 'MiB', 'GiB', 'TiB']` to mark base 1024 the IEC way, or localised ones.
|
|
13
|
+
*/
|
|
14
|
+
units?: readonly [string, string, string, string, string];
|
|
15
|
+
}
|
|
16
|
+
/** A product's own size formatter for `FileUpload` and `Attachment` (`formatSize`). */
|
|
17
|
+
type FileSizeFormatter = (bytes: number, locale?: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* "1.2 MB", "340 KB", "12 B" in the locale's number format. Base 1000 unless `options.base`
|
|
20
|
+
* is 1024.
|
|
21
|
+
*/
|
|
22
|
+
declare function formatFileSize(bytes: number, locale?: string, options?: FileSizeOptions): string;
|
|
4
23
|
/** Whether a file matches an `accept` list (".pdf,image/*,application/zip"). An empty list accepts all. */
|
|
5
24
|
declare function fileMatchesAccept(file: {
|
|
6
25
|
name: string;
|
|
@@ -34,4 +53,4 @@ declare function fileKind(file: {
|
|
|
34
53
|
type?: string;
|
|
35
54
|
}): 'image' | 'video' | 'pdf' | 'sheet' | 'archive' | 'text' | 'other';
|
|
36
55
|
|
|
37
|
-
export { type FileRejection, type FileRejectionReason, fileKind, fileMatchesAccept, formatFileSize, partitionFiles };
|
|
56
|
+
export { type FileRejection, type FileRejectionReason, type FileSizeFormatter, type FileSizeOptions, fileKind, fileMatchesAccept, formatFileSize, partitionFiles };
|
package/dist/file-model.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
const UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
2
|
+
function formatFileSize(bytes, locale, options = {}) {
|
|
2
3
|
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
3
|
-
const
|
|
4
|
+
const base = options.base === 1024 ? 1024 : 1e3;
|
|
5
|
+
const units = options.units ?? UNITS;
|
|
4
6
|
let value = bytes;
|
|
5
7
|
let unit = 0;
|
|
6
|
-
while (value >=
|
|
7
|
-
value /=
|
|
8
|
+
while (value >= base && unit < units.length - 1) {
|
|
9
|
+
value /= base;
|
|
8
10
|
unit += 1;
|
|
9
11
|
}
|
|
10
12
|
const digits = unit === 0 || value >= 100 ? 0 : 1;
|
package/dist/filter-model.d.ts
CHANGED
|
@@ -33,6 +33,16 @@ type FilterValue = {
|
|
|
33
33
|
values: FilterChoice[];
|
|
34
34
|
};
|
|
35
35
|
type FilterValueKind = FilterValue['kind'];
|
|
36
|
+
/**
|
|
37
|
+
* A named selection for an `enum` field (HAR-1505), e.g. "Open work" = new, open and pending.
|
|
38
|
+
* The id, label and values come from the product.
|
|
39
|
+
*/
|
|
40
|
+
interface FilterPreset {
|
|
41
|
+
id: string;
|
|
42
|
+
label: string;
|
|
43
|
+
/** The option values the preset selects. Compared as a set: order and repeats do not matter. */
|
|
44
|
+
values: readonly string[];
|
|
45
|
+
}
|
|
36
46
|
interface FilterField {
|
|
37
47
|
id: string;
|
|
38
48
|
/** Column name, also the start of the chip summary. */
|
|
@@ -49,6 +59,12 @@ interface FilterField {
|
|
|
49
59
|
/** `boolean`: words for true and false, e.g. Escalated / Not escalated. Default from labels. */
|
|
50
60
|
trueLabel?: string;
|
|
51
61
|
falseLabel?: string;
|
|
62
|
+
/**
|
|
63
|
+
* `enum`: named selections shown as toggle chips above the options. Activating one replaces
|
|
64
|
+
* the selection with its values; the one equal to the selection is pressed, and the chip
|
|
65
|
+
* summary names it.
|
|
66
|
+
*/
|
|
67
|
+
presets?: readonly FilterPreset[];
|
|
52
68
|
}
|
|
53
69
|
interface FilterSummaryLabels {
|
|
54
70
|
operators: Record<TextFilterOperator | NumberFilterOperator, string>;
|
|
@@ -62,6 +78,8 @@ interface FilterSummaryLabels {
|
|
|
62
78
|
until: string;
|
|
63
79
|
/** More than `maxListed` values: `{first}`, `{count}`. */
|
|
64
80
|
more: string;
|
|
81
|
+
/** An `enum` value equal to a preset: `{label}` (the preset), `{field}`. Default `{label}`. */
|
|
82
|
+
preset?: string;
|
|
65
83
|
}
|
|
66
84
|
declare const DEFAULT_FILTER_SUMMARY_LABELS: FilterSummaryLabels;
|
|
67
85
|
declare const TEXT_FILTER_OPERATORS: readonly TextFilterOperator[];
|
|
@@ -70,6 +88,11 @@ declare const NUMBER_FILTER_OPERATORS: readonly NumberFilterOperator[];
|
|
|
70
88
|
declare function emptyFilterValue(kind: FilterValueKind): FilterValue;
|
|
71
89
|
/** True when the value filters nothing (so the chip goes away). */
|
|
72
90
|
declare function isFilterEmpty(value: FilterValue | undefined): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* The first of `field.presets` whose values equal the value's as a set, or undefined. Only an
|
|
93
|
+
* `enum` value can match a preset.
|
|
94
|
+
*/
|
|
95
|
+
declare function matchFilterPreset(field: FilterField, value: FilterValue | undefined): FilterPreset | undefined;
|
|
73
96
|
interface SummarizeFilterOptions {
|
|
74
97
|
labels?: Partial<FilterSummaryLabels>;
|
|
75
98
|
/** Formats a `YYYY-MM-DD` date (a product format such as DD.MM.YYYY). Default: as given. */
|
|
@@ -81,8 +104,9 @@ interface SummarizeFilterOptions {
|
|
|
81
104
|
}
|
|
82
105
|
/**
|
|
83
106
|
* One line for the filter chip, e.g. "State: Open, Pending", "Priority ≥ 2", "Created:
|
|
84
|
-
* 01.10.2026 – 05.10.2026", "Escalated: Yes".
|
|
107
|
+
* 01.10.2026 – 05.10.2026", "Escalated: Yes". An enum value equal to one of `field.presets`
|
|
108
|
+
* gives that preset's label (the `preset` label). Empty values give an empty string.
|
|
85
109
|
*/
|
|
86
110
|
declare function summarizeFilter(field: FilterField, value: FilterValue | undefined, options?: SummarizeFilterOptions): string;
|
|
87
111
|
|
|
88
|
-
export { DEFAULT_FILTER_SUMMARY_LABELS, type FilterChoice, type FilterField, type FilterSummaryLabels, type FilterValue, type FilterValueKind, NUMBER_FILTER_OPERATORS, type NumberFilterOperator, type SummarizeFilterOptions, TEXT_FILTER_OPERATORS, type TextFilterOperator, emptyFilterValue, isFilterEmpty, summarizeFilter };
|
|
112
|
+
export { DEFAULT_FILTER_SUMMARY_LABELS, type FilterChoice, type FilterField, type FilterPreset, type FilterSummaryLabels, type FilterValue, type FilterValueKind, NUMBER_FILTER_OPERATORS, type NumberFilterOperator, type SummarizeFilterOptions, TEXT_FILTER_OPERATORS, type TextFilterOperator, emptyFilterValue, isFilterEmpty, matchFilterPreset, summarizeFilter };
|
package/dist/filter-model.js
CHANGED
|
@@ -18,7 +18,8 @@ const DEFAULT_FILTER_SUMMARY_LABELS = {
|
|
|
18
18
|
range: "{from} \u2013 {to}",
|
|
19
19
|
from: "from {from}",
|
|
20
20
|
until: "until {to}",
|
|
21
|
-
more: "{first} +{count}"
|
|
21
|
+
more: "{first} +{count}",
|
|
22
|
+
preset: "{label}"
|
|
22
23
|
};
|
|
23
24
|
const TEXT_FILTER_OPERATORS = ["contains", "not-contains", "is", "is-not", "starts-with"];
|
|
24
25
|
const NUMBER_FILTER_OPERATORS = ["eq", "neq", "lt", "lte", "gt", "gte", "between"];
|
|
@@ -54,6 +55,14 @@ function isFilterEmpty(value) {
|
|
|
54
55
|
return value.value === void 0;
|
|
55
56
|
}
|
|
56
57
|
}
|
|
58
|
+
function matchFilterPreset(field, value) {
|
|
59
|
+
if (value?.kind !== "enum" || !field.presets?.length) return void 0;
|
|
60
|
+
const chosen = new Set(value.values);
|
|
61
|
+
return field.presets.find((preset) => {
|
|
62
|
+
const wanted = new Set(preset.values);
|
|
63
|
+
return wanted.size === chosen.size && [...wanted].every((v) => chosen.has(v));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
57
66
|
const fill = (template, values) => template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
|
|
58
67
|
function summarizeFilter(field, value, options = {}) {
|
|
59
68
|
if (!value || isFilterEmpty(value)) return "";
|
|
@@ -66,6 +75,8 @@ function summarizeFilter(field, value, options = {}) {
|
|
|
66
75
|
};
|
|
67
76
|
switch (value.kind) {
|
|
68
77
|
case "enum": {
|
|
78
|
+
const preset = matchFilterPreset(field, value);
|
|
79
|
+
if (preset) return fill(labels.preset ?? DEFAULT_FILTER_SUMMARY_LABELS.preset, { label: preset.label, field: field.label });
|
|
69
80
|
const byValue = new Map((field.options ?? []).map((o) => [o.value, o.label]));
|
|
70
81
|
return `${field.label}: ${list(value.values.map((v) => byValue.get(v) ?? v))}`;
|
|
71
82
|
}
|
|
@@ -87,5 +98,6 @@ export {
|
|
|
87
98
|
TEXT_FILTER_OPERATORS,
|
|
88
99
|
emptyFilterValue,
|
|
89
100
|
isFilterEmpty,
|
|
101
|
+
matchFilterPreset,
|
|
90
102
|
summarizeFilter
|
|
91
103
|
};
|
|
@@ -7,6 +7,25 @@ interface UseAnchoredPositionOptions {
|
|
|
7
7
|
placement?: Placement;
|
|
8
8
|
offset?: number;
|
|
9
9
|
padding?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Keep the element inside the viewport on the side it hangs off as well: when it fits neither
|
|
12
|
+
* above nor below (or left nor right) it slides over the anchor instead of leaving the
|
|
13
|
+
* viewport. Once the anchor itself has left the viewport the element follows it out; pair
|
|
14
|
+
* that with `onAnchorHidden` to close it. Default true.
|
|
15
|
+
*/
|
|
16
|
+
shiftMainAxis?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Cap the element's height to the viewport minus the padding on both edges, and let it scroll
|
|
19
|
+
* (`overflow-y: auto`, unless its own CSS already sets an overflow). A `max-height` from the
|
|
20
|
+
* element's own CSS still applies when it is smaller. Default false.
|
|
21
|
+
*/
|
|
22
|
+
capHeight?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Called when the anchor has scrolled completely out of view (outside the viewport or a
|
|
25
|
+
* clipping ancestor) while `open`. Close the overlay here; `Popover closeWhenAnchorHidden`
|
|
26
|
+
* is built on it.
|
|
27
|
+
*/
|
|
28
|
+
onAnchorHidden?: () => void;
|
|
10
29
|
}
|
|
11
30
|
type AnchorArg = RefObject<HTMLElement | null> | HTMLElement | null | undefined;
|
|
12
31
|
/** @internal The element behind an anchor ref or element. */
|
|
@@ -22,9 +41,13 @@ declare const resolveAnchor: (a: AnchorArg) => HTMLElement | null;
|
|
|
22
41
|
* `position: fixed` + left/top and follows scroll/resize from JS. While `open`, the side
|
|
23
42
|
* chosen at open is kept until it no longer fits and the other side does, scrolls inside
|
|
24
43
|
* the overlay itself are ignored, and a size change of either element re-places it.
|
|
44
|
+
* An element that fits on neither side is kept inside the viewport (it covers the anchor).
|
|
45
|
+
* While it is held there, or within `ANCHOR_BAND` of the limit, it is written as fixed
|
|
46
|
+
* coordinates even where anchor positioning exists: those cannot drift while the page scrolls,
|
|
47
|
+
* so it is inside on every frame, not one frame later (HAR-1613).
|
|
25
48
|
* Returns a `reposition` fn for callers that want to nudge it manually (e.g. right when a
|
|
26
49
|
* native popover's `toggle` fires).
|
|
27
50
|
*/
|
|
28
|
-
declare function useAnchoredPosition(anchor: RefObject<HTMLElement | null> | HTMLElement | null | undefined, floatingRef: RefObject<HTMLElement | null>, { open, placement, offset, padding }: UseAnchoredPositionOptions): () => void;
|
|
51
|
+
declare function useAnchoredPosition(anchor: RefObject<HTMLElement | null> | HTMLElement | null | undefined, floatingRef: RefObject<HTMLElement | null>, { open, placement, offset, padding, shiftMainAxis, capHeight, onAnchorHidden }: UseAnchoredPositionOptions): () => void;
|
|
29
52
|
|
|
30
53
|
export { type UseAnchoredPositionOptions, resolveAnchor, useAnchoredPosition };
|
|
@@ -15,6 +15,7 @@ function supportsAnchoring() {
|
|
|
15
15
|
}
|
|
16
16
|
return anchoringSupported;
|
|
17
17
|
}
|
|
18
|
+
const ANCHOR_BAND = 64;
|
|
18
19
|
let anchorCount = 0;
|
|
19
20
|
const namesOf = (value) => value.split(",").map((n) => n.trim()).filter((n) => n.startsWith("--"));
|
|
20
21
|
function addAnchorName(el, name) {
|
|
@@ -39,9 +40,12 @@ function translationOf(el) {
|
|
|
39
40
|
}
|
|
40
41
|
const signed = (n) => `${n < 0 ? "-" : "+"} ${Math.round(Math.abs(n) * 100) / 100}px`;
|
|
41
42
|
const prepared = /* @__PURE__ */ new WeakSet();
|
|
42
|
-
function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8 }) {
|
|
43
|
-
const opts = useRef({ placement, offset, padding });
|
|
44
|
-
opts.current = { placement, offset, padding };
|
|
43
|
+
function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8, shiftMainAxis = true, capHeight = false, onAnchorHidden }) {
|
|
44
|
+
const opts = useRef({ placement, offset, padding, shiftMainAxis, capHeight });
|
|
45
|
+
opts.current = { placement, offset, padding, shiftMainAxis, capHeight };
|
|
46
|
+
const onAnchorHiddenRef = useRef(onAnchorHidden);
|
|
47
|
+
onAnchorHiddenRef.current = onAnchorHidden;
|
|
48
|
+
const heightCap = useRef(null);
|
|
45
49
|
const session = useRef(false);
|
|
46
50
|
const side = useRef(null);
|
|
47
51
|
const written = useRef("");
|
|
@@ -65,6 +69,15 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
65
69
|
}
|
|
66
70
|
removeAnchorName(current.anchor, current.name);
|
|
67
71
|
}, [floatingRef]);
|
|
72
|
+
const uncap = useCallback(() => {
|
|
73
|
+
const cap = heightCap.current;
|
|
74
|
+
if (!cap) return;
|
|
75
|
+
heightCap.current = null;
|
|
76
|
+
const floating = floatingRef.current;
|
|
77
|
+
if (!floating) return;
|
|
78
|
+
floating.style.maxHeight = cap.inlineMaxHeight;
|
|
79
|
+
if (cap.setOverflow) floating.style.overflowY = cap.inlineOverflowY;
|
|
80
|
+
}, [floatingRef]);
|
|
68
81
|
const reposition = useCallback(() => {
|
|
69
82
|
const anchorEl = resolveAnchor(anchor);
|
|
70
83
|
const floating = floatingRef.current;
|
|
@@ -77,14 +90,36 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
77
90
|
prepared.add(floating);
|
|
78
91
|
written.current = "";
|
|
79
92
|
}
|
|
93
|
+
const { placement: p, offset: o, padding: pad, shiftMainAxis: keepInView, capHeight: cap } = opts.current;
|
|
94
|
+
if (cap) {
|
|
95
|
+
if (!heightCap.current) {
|
|
96
|
+
const own = getComputedStyle(floating);
|
|
97
|
+
const setOverflow = !own.overflowY || own.overflowY === "visible";
|
|
98
|
+
heightCap.current = {
|
|
99
|
+
inlineMaxHeight: s.maxHeight,
|
|
100
|
+
inlineOverflowY: s.overflowY,
|
|
101
|
+
ownMaxHeight: own.maxHeight && own.maxHeight !== "none" ? own.maxHeight : "",
|
|
102
|
+
setOverflow,
|
|
103
|
+
written: ""
|
|
104
|
+
};
|
|
105
|
+
if (setOverflow) s.overflowY = "auto";
|
|
106
|
+
}
|
|
107
|
+
const room = `${Math.max(0, window.innerHeight - 2 * pad)}px`;
|
|
108
|
+
const max = heightCap.current.ownMaxHeight ? `min(${room}, ${heightCap.current.ownMaxHeight})` : room;
|
|
109
|
+
if (max !== heightCap.current.written) {
|
|
110
|
+
s.maxHeight = max;
|
|
111
|
+
heightCap.current.written = max;
|
|
112
|
+
}
|
|
113
|
+
} else if (heightCap.current) {
|
|
114
|
+
uncap();
|
|
115
|
+
}
|
|
80
116
|
const a = anchorEl.getBoundingClientRect();
|
|
81
117
|
const size = { width: floating.offsetWidth, height: floating.offsetHeight };
|
|
82
|
-
const { placement: p, offset: o, padding: pad } = opts.current;
|
|
83
118
|
const pos = computePosition(
|
|
84
119
|
{ x: a.x, y: a.y, width: a.width, height: a.height },
|
|
85
120
|
size,
|
|
86
121
|
{ width: window.innerWidth, height: window.innerHeight },
|
|
87
|
-
{ placement: p, offset: o, padding: pad, stickySide: side.current ?? void 0 }
|
|
122
|
+
{ placement: p, offset: o, padding: pad, shiftMainAxis: keepInView, stickySide: side.current ?? void 0 }
|
|
88
123
|
);
|
|
89
124
|
const measured = size.width > 0 || size.height > 0;
|
|
90
125
|
if (measured && session.current) side.current = pos.side;
|
|
@@ -98,7 +133,10 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
98
133
|
s.top = top2;
|
|
99
134
|
written.current = key2;
|
|
100
135
|
};
|
|
101
|
-
|
|
136
|
+
const limit = pos.mainAxisRange;
|
|
137
|
+
const nearLimit = limit !== null && (pos.mainAxisNatural < limit[0] + ANCHOR_BAND || pos.mainAxisNatural > limit[1] - ANCHOR_BAND);
|
|
138
|
+
if (anchoringFailed.current || nearLimit || !supportsAnchoring()) {
|
|
139
|
+
if (anchoring.current) release(false);
|
|
102
140
|
writeFixed(pos);
|
|
103
141
|
return;
|
|
104
142
|
}
|
|
@@ -135,7 +173,7 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
135
173
|
writeFixed(pos);
|
|
136
174
|
}
|
|
137
175
|
}
|
|
138
|
-
}, [anchor, floatingRef, release]);
|
|
176
|
+
}, [anchor, floatingRef, release, uncap]);
|
|
139
177
|
useIsomorphicLayoutEffect(() => {
|
|
140
178
|
if (!open) return;
|
|
141
179
|
session.current = true;
|
|
@@ -164,8 +202,20 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
164
202
|
side.current = null;
|
|
165
203
|
anchoringFailed.current = false;
|
|
166
204
|
release(true);
|
|
205
|
+
uncap();
|
|
167
206
|
};
|
|
168
|
-
}, [open, reposition, anchor, floatingRef, release]);
|
|
207
|
+
}, [open, reposition, anchor, floatingRef, release, uncap]);
|
|
208
|
+
const watchAnchor = open && !!onAnchorHidden;
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
const anchorEl = resolveAnchor(anchor);
|
|
211
|
+
if (!watchAnchor || !anchorEl || typeof IntersectionObserver !== "function") return;
|
|
212
|
+
const observer = new IntersectionObserver((entries) => {
|
|
213
|
+
const latest = entries[entries.length - 1];
|
|
214
|
+
if (latest && !latest.isIntersecting) onAnchorHiddenRef.current?.();
|
|
215
|
+
});
|
|
216
|
+
observer.observe(anchorEl);
|
|
217
|
+
return () => observer.disconnect();
|
|
218
|
+
}, [watchAnchor, anchor]);
|
|
169
219
|
useEffect(() => () => release(false), [release]);
|
|
170
220
|
return reposition;
|
|
171
221
|
}
|
|
@@ -5,8 +5,21 @@ interface UseVirtualRowsOptions {
|
|
|
5
5
|
rowHeight: number;
|
|
6
6
|
/** Rows rendered beyond each viewport edge. Default: 6. */
|
|
7
7
|
overscan?: number;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Render all rows at or below this count: rows are windowed only when
|
|
10
|
+
* `rows.length > threshold` (so exactly `threshold` rows are still rendered whole).
|
|
11
|
+
* Default: 100.
|
|
12
|
+
*/
|
|
9
13
|
threshold?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Viewport height in pixels to assume until the scroll element has been measured, and
|
|
16
|
+
* whenever it measures 0 (first render, a hidden tab, a test without layout). With it, a
|
|
17
|
+
* 5,000-row table mounts with one window of rows; without it every row is rendered until
|
|
18
|
+
* the first measurement, as before (HAR-1616).
|
|
19
|
+
*/
|
|
20
|
+
estimatedViewportHeight?: number;
|
|
21
|
+
/** `false` renders every row with no spacers, whatever the count. Default: `true`. */
|
|
22
|
+
enabled?: boolean;
|
|
10
23
|
}
|
|
11
24
|
interface UseVirtualRowsResult<T> {
|
|
12
25
|
/** Attach to the scrolling TableWrap. */
|
|
@@ -21,7 +34,9 @@ interface UseVirtualRowsResult<T> {
|
|
|
21
34
|
/**
|
|
22
35
|
* Owns scroll/resize observation and exposes the small visible row window.
|
|
23
36
|
* Consumers render padTop/padBottom spacer rows inside their existing tbody.
|
|
37
|
+
* When the row count shrinks under a large scroll position the window is clamped to the new
|
|
38
|
+
* count in the same render, so `padTop` never exceeds the list (HAR-1616).
|
|
24
39
|
*/
|
|
25
|
-
declare function useVirtualRows<T>(rows: readonly T[], { rowHeight, overscan, threshold }: UseVirtualRowsOptions): UseVirtualRowsResult<T>;
|
|
40
|
+
declare function useVirtualRows<T>(rows: readonly T[], { rowHeight, overscan, threshold, estimatedViewportHeight, enabled }: UseVirtualRowsOptions): UseVirtualRowsResult<T>;
|
|
26
41
|
|
|
27
42
|
export { type UseVirtualRowsOptions, type UseVirtualRowsResult, useVirtualRows };
|