@tensor_1/react 2.30.0 → 2.32.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 +430 -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 +58 -0
- package/dist/components/Attachment.js +69 -0
- 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/EntityPicker.d.ts +56 -0
- package/dist/components/EntityPicker.js +168 -0
- package/dist/components/FileIcon.d.ts +9 -0
- package/dist/components/FileIcon.js +17 -0
- package/dist/components/FileUpload.d.ts +95 -0
- package/dist/components/FileUpload.js +145 -0
- package/dist/components/FilterEditor.d.ts +55 -0
- package/dist/components/FilterEditor.js +186 -0
- package/dist/components/Heartbeat.d.ts +31 -0
- package/dist/components/Heartbeat.js +28 -0
- package/dist/components/Lightbox.d.ts +45 -0
- package/dist/components/Lightbox.js +102 -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 +142 -11
- package/dist/components/SchedulingCalendar.js +234 -57
- package/dist/components/SchedulingTimeline.d.ts +1 -0
- package/dist/components/SearchSuggest.d.ts +6 -2
- package/dist/components/SearchSuggest.js +4 -0
- 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 +56 -0
- package/dist/file-model.js +52 -0
- package/dist/filter-model.d.ts +112 -0
- package/dist/filter-model.js +103 -0
- 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 +4938 -1631
- package/dist/index.d.cts +1622 -178
- package/dist/index.d.ts +20 -6
- package/dist/index.js +88 -4
- package/dist/labels-context.d.ts +28 -0
- 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 +66 -0
- package/dist/scheduling-calendar-model.js +82 -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 {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** File helpers shared by `FileUpload` and `Attachment` (HAR-984/985). Pure. */
|
|
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;
|
|
23
|
+
/** Whether a file matches an `accept` list (".pdf,image/*,application/zip"). An empty list accepts all. */
|
|
24
|
+
declare function fileMatchesAccept(file: {
|
|
25
|
+
name: string;
|
|
26
|
+
type?: string;
|
|
27
|
+
}, accept?: string): boolean;
|
|
28
|
+
type FileRejectionReason = 'type' | 'size' | 'count';
|
|
29
|
+
interface FileRejection<F = File> {
|
|
30
|
+
file: F;
|
|
31
|
+
reason: FileRejectionReason;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Splits picked files into accepted and rejected (wrong type, over `maxSize` bytes, or beyond
|
|
35
|
+
* `maxFiles` counting the `existing` ones). Order is kept.
|
|
36
|
+
*/
|
|
37
|
+
declare function partitionFiles<F extends {
|
|
38
|
+
name: string;
|
|
39
|
+
type?: string;
|
|
40
|
+
size: number;
|
|
41
|
+
}>(files: readonly F[], { accept, maxSize, maxFiles, existing }: {
|
|
42
|
+
accept?: string;
|
|
43
|
+
maxSize?: number;
|
|
44
|
+
maxFiles?: number;
|
|
45
|
+
existing?: number;
|
|
46
|
+
}): {
|
|
47
|
+
accepted: F[];
|
|
48
|
+
rejected: FileRejection<F>[];
|
|
49
|
+
};
|
|
50
|
+
/** A coarse kind for a file icon. */
|
|
51
|
+
declare function fileKind(file: {
|
|
52
|
+
name: string;
|
|
53
|
+
type?: string;
|
|
54
|
+
}): 'image' | 'video' | 'pdf' | 'sheet' | 'archive' | 'text' | 'other';
|
|
55
|
+
|
|
56
|
+
export { type FileRejection, type FileRejectionReason, type FileSizeFormatter, type FileSizeOptions, fileKind, fileMatchesAccept, formatFileSize, partitionFiles };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const UNITS = ["B", "KB", "MB", "GB", "TB"];
|
|
2
|
+
function formatFileSize(bytes, locale, options = {}) {
|
|
3
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
4
|
+
const base = options.base === 1024 ? 1024 : 1e3;
|
|
5
|
+
const units = options.units ?? UNITS;
|
|
6
|
+
let value = bytes;
|
|
7
|
+
let unit = 0;
|
|
8
|
+
while (value >= base && unit < units.length - 1) {
|
|
9
|
+
value /= base;
|
|
10
|
+
unit += 1;
|
|
11
|
+
}
|
|
12
|
+
const digits = unit === 0 || value >= 100 ? 0 : 1;
|
|
13
|
+
return `${new Intl.NumberFormat(locale, { maximumFractionDigits: digits, minimumFractionDigits: 0 }).format(value)} ${units[unit]}`;
|
|
14
|
+
}
|
|
15
|
+
function fileMatchesAccept(file, accept) {
|
|
16
|
+
if (!accept || !accept.trim()) return true;
|
|
17
|
+
const name = file.name.toLowerCase();
|
|
18
|
+
const type = (file.type ?? "").toLowerCase();
|
|
19
|
+
return accept.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean).some((token) => {
|
|
20
|
+
if (token.startsWith(".")) return name.endsWith(token);
|
|
21
|
+
if (token.endsWith("/*")) return type.startsWith(token.slice(0, -1));
|
|
22
|
+
return type === token;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function partitionFiles(files, { accept, maxSize, maxFiles, existing = 0 }) {
|
|
26
|
+
const accepted = [];
|
|
27
|
+
const rejected = [];
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
if (!fileMatchesAccept(file, accept)) rejected.push({ file, reason: "type" });
|
|
30
|
+
else if (maxSize !== void 0 && file.size > maxSize) rejected.push({ file, reason: "size" });
|
|
31
|
+
else if (maxFiles !== void 0 && existing + accepted.length >= maxFiles) rejected.push({ file, reason: "count" });
|
|
32
|
+
else accepted.push(file);
|
|
33
|
+
}
|
|
34
|
+
return { accepted, rejected };
|
|
35
|
+
}
|
|
36
|
+
function fileKind(file) {
|
|
37
|
+
const type = (file.type ?? "").toLowerCase();
|
|
38
|
+
const ext = file.name.toLowerCase().split(".").pop() ?? "";
|
|
39
|
+
if (type.startsWith("image/") || ["png", "jpg", "jpeg", "gif", "webp", "svg", "heic"].includes(ext)) return "image";
|
|
40
|
+
if (type.startsWith("video/") || ["mp4", "mov", "webm"].includes(ext)) return "video";
|
|
41
|
+
if (type === "application/pdf" || ext === "pdf") return "pdf";
|
|
42
|
+
if (["xls", "xlsx", "csv", "ods"].includes(ext) || type.includes("spreadsheet") || type === "text/csv") return "sheet";
|
|
43
|
+
if (["zip", "7z", "gz", "tar", "rar"].includes(ext) || type.includes("zip")) return "archive";
|
|
44
|
+
if (type.startsWith("text/") || ["txt", "md", "log", "json", "xml", "doc", "docx", "odt", "rtf"].includes(ext)) return "text";
|
|
45
|
+
return "other";
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
fileKind,
|
|
49
|
+
fileMatchesAccept,
|
|
50
|
+
formatFileSize,
|
|
51
|
+
partitionFiles
|
|
52
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed column filters for `FilterEditor` (HAR-1365; TENSOR C6 `table-filter-editor.tsx`,
|
|
3
|
+
* MOTUS C-1 "+ filter"). The value is data the product serializes into its own query; the
|
|
4
|
+
* summary feeds a `Chip`. Pure, so a server can summarize a saved view too.
|
|
5
|
+
*/
|
|
6
|
+
interface FilterChoice {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
}
|
|
10
|
+
type TextFilterOperator = 'contains' | 'not-contains' | 'is' | 'is-not' | 'starts-with';
|
|
11
|
+
type NumberFilterOperator = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'between';
|
|
12
|
+
type FilterValue = {
|
|
13
|
+
kind: 'enum';
|
|
14
|
+
values: string[];
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'text';
|
|
17
|
+
operator: TextFilterOperator;
|
|
18
|
+
text: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'number';
|
|
21
|
+
operator: NumberFilterOperator;
|
|
22
|
+
value?: number;
|
|
23
|
+
to?: number;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'date-range';
|
|
26
|
+
from?: string;
|
|
27
|
+
to?: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'boolean';
|
|
30
|
+
value?: boolean;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'reference';
|
|
33
|
+
values: FilterChoice[];
|
|
34
|
+
};
|
|
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
|
+
}
|
|
46
|
+
interface FilterField {
|
|
47
|
+
id: string;
|
|
48
|
+
/** Column name, also the start of the chip summary. */
|
|
49
|
+
label: string;
|
|
50
|
+
kind: FilterValueKind;
|
|
51
|
+
/** `enum`: the choices. */
|
|
52
|
+
options?: readonly FilterChoice[];
|
|
53
|
+
/** `reference`: finds records to pick; rejected promises show the error state. */
|
|
54
|
+
onSearch?: (query: string) => Promise<readonly FilterChoice[]>;
|
|
55
|
+
/** `text` / `number`: the operators offered, in order. Default: all of the kind. */
|
|
56
|
+
operators?: readonly (TextFilterOperator | NumberFilterOperator)[];
|
|
57
|
+
/** `number`: a unit shown after the value, e.g. "days". */
|
|
58
|
+
unit?: string;
|
|
59
|
+
/** `boolean`: words for true and false, e.g. Escalated / Not escalated. Default from labels. */
|
|
60
|
+
trueLabel?: string;
|
|
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[];
|
|
68
|
+
}
|
|
69
|
+
interface FilterSummaryLabels {
|
|
70
|
+
operators: Record<TextFilterOperator | NumberFilterOperator, string>;
|
|
71
|
+
yes: string;
|
|
72
|
+
no: string;
|
|
73
|
+
/** `{from}`, `{to}`; also used for a number range. */
|
|
74
|
+
range: string;
|
|
75
|
+
/** Only a start date. `{from}`. */
|
|
76
|
+
from: string;
|
|
77
|
+
/** Only an end date. `{to}`. */
|
|
78
|
+
until: string;
|
|
79
|
+
/** More than `maxListed` values: `{first}`, `{count}`. */
|
|
80
|
+
more: string;
|
|
81
|
+
/** An `enum` value equal to a preset: `{label}` (the preset), `{field}`. Default `{label}`. */
|
|
82
|
+
preset?: string;
|
|
83
|
+
}
|
|
84
|
+
declare const DEFAULT_FILTER_SUMMARY_LABELS: FilterSummaryLabels;
|
|
85
|
+
declare const TEXT_FILTER_OPERATORS: readonly TextFilterOperator[];
|
|
86
|
+
declare const NUMBER_FILTER_OPERATORS: readonly NumberFilterOperator[];
|
|
87
|
+
/** The empty value for a field kind (what Clear resets to). */
|
|
88
|
+
declare function emptyFilterValue(kind: FilterValueKind): FilterValue;
|
|
89
|
+
/** True when the value filters nothing (so the chip goes away). */
|
|
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;
|
|
96
|
+
interface SummarizeFilterOptions {
|
|
97
|
+
labels?: Partial<FilterSummaryLabels>;
|
|
98
|
+
/** Formats a `YYYY-MM-DD` date (a product format such as DD.MM.YYYY). Default: as given. */
|
|
99
|
+
formatDate?: (date: string) => string;
|
|
100
|
+
/** Formats a number. Default: `String`. */
|
|
101
|
+
formatNumber?: (value: number) => string;
|
|
102
|
+
/** List this many values before "+N". Default 2. */
|
|
103
|
+
maxListed?: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* One line for the filter chip, e.g. "State: Open, Pending", "Priority ≥ 2", "Created:
|
|
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.
|
|
109
|
+
*/
|
|
110
|
+
declare function summarizeFilter(field: FilterField, value: FilterValue | undefined, options?: SummarizeFilterOptions): string;
|
|
111
|
+
|
|
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 };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const DEFAULT_FILTER_SUMMARY_LABELS = {
|
|
2
|
+
operators: {
|
|
3
|
+
contains: "contains",
|
|
4
|
+
"not-contains": "does not contain",
|
|
5
|
+
is: "is",
|
|
6
|
+
"is-not": "is not",
|
|
7
|
+
"starts-with": "starts with",
|
|
8
|
+
eq: "=",
|
|
9
|
+
neq: "\u2260",
|
|
10
|
+
lt: "<",
|
|
11
|
+
lte: "\u2264",
|
|
12
|
+
gt: ">",
|
|
13
|
+
gte: "\u2265",
|
|
14
|
+
between: "between"
|
|
15
|
+
},
|
|
16
|
+
yes: "Yes",
|
|
17
|
+
no: "No",
|
|
18
|
+
range: "{from} \u2013 {to}",
|
|
19
|
+
from: "from {from}",
|
|
20
|
+
until: "until {to}",
|
|
21
|
+
more: "{first} +{count}",
|
|
22
|
+
preset: "{label}"
|
|
23
|
+
};
|
|
24
|
+
const TEXT_FILTER_OPERATORS = ["contains", "not-contains", "is", "is-not", "starts-with"];
|
|
25
|
+
const NUMBER_FILTER_OPERATORS = ["eq", "neq", "lt", "lte", "gt", "gte", "between"];
|
|
26
|
+
function emptyFilterValue(kind) {
|
|
27
|
+
switch (kind) {
|
|
28
|
+
case "enum":
|
|
29
|
+
return { kind, values: [] };
|
|
30
|
+
case "text":
|
|
31
|
+
return { kind, operator: "contains", text: "" };
|
|
32
|
+
case "number":
|
|
33
|
+
return { kind, operator: "eq" };
|
|
34
|
+
case "date-range":
|
|
35
|
+
return { kind };
|
|
36
|
+
case "boolean":
|
|
37
|
+
return { kind };
|
|
38
|
+
case "reference":
|
|
39
|
+
return { kind, values: [] };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function isFilterEmpty(value) {
|
|
43
|
+
if (!value) return true;
|
|
44
|
+
switch (value.kind) {
|
|
45
|
+
case "enum":
|
|
46
|
+
case "reference":
|
|
47
|
+
return value.values.length === 0;
|
|
48
|
+
case "text":
|
|
49
|
+
return value.text.trim() === "";
|
|
50
|
+
case "number":
|
|
51
|
+
return value.value === void 0 || value.operator === "between" && value.to === void 0;
|
|
52
|
+
case "date-range":
|
|
53
|
+
return !value.from && !value.to;
|
|
54
|
+
case "boolean":
|
|
55
|
+
return value.value === void 0;
|
|
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
|
+
}
|
|
66
|
+
const fill = (template, values) => template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
|
|
67
|
+
function summarizeFilter(field, value, options = {}) {
|
|
68
|
+
if (!value || isFilterEmpty(value)) return "";
|
|
69
|
+
const labels = { ...DEFAULT_FILTER_SUMMARY_LABELS, ...options.labels, operators: { ...DEFAULT_FILTER_SUMMARY_LABELS.operators, ...options.labels?.operators } };
|
|
70
|
+
const date = options.formatDate ?? ((d) => d);
|
|
71
|
+
const num = (n) => `${options.formatNumber?.(n) ?? String(n)}${field.unit ? ` ${field.unit}` : ""}`;
|
|
72
|
+
const list = (items) => {
|
|
73
|
+
const max = Math.max(1, options.maxListed ?? 2);
|
|
74
|
+
return items.length > max ? fill(labels.more, { first: items.slice(0, max).join(", "), count: items.length - max }) : items.join(", ");
|
|
75
|
+
};
|
|
76
|
+
switch (value.kind) {
|
|
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 });
|
|
80
|
+
const byValue = new Map((field.options ?? []).map((o) => [o.value, o.label]));
|
|
81
|
+
return `${field.label}: ${list(value.values.map((v) => byValue.get(v) ?? v))}`;
|
|
82
|
+
}
|
|
83
|
+
case "reference":
|
|
84
|
+
return `${field.label}: ${list(value.values.map((v) => v.label))}`;
|
|
85
|
+
case "text":
|
|
86
|
+
return `${field.label} ${labels.operators[value.operator]} \u201C${value.text.trim()}\u201D`;
|
|
87
|
+
case "number":
|
|
88
|
+
return value.operator === "between" ? `${field.label}: ${fill(labels.range, { from: num(value.value), to: num(value.to) })}` : `${field.label} ${labels.operators[value.operator]} ${num(value.value)}`;
|
|
89
|
+
case "date-range":
|
|
90
|
+
return `${field.label}: ${value.from && value.to ? fill(labels.range, { from: date(value.from), to: date(value.to) }) : value.from ? fill(labels.from, { from: date(value.from) }) : fill(labels.until, { to: date(value.to) })}`;
|
|
91
|
+
case "boolean":
|
|
92
|
+
return `${field.label}: ${value.value ? field.trueLabel ?? labels.yes : field.falseLabel ?? labels.no}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export {
|
|
96
|
+
DEFAULT_FILTER_SUMMARY_LABELS,
|
|
97
|
+
NUMBER_FILTER_OPERATORS,
|
|
98
|
+
TEXT_FILTER_OPERATORS,
|
|
99
|
+
emptyFilterValue,
|
|
100
|
+
isFilterEmpty,
|
|
101
|
+
matchFilterPreset,
|
|
102
|
+
summarizeFilter
|
|
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 };
|