@tensor_1/react 2.47.0 → 2.49.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 +540 -2
- package/dist/{SchedulingCalendar-B6TakO2h.d.ts → SchedulingCalendar-WWyFL3Eo.d.ts} +51 -2
- package/dist/announce.d.ts +10 -0
- package/dist/announce.js +30 -0
- package/dist/chart-analytic.cjs +33 -25
- package/dist/chart-preset.cjs +23 -15
- package/dist/chart.cjs +23 -15
- package/dist/combobox-model.d.ts +10 -1
- package/dist/combobox-model.js +15 -0
- package/dist/command-palette-model.d.ts +44 -0
- package/dist/command-palette-model.js +36 -0
- package/dist/components/AccountMenu.d.ts +44 -0
- package/dist/components/AccountMenu.js +43 -0
- package/dist/components/AppShell.d.ts +45 -2
- package/dist/components/AppShell.js +116 -5
- package/dist/components/AuditLog.d.ts +128 -0
- package/dist/components/AuditLog.js +196 -0
- package/dist/components/BrandProfiles.js +4 -3
- package/dist/components/Breadcrumbs.d.ts +9 -2
- package/dist/components/Breadcrumbs.js +3 -2
- package/dist/components/Button.d.ts +12 -5
- package/dist/components/Button.js +7 -3
- package/dist/components/Card.d.ts +18 -1
- package/dist/components/Card.js +5 -1
- package/dist/components/ChangeHighlight.d.ts +30 -0
- package/dist/components/ChangeHighlight.js +21 -0
- package/dist/components/ChartCore.js +2 -1
- package/dist/components/Checkbox.d.ts +27 -1
- package/dist/components/Checkbox.js +31 -2
- package/dist/components/Chip.d.ts +7 -3
- package/dist/components/Chip.js +2 -2
- package/dist/components/CodeDisplay.d.ts +65 -0
- package/dist/components/CodeDisplay.js +124 -0
- package/dist/components/Combobox.d.ts +27 -6
- package/dist/components/Combobox.js +195 -58
- package/dist/components/CommandPalette.d.ts +36 -1
- package/dist/components/CommandPalette.js +254 -2
- package/dist/components/ContactCard.d.ts +66 -0
- package/dist/components/ContactCard.js +74 -0
- package/dist/components/Count.d.ts +24 -0
- package/dist/components/Count.js +32 -0
- package/dist/components/DashboardCanvas.js +14 -0
- package/dist/components/DataWorkspace.d.ts +167 -0
- package/dist/components/DataWorkspace.js +380 -0
- package/dist/components/DateFieldParts.d.ts +2 -1
- package/dist/components/DateFieldParts.js +7 -0
- package/dist/components/DatePicker.d.ts +9 -2
- package/dist/components/DatePicker.js +67 -28
- package/dist/components/DateRangePicker.d.ts +12 -2
- package/dist/components/DateRangePicker.js +13 -12
- package/dist/components/DetailPage.d.ts +7 -1
- package/dist/components/DetailPage.js +2 -1
- package/dist/components/Dialogs.d.ts +71 -10
- package/dist/components/Dialogs.js +130 -30
- package/dist/components/DiffViewer.d.ts +23 -3
- package/dist/components/DiffViewer.js +26 -12
- package/dist/components/DiscardPrompt.js +16 -0
- package/dist/components/DisplayPreferences.d.ts +83 -0
- package/dist/components/DisplayPreferences.js +185 -0
- package/dist/components/EditorialHome.d.ts +23 -3
- package/dist/components/EditorialHome.js +16 -11
- package/dist/components/EntityPicker.d.ts +20 -3
- package/dist/components/EntityPicker.js +32 -13
- package/dist/components/FilterEditor.d.ts +18 -4
- package/dist/components/FilterEditor.js +141 -19
- package/dist/components/FocusOnMount.d.ts +282 -0
- package/dist/components/FocusOnMount.js +12 -0
- package/dist/components/InfoTip.js +11 -12
- package/dist/components/InlineEdit.d.ts +87 -20
- package/dist/components/InlineEdit.js +197 -54
- package/dist/components/Input.d.ts +12 -2
- package/dist/components/Input.js +5 -3
- package/dist/components/Kanban.d.ts +14 -3
- package/dist/components/Kanban.js +9 -4
- package/dist/components/Kbd.d.ts +6 -1
- package/dist/components/Kbd.js +6 -6
- package/dist/components/Lightbox.d.ts +41 -5
- package/dist/components/Lightbox.js +95 -9
- package/dist/components/MatchReview.d.ts +40 -1
- package/dist/components/MatchReview.js +130 -4
- package/dist/components/MetricInput.js +3 -2
- package/dist/components/Modal.d.ts +2 -1
- package/dist/components/Modal.js +13 -4
- package/dist/components/MotionList.d.ts +303 -0
- package/dist/components/MotionList.js +175 -0
- package/dist/components/NavFavourites.d.ts +5 -0
- package/dist/components/NavFavourites.js +7 -7
- package/dist/components/OtpInput.d.ts +33 -0
- package/dist/components/OtpInput.js +123 -0
- package/dist/components/PageHeader.d.ts +7 -1
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/Pagination.js +6 -5
- package/dist/components/PasswordInput.d.ts +34 -0
- package/dist/components/PasswordInput.js +101 -0
- package/dist/components/Peek.js +3 -3
- package/dist/components/Popover.js +11 -0
- package/dist/components/PrintLayout.d.ts +63 -0
- package/dist/components/PrintLayout.js +114 -0
- package/dist/components/QrCode.d.ts +47 -0
- package/dist/components/QrCode.js +55 -0
- package/dist/components/QrScanner.d.ts +95 -0
- package/dist/components/QrScanner.js +266 -0
- package/dist/components/RelationshipGraph.d.ts +32 -1
- package/dist/components/RelationshipGraph.js +22 -6
- package/dist/components/RelationshipGraphLayered.d.ts +1 -1
- package/dist/components/RelationshipGraphLayered.js +268 -118
- package/dist/components/RelationshipGraphList.js +33 -6
- package/dist/components/RuleBuilder.js +39 -7
- package/dist/components/SchedulingAgenda.d.ts +3 -1
- package/dist/components/SchedulingCalendar.d.ts +3 -1
- package/dist/components/SchedulingCalendar.js +43 -4
- package/dist/components/SchedulingResourceDay.d.ts +57 -0
- package/dist/components/SchedulingResourceDay.js +287 -0
- package/dist/components/SchedulingTimeGrid.d.ts +3 -1
- package/dist/components/SchedulingTimeline.d.ts +3 -1
- package/dist/components/SearchSuggest.d.ts +23 -3
- package/dist/components/SearchSuggest.js +56 -18
- package/dist/components/Segmented.d.ts +39 -4
- package/dist/components/Segmented.js +39 -5
- package/dist/components/Select.d.ts +3 -2
- package/dist/components/Select.js +7 -1
- package/dist/components/ShortcutHint.d.ts +11 -10
- package/dist/components/ShortcutHint.js +44 -38
- package/dist/components/Sidebar.d.ts +36 -4
- package/dist/components/Sidebar.js +37 -12
- package/dist/components/SortableList.d.ts +45 -0
- package/dist/components/SortableList.js +147 -0
- package/dist/components/Stat.d.ts +28 -3
- package/dist/components/Stat.js +13 -3
- package/dist/components/States.d.ts +33 -1
- package/dist/components/States.js +43 -1
- package/dist/components/StatusPill.d.ts +14 -3
- package/dist/components/StatusPill.js +11 -3
- package/dist/components/Steps.js +2 -1
- package/dist/components/Table.d.ts +9 -2
- package/dist/components/Table.js +4 -2
- package/dist/components/TableControls.js +18 -58
- package/dist/components/TableOverflowProbe.d.ts +12 -0
- package/dist/components/TableOverflowProbe.js +27 -0
- package/dist/components/Tabs.d.ts +2 -1
- package/dist/components/Tabs.js +10 -2
- package/dist/components/TagInput.d.ts +67 -0
- package/dist/components/TagInput.js +342 -0
- package/dist/components/Textarea.d.ts +5 -0
- package/dist/components/Textarea.js +2 -2
- package/dist/components/TimePicker.d.ts +80 -0
- package/dist/components/TimePicker.js +347 -0
- package/dist/components/Toast.d.ts +1 -1
- package/dist/components/Toast.js +109 -4
- package/dist/components/Tooltip.d.ts +8 -4
- package/dist/components/Tooltip.js +86 -28
- package/dist/components/Tree.d.ts +8 -0
- package/dist/components/Tree.js +11 -5
- package/dist/display-preferences-model.d.ts +53 -0
- package/dist/display-preferences-model.js +64 -0
- package/dist/emoji.cjs +143 -49
- package/dist/filter-model.d.ts +59 -3
- package/dist/filter-model.js +67 -2
- package/dist/hooks/useAnchoredPosition.js +14 -2
- package/dist/hooks/useChangeHighlight.d.ts +33 -0
- package/dist/hooks/useChangeHighlight.js +44 -0
- package/dist/hooks/useDeferredReorder.d.ts +32 -0
- package/dist/hooks/useDeferredReorder.js +47 -0
- package/dist/hooks/useDialog.d.ts +5 -0
- package/dist/hooks/useDialog.js +17 -3
- package/dist/hooks/useKeyboardGrab.d.ts +58 -0
- package/dist/hooks/useKeyboardGrab.js +118 -0
- package/dist/hooks/useOptimisticValue.d.ts +51 -0
- package/dist/hooks/useOptimisticValue.js +61 -0
- package/dist/hooks/useReducedMotion.d.ts +4 -1
- package/dist/hooks/useReducedMotion.js +7 -1
- package/dist/hooks/useResolvedFilterLabels.d.ts +15 -0
- package/dist/hooks/useResolvedFilterLabels.js +34 -0
- package/dist/hooks/useSlidingIndicator.d.ts +18 -0
- package/dist/hooks/useSlidingIndicator.js +80 -0
- package/dist/hooks/useVirtualRows.d.ts +21 -2
- package/dist/hooks/useVirtualRows.js +30 -3
- package/dist/icons/Icon.d.ts +1 -1
- package/dist/index.cjs +19687 -13423
- package/dist/index.d.cts +3419 -651
- package/dist/index.d.ts +48 -18
- package/dist/index.js +153 -9
- package/dist/labels-context.d.ts +93 -3
- package/dist/motion.d.ts +11 -0
- package/dist/motion.js +35 -0
- package/dist/overlay-stack.d.ts +6 -0
- package/dist/overlay-stack.js +13 -0
- package/dist/qr-model.d.ts +65 -0
- package/dist/qr-model.js +323 -0
- package/dist/relationship-graph-model.d.ts +92 -3
- package/dist/relationship-graph-model.js +134 -15
- package/dist/reorder.d.ts +30 -0
- package/dist/reorder.js +74 -0
- package/dist/rich-text.cjs +46 -4
- package/dist/scheduling-resource-model.d.ts +76 -0
- package/dist/scheduling-resource-model.js +74 -0
- package/dist/search-suggest-model.js +3 -1
- package/dist/shell-context.d.ts +11 -0
- package/dist/shell-context.js +7 -0
- package/dist/time-field-model.d.ts +46 -0
- package/dist/time-field-model.js +152 -0
- package/dist/toast-store.d.ts +22 -3
- package/dist/toast-store.js +26 -9
- package/package.json +2 -1
package/dist/emoji.cjs
CHANGED
|
@@ -169,12 +169,17 @@ function removeAnchorName(el, name) {
|
|
|
169
169
|
else el.style.removeProperty("anchor-name");
|
|
170
170
|
}
|
|
171
171
|
function translationOf(el) {
|
|
172
|
-
const
|
|
172
|
+
const style = getComputedStyle(el);
|
|
173
|
+
const t = style.transform;
|
|
173
174
|
if (!t || t === "none" || typeof DOMMatrixReadOnly === "undefined") return { x: 0, y: 0 };
|
|
174
175
|
try {
|
|
175
176
|
const m = new DOMMatrixReadOnly(t);
|
|
177
|
+
const [ox = 0, oy = 0] = style.transformOrigin.split(" ").map((v) => parseFloat(v) || 0);
|
|
178
|
+
const cx2 = el.offsetWidth / 2 - ox;
|
|
179
|
+
const cy = el.offsetHeight / 2 - oy;
|
|
180
|
+
const p = m.transformPoint({ x: cx2, y: cy });
|
|
176
181
|
const z = zoomOf(el);
|
|
177
|
-
return { x:
|
|
182
|
+
return { x: (p.x - cx2) * z, y: (p.y - cy) * z };
|
|
178
183
|
} catch {
|
|
179
184
|
return { x: 0, y: 0 };
|
|
180
185
|
}
|
|
@@ -271,6 +276,13 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
271
276
|
const measured = size.width > 0 || size.height > 0;
|
|
272
277
|
if (measured && session.current) side.current = pos.side;
|
|
273
278
|
if (floating.dataset.placement !== pos.placement) floating.dataset.placement = pos.placement;
|
|
279
|
+
if (measured) {
|
|
280
|
+
const clamp2 = (v, max) => Math.min(Math.max(v, 0), max);
|
|
281
|
+
const ox = pos.side === "left" ? size.width : pos.side === "right" ? 0 : clamp2(a.x + a.width / 2 - pos.x, size.width);
|
|
282
|
+
const oy = pos.side === "top" ? size.height : pos.side === "bottom" ? 0 : clamp2(a.y + a.height / 2 - pos.y, size.height);
|
|
283
|
+
const origin = `${Math.round(ox / zoom)}px ${Math.round(oy / zoom)}px`;
|
|
284
|
+
if (s.getPropertyValue("--uix-anchor-origin") !== origin) s.setProperty("--uix-anchor-origin", origin);
|
|
285
|
+
}
|
|
274
286
|
const writeFixed = (at) => {
|
|
275
287
|
const left2 = `${Math.round(at.x / zoom)}px`;
|
|
276
288
|
const top2 = `${Math.round(at.y / zoom)}px`;
|
|
@@ -367,6 +379,33 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
367
379
|
return reposition;
|
|
368
380
|
}
|
|
369
381
|
|
|
382
|
+
// src/motion.ts
|
|
383
|
+
var KEY_OPENED = "data-uix-key-opened";
|
|
384
|
+
var keyboard = false;
|
|
385
|
+
var watching = false;
|
|
386
|
+
function watchInputModality() {
|
|
387
|
+
if (watching || typeof document === "undefined") return;
|
|
388
|
+
watching = true;
|
|
389
|
+
document.addEventListener("keydown", (event) => {
|
|
390
|
+
if (!["Shift", "Control", "Alt", "Meta"].includes(event.key)) keyboard = true;
|
|
391
|
+
}, true);
|
|
392
|
+
document.addEventListener("pointerdown", () => {
|
|
393
|
+
keyboard = false;
|
|
394
|
+
}, true);
|
|
395
|
+
}
|
|
396
|
+
function markKeyOpened(el) {
|
|
397
|
+
if (!el) return;
|
|
398
|
+
if (keyboard) el.setAttribute(KEY_OPENED, "");
|
|
399
|
+
else el.removeAttribute(KEY_OPENED);
|
|
400
|
+
}
|
|
401
|
+
function tokenMs(name, fallback, el) {
|
|
402
|
+
if (typeof getComputedStyle === "undefined" || typeof document === "undefined") return fallback;
|
|
403
|
+
const raw = getComputedStyle(el ?? document.documentElement).getPropertyValue(name).trim();
|
|
404
|
+
const m = /^(-?\d*\.?\d+)(ms|s)$/.exec(raw);
|
|
405
|
+
if (!m) return fallback;
|
|
406
|
+
return m[2] === "s" ? Number(m[1]) * 1e3 : Number(m[1]);
|
|
407
|
+
}
|
|
408
|
+
|
|
370
409
|
// src/components/Popover.tsx
|
|
371
410
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
372
411
|
var useIsomorphicLayoutEffect2 = typeof document !== "undefined" ? import_react2.useLayoutEffect : import_react2.useEffect;
|
|
@@ -420,6 +459,14 @@ function Popover({
|
|
|
420
459
|
el.hidePopover();
|
|
421
460
|
} : void 0
|
|
422
461
|
});
|
|
462
|
+
(0, import_react2.useEffect)(() => {
|
|
463
|
+
watchInputModality();
|
|
464
|
+
const el = ref.current;
|
|
465
|
+
if (!el) return;
|
|
466
|
+
const mark = () => markKeyOpened(el);
|
|
467
|
+
el.addEventListener("beforetoggle", mark);
|
|
468
|
+
return () => el.removeEventListener("beforetoggle", mark);
|
|
469
|
+
}, []);
|
|
423
470
|
(0, import_react2.useEffect)(() => {
|
|
424
471
|
const el = ref.current;
|
|
425
472
|
if (!el || !anchor) return;
|
|
@@ -500,6 +547,8 @@ function Popover({
|
|
|
500
547
|
};
|
|
501
548
|
const onKeyDown = (event) => {
|
|
502
549
|
if (event.key !== "Escape" || !isPopoverOpen(el)) return;
|
|
550
|
+
event.preventDefault();
|
|
551
|
+
event.stopPropagation();
|
|
503
552
|
window.clearTimeout(timer);
|
|
504
553
|
const hadFocus = el.contains(el.ownerDocument.activeElement);
|
|
505
554
|
try {
|
|
@@ -554,9 +603,9 @@ function buildEmojiData(locale, compact, messages) {
|
|
|
554
603
|
const sorted = [...compact].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
555
604
|
for (const item of sorted) {
|
|
556
605
|
if (item.group === void 0) continue;
|
|
557
|
-
const
|
|
558
|
-
if (
|
|
559
|
-
const entry = { emoji: item.unicode, name: item.label, tags: item.tags ?? [], group };
|
|
606
|
+
const group2 = groupIndex.get(item.group);
|
|
607
|
+
if (group2 === void 0) continue;
|
|
608
|
+
const entry = { emoji: item.unicode, name: item.label, tags: item.tags ?? [], group: group2 };
|
|
560
609
|
emojis.push(entry);
|
|
561
610
|
names.set(item.unicode, item.label);
|
|
562
611
|
names.set(item.unicode.replace(/️/g, ""), item.label);
|
|
@@ -828,8 +877,8 @@ function EmojiPicker({
|
|
|
828
877
|
if (quickPicks?.length) list.push({ key: "quick", title: labels.quickPicks, emojis: [...new Set(quickPicks)] });
|
|
829
878
|
if (recent.length) list.push({ key: "recent", title: labels.recent, emojis: recent });
|
|
830
879
|
if (data) {
|
|
831
|
-
data.groups.forEach((
|
|
832
|
-
list.push({ key:
|
|
880
|
+
data.groups.forEach((group2, index) => {
|
|
881
|
+
list.push({ key: group2.key, title: group2.name, emojis: data.emojis.filter((e) => e.group === index).map((e) => e.emoji) });
|
|
833
882
|
});
|
|
834
883
|
}
|
|
835
884
|
return list;
|
|
@@ -1122,86 +1171,131 @@ function useKbdPlatform(platform) {
|
|
|
1122
1171
|
const detected = (0, import_react5.useSyncExternalStore)(noSubscription, detectPlatform, serverPlatform);
|
|
1123
1172
|
return platform ?? detected;
|
|
1124
1173
|
}
|
|
1125
|
-
var ARIA_KEYS = {
|
|
1126
|
-
Mod: { mac: "Meta", other: "Control" },
|
|
1127
|
-
Ctrl: { mac: "Control", other: "Control" },
|
|
1128
|
-
Esc: { mac: "Escape", other: "Escape" },
|
|
1129
|
-
Del: { mac: "Delete", other: "Delete" },
|
|
1130
|
-
" ": { mac: "Space", other: "Space" },
|
|
1131
|
-
"+": { mac: "Plus", other: "Plus" }
|
|
1132
|
-
};
|
|
1133
|
-
function ariaKeyShortcuts(keys, platform) {
|
|
1134
|
-
return keys.map((key) => ARIA_KEYS[key]?.[platform] ?? (key.length === 1 ? key.toUpperCase() : key)).join("+");
|
|
1135
|
-
}
|
|
1136
1174
|
var isKeyShortcut = (value) => Array.isArray(value) && value.length > 0 && value.every((key) => typeof key === "string");
|
|
1175
|
+
function spokenKeys(keys, platform, keyLabels) {
|
|
1176
|
+
return keys.map((key) => keyLabels?.[key] ?? KEYS[key]?.[platform]?.[1] ?? key).join("+");
|
|
1177
|
+
}
|
|
1137
1178
|
function KbdCombo({ keys, platform, keyLabels, className, ...props }) {
|
|
1138
1179
|
const active = useKbdPlatform(platform);
|
|
1139
|
-
const parts = keys.map((key) => {
|
|
1140
|
-
const known = KEYS[key]?.[active];
|
|
1141
|
-
return { glyph: known?.[0] ?? key, spoken: keyLabels?.[key] ?? known?.[1] ?? key };
|
|
1142
|
-
});
|
|
1143
1180
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: cx("uix-kbd-combo", className), ...props, children: [
|
|
1144
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "uix-visually-hidden", children:
|
|
1145
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "uix-kbd-combo__keys", "aria-hidden": "true", children:
|
|
1181
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "uix-visually-hidden", children: spokenKeys(keys, active, keyLabels) }),
|
|
1182
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "uix-kbd-combo__keys", "aria-hidden": "true", children: keys.map((key, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("kbd", { className: "uix-kbd", children: KEYS[key]?.[active]?.[0] ?? key }, `${key}-${i}`)) })
|
|
1146
1183
|
] });
|
|
1147
1184
|
}
|
|
1148
1185
|
|
|
1149
1186
|
// src/components/Tooltip.tsx
|
|
1150
1187
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1188
|
+
var group = { open: 0, lastClosed: -Infinity, current: null };
|
|
1189
|
+
var useIsomorphicLayoutEffect4 = typeof document !== "undefined" ? import_react6.useLayoutEffect : import_react6.useEffect;
|
|
1151
1190
|
function Tooltip({ label, content, placement = "top", shortcut, className, children, ...props }) {
|
|
1152
1191
|
const triggerRef = (0, import_react6.useRef)(null);
|
|
1153
1192
|
const bubbleRef = (0, import_react6.useRef)(null);
|
|
1154
1193
|
const closeTimer = (0, import_react6.useRef)(null);
|
|
1194
|
+
const openTimer = (0, import_react6.useRef)(null);
|
|
1195
|
+
const shown = (0, import_react6.useRef)(false);
|
|
1155
1196
|
const [open, setOpen] = (0, import_react6.useState)(false);
|
|
1156
1197
|
const id = (0, import_react6.useId)();
|
|
1157
1198
|
const platform = useKbdPlatform();
|
|
1158
1199
|
const childShortcut = (0, import_react6.isValidElement)(children) ? children.props.shortcut : void 0;
|
|
1159
1200
|
const own = shortcut ?? (isKeyShortcut(childShortcut) ? childShortcut : void 0);
|
|
1160
1201
|
const keys = own && own.length > 0 ? own : null;
|
|
1161
|
-
const keyshortcuts = keys ? ariaKeyShortcuts(keys, platform) : void 0;
|
|
1162
1202
|
useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
|
|
1203
|
+
(0, import_react6.useEffect)(() => {
|
|
1204
|
+
watchInputModality();
|
|
1205
|
+
}, []);
|
|
1163
1206
|
const cancelClose = () => {
|
|
1164
1207
|
if (closeTimer.current != null) {
|
|
1165
1208
|
clearTimeout(closeTimer.current);
|
|
1166
1209
|
closeTimer.current = null;
|
|
1167
1210
|
}
|
|
1168
1211
|
};
|
|
1169
|
-
const
|
|
1170
|
-
|
|
1212
|
+
const cancelOpen = () => {
|
|
1213
|
+
if (openTimer.current != null) {
|
|
1214
|
+
clearTimeout(openTimer.current);
|
|
1215
|
+
openTimer.current = null;
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
const reveal = (instant) => {
|
|
1219
|
+
cancelOpen();
|
|
1220
|
+
if (group.current && group.current !== self) group.current.current(true);
|
|
1221
|
+
group.current = self;
|
|
1171
1222
|
const b = bubbleRef.current;
|
|
1172
1223
|
if (b && !b.matches(":popover-open")) {
|
|
1224
|
+
b.toggleAttribute("data-uix-instant", instant);
|
|
1225
|
+
markKeyOpened(b);
|
|
1173
1226
|
try {
|
|
1174
1227
|
b.showPopover();
|
|
1175
1228
|
} catch {
|
|
1176
1229
|
}
|
|
1177
1230
|
}
|
|
1231
|
+
if (!shown.current) {
|
|
1232
|
+
shown.current = true;
|
|
1233
|
+
group.open += 1;
|
|
1234
|
+
}
|
|
1178
1235
|
setOpen(true);
|
|
1179
1236
|
};
|
|
1180
|
-
const
|
|
1237
|
+
const show = (via) => {
|
|
1181
1238
|
cancelClose();
|
|
1239
|
+
if (shown.current) return;
|
|
1240
|
+
const warm = group.open > 0 || performance.now() - group.lastClosed < tokenMs("--uix-delay-tooltip-skip", 300);
|
|
1241
|
+
if (via === "focus" || warm) {
|
|
1242
|
+
reveal(via === "pointer");
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
if (openTimer.current == null) openTimer.current = window.setTimeout(() => reveal(false), tokenMs("--uix-delay-tooltip", 400));
|
|
1246
|
+
};
|
|
1247
|
+
const hide = (instant = false) => {
|
|
1248
|
+
cancelClose();
|
|
1249
|
+
cancelOpen();
|
|
1182
1250
|
const b = bubbleRef.current;
|
|
1183
1251
|
if (b && b.matches(":popover-open")) {
|
|
1252
|
+
b.toggleAttribute("data-uix-instant", instant);
|
|
1184
1253
|
try {
|
|
1185
1254
|
b.hidePopover();
|
|
1186
1255
|
} catch {
|
|
1187
1256
|
}
|
|
1188
1257
|
}
|
|
1258
|
+
if (shown.current) {
|
|
1259
|
+
shown.current = false;
|
|
1260
|
+
group.open -= 1;
|
|
1261
|
+
group.lastClosed = performance.now();
|
|
1262
|
+
}
|
|
1263
|
+
if (group.current === self) group.current = null;
|
|
1189
1264
|
setOpen(false);
|
|
1190
1265
|
};
|
|
1266
|
+
const self = (0, import_react6.useRef)(hide);
|
|
1267
|
+
self.current = hide;
|
|
1191
1268
|
const scheduleHide = () => {
|
|
1192
1269
|
cancelClose();
|
|
1193
|
-
closeTimer.current = window.setTimeout(
|
|
1270
|
+
closeTimer.current = window.setTimeout(() => {
|
|
1271
|
+
closeTimer.current = null;
|
|
1272
|
+
try {
|
|
1273
|
+
if (triggerRef.current?.matches(":hover")) return;
|
|
1274
|
+
} catch {
|
|
1275
|
+
}
|
|
1276
|
+
hide();
|
|
1277
|
+
}, 100);
|
|
1194
1278
|
};
|
|
1195
|
-
(
|
|
1196
|
-
if (!open) return;
|
|
1279
|
+
useIsomorphicLayoutEffect4(() => {
|
|
1280
|
+
if (!open) return void 0;
|
|
1197
1281
|
const onKey = (e) => {
|
|
1198
|
-
if (e.key
|
|
1282
|
+
if (e.key !== "Escape") return;
|
|
1283
|
+
e.preventDefault();
|
|
1284
|
+
e.stopPropagation();
|
|
1285
|
+
hide();
|
|
1199
1286
|
};
|
|
1200
|
-
document.addEventListener("keydown", onKey);
|
|
1201
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
1287
|
+
document.addEventListener("keydown", onKey, true);
|
|
1288
|
+
return () => document.removeEventListener("keydown", onKey, true);
|
|
1202
1289
|
}, [open]);
|
|
1203
1290
|
(0, import_react6.useEffect)(() => () => {
|
|
1204
1291
|
if (closeTimer.current != null) clearTimeout(closeTimer.current);
|
|
1292
|
+
if (openTimer.current != null) clearTimeout(openTimer.current);
|
|
1293
|
+
if (shown.current) {
|
|
1294
|
+
shown.current = false;
|
|
1295
|
+
group.open -= 1;
|
|
1296
|
+
group.lastClosed = performance.now();
|
|
1297
|
+
}
|
|
1298
|
+
if (group.current === self) group.current = null;
|
|
1205
1299
|
const b = bubbleRef.current;
|
|
1206
1300
|
if (b && b.matches(":popover-open")) {
|
|
1207
1301
|
try {
|
|
@@ -1211,30 +1305,27 @@ function Tooltip({ label, content, placement = "top", shortcut, className, child
|
|
|
1211
1305
|
}
|
|
1212
1306
|
}, []);
|
|
1213
1307
|
const bubbleAttr = { popover: "manual" };
|
|
1308
|
+
const text = content ?? label;
|
|
1309
|
+
const description = keys ? [text != null ? `${id}-text` : null, `${id}-keys`].filter(Boolean).join(" ") : id;
|
|
1214
1310
|
const single = (0, import_react6.isValidElement)(children);
|
|
1215
1311
|
const child = single ? (0, import_react6.cloneElement)(children, {
|
|
1216
|
-
"aria-describedby": [children.props["aria-describedby"],
|
|
1217
|
-
...keyshortcuts ? { "aria-keyshortcuts": keyshortcuts } : {}
|
|
1312
|
+
"aria-describedby": [children.props["aria-describedby"], description].filter(Boolean).join(" ")
|
|
1218
1313
|
}) : children;
|
|
1219
1314
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1220
1315
|
"span",
|
|
1221
1316
|
{
|
|
1222
1317
|
ref: triggerRef,
|
|
1223
1318
|
className: cx("uix-tooltip", className),
|
|
1224
|
-
"aria-describedby": single ? void 0 :
|
|
1225
|
-
"aria-keyshortcuts": single ? void 0 : keyshortcuts,
|
|
1319
|
+
"aria-describedby": single ? void 0 : description,
|
|
1226
1320
|
tabIndex: single ? void 0 : 0,
|
|
1227
|
-
onMouseEnter: show,
|
|
1321
|
+
onMouseEnter: () => show("pointer"),
|
|
1228
1322
|
onMouseLeave: scheduleHide,
|
|
1229
|
-
onFocus: show,
|
|
1230
|
-
onBlur: hide,
|
|
1231
|
-
onKeyDown: (e) => {
|
|
1232
|
-
if (e.key === "Escape") hide();
|
|
1233
|
-
},
|
|
1323
|
+
onFocus: () => show("focus"),
|
|
1324
|
+
onBlur: () => hide(),
|
|
1234
1325
|
...props,
|
|
1235
1326
|
children: [
|
|
1236
1327
|
child,
|
|
1237
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1238
1329
|
"span",
|
|
1239
1330
|
{
|
|
1240
1331
|
ref: bubbleRef,
|
|
@@ -1244,10 +1335,13 @@ function Tooltip({ label, content, placement = "top", shortcut, className, child
|
|
|
1244
1335
|
onMouseEnter: cancelClose,
|
|
1245
1336
|
onMouseLeave: scheduleHide,
|
|
1246
1337
|
...bubbleAttr,
|
|
1247
|
-
children: [
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1338
|
+
children: keys ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1339
|
+
text != null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { id: `${id}-text`, children: text }),
|
|
1340
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "uix-tooltip__shortcut", children: [
|
|
1341
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { id: `${id}-keys`, className: "uix-visually-hidden", children: spokenKeys(keys, platform) }),
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KbdCombo, { keys: [...keys], platform, "aria-hidden": "true" })
|
|
1343
|
+
] })
|
|
1344
|
+
] }) : text
|
|
1251
1345
|
}
|
|
1252
1346
|
)
|
|
1253
1347
|
]
|
package/dist/filter-model.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ interface FilterChoice {
|
|
|
9
9
|
}
|
|
10
10
|
type TextFilterOperator = 'contains' | 'not-contains' | 'is' | 'is-not' | 'starts-with';
|
|
11
11
|
type NumberFilterOperator = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'between';
|
|
12
|
+
/**
|
|
13
|
+
* A date condition (HAR-1832). `on`, `before` and `after` take one day in `from`; `between` takes
|
|
14
|
+
* `from` and `to`, both inclusive. "Before" excludes the day and "after" starts the next day:
|
|
15
|
+
* `dateFilterBounds` turns each into the half-open range a query needs.
|
|
16
|
+
*/
|
|
17
|
+
type DateFilterOperator = 'on' | 'before' | 'after' | 'between';
|
|
12
18
|
type FilterValue = {
|
|
13
19
|
kind: 'enum';
|
|
14
20
|
values: string[];
|
|
@@ -25,12 +31,24 @@ type FilterValue = {
|
|
|
25
31
|
kind: 'date-range';
|
|
26
32
|
from?: string;
|
|
27
33
|
to?: string;
|
|
34
|
+
}
|
|
35
|
+
/** A date with a condition (HAR-1832); `YYYY-MM-DD`. A saved `between` stays `between`, even of one day. */
|
|
36
|
+
| {
|
|
37
|
+
kind: 'date';
|
|
38
|
+
operator: DateFilterOperator;
|
|
39
|
+
from?: string;
|
|
40
|
+
to?: string;
|
|
28
41
|
} | {
|
|
29
42
|
kind: 'boolean';
|
|
30
43
|
value?: boolean;
|
|
31
44
|
} | {
|
|
32
45
|
kind: 'reference';
|
|
33
46
|
values: FilterChoice[];
|
|
47
|
+
}
|
|
48
|
+
/** Free-text values typed in as tags (HAR-1832), e.g. several hostnames; matched by the product. */
|
|
49
|
+
| {
|
|
50
|
+
kind: 'tags';
|
|
51
|
+
values: string[];
|
|
34
52
|
};
|
|
35
53
|
type FilterValueKind = FilterValue['kind'];
|
|
36
54
|
/**
|
|
@@ -52,8 +70,15 @@ interface FilterField {
|
|
|
52
70
|
options?: readonly FilterChoice[];
|
|
53
71
|
/** `reference`: finds records to pick; rejected promises show the error state. */
|
|
54
72
|
onSearch?: (query: string) => Promise<readonly FilterChoice[]>;
|
|
73
|
+
/**
|
|
74
|
+
* `reference`: labels for chosen ids that arrived without one (a saved view restores ids).
|
|
75
|
+
* The editor asks once for every id whose label is missing or equal to the id.
|
|
76
|
+
*/
|
|
77
|
+
resolveLabels?: (ids: readonly string[]) => Promise<readonly FilterChoice[]>;
|
|
55
78
|
/** `text` / `number`: the operators offered, in order. Default: all of the kind. */
|
|
56
79
|
operators?: readonly (TextFilterOperator | NumberFilterOperator)[];
|
|
80
|
+
/** `date`: the conditions offered, in order. Default: on, before, after, between. */
|
|
81
|
+
dateOperators?: readonly DateFilterOperator[];
|
|
57
82
|
/** `number`: a unit shown after the value, e.g. "days". */
|
|
58
83
|
unit?: string;
|
|
59
84
|
/** `boolean`: words for true and false, e.g. Escalated / Not escalated. Default from labels. */
|
|
@@ -80,14 +105,45 @@ interface FilterSummaryLabels {
|
|
|
80
105
|
more: string;
|
|
81
106
|
/** An `enum` value equal to a preset: `{label}` (the preset), `{field}`. Default `{label}`. */
|
|
82
107
|
preset?: string;
|
|
108
|
+
/** `date` conditions (HAR-1832), `{date}`. Default "{date}", "before {date}", "after {date}". */
|
|
109
|
+
on?: string;
|
|
110
|
+
before?: string;
|
|
111
|
+
after?: string;
|
|
83
112
|
}
|
|
84
113
|
declare const DEFAULT_FILTER_SUMMARY_LABELS: FilterSummaryLabels;
|
|
85
114
|
declare const TEXT_FILTER_OPERATORS: readonly TextFilterOperator[];
|
|
86
115
|
declare const NUMBER_FILTER_OPERATORS: readonly NumberFilterOperator[];
|
|
116
|
+
declare const DATE_FILTER_OPERATORS: readonly DateFilterOperator[];
|
|
87
117
|
/** The empty value for a field kind (what Clear resets to). */
|
|
88
118
|
declare function emptyFilterValue(kind: FilterValueKind): FilterValue;
|
|
89
119
|
/** True when the value filters nothing (so the chip goes away). */
|
|
90
120
|
declare function isFilterEmpty(value: FilterValue | undefined): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* A range whose ends were typed the wrong way round (from 10 Oct to 1 Oct, 7 to 2) put in order,
|
|
123
|
+
* so it means what the person meant instead of matching nothing (PR #168 review). Number
|
|
124
|
+
* `between`, date `between` and `date-range`; every other value comes back as it is. Internal:
|
|
125
|
+
* FilterEditor applies through it, `dateFilterBounds` and `summarizeFilter` read through it.
|
|
126
|
+
*/
|
|
127
|
+
declare function orderFilterRange<V extends FilterValue | undefined>(value: V): V;
|
|
128
|
+
/**
|
|
129
|
+
* The half-open range a `date` filter means, for a query (HAR-1832): `{ from }` is the first day
|
|
130
|
+
* included, `{ until }` the first day NOT included, either may be missing. "On 5 Oct" is
|
|
131
|
+
* `[5 Oct, 6 Oct)`, "before 5 Oct" is `[…, 5 Oct)`, "after 5 Oct" is `[6 Oct, …)`, "between 1 and
|
|
132
|
+
* 5 Oct" is `[1 Oct, 6 Oct)`. A `date-range` value reads as `between` with optional ends. Empty
|
|
133
|
+
* values give `{}`.
|
|
134
|
+
*/
|
|
135
|
+
declare function dateFilterBounds(input: FilterValue | undefined): {
|
|
136
|
+
from?: string;
|
|
137
|
+
until?: string;
|
|
138
|
+
};
|
|
139
|
+
/** True for a chosen reference whose label is missing or only repeats its id (restored from an id). */
|
|
140
|
+
declare const isUnresolvedChoice: (choice: FilterChoice) => boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Fills in the labels of a `reference` value restored from ids (HAR-1832), with
|
|
143
|
+
* `field.resolveLabels`. Other values, and values whose labels are all known, come back as they
|
|
144
|
+
* are. Ids the resolver does not return keep the label they had.
|
|
145
|
+
*/
|
|
146
|
+
declare function resolveFilterLabels(field: FilterField, value: FilterValue | undefined): Promise<FilterValue | undefined>;
|
|
91
147
|
/**
|
|
92
148
|
* The first of `field.presets` whose values equal the value's as a set, or undefined. Only an
|
|
93
149
|
* `enum` value can match a preset.
|
|
@@ -104,9 +160,9 @@ interface SummarizeFilterOptions {
|
|
|
104
160
|
}
|
|
105
161
|
/**
|
|
106
162
|
* 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`
|
|
163
|
+
* 01.10.2026 – 05.10.2026", "Created: before 05.10.2026", "Host: db-01, db-02 +1", "Escalated: Yes". An enum value equal to one of `field.presets`
|
|
108
164
|
* gives that preset's label (the `preset` label). Empty values give an empty string.
|
|
109
165
|
*/
|
|
110
|
-
declare function summarizeFilter(field: FilterField,
|
|
166
|
+
declare function summarizeFilter(field: FilterField, input: FilterValue | undefined, options?: SummarizeFilterOptions): string;
|
|
111
167
|
|
|
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 };
|
|
168
|
+
export { DATE_FILTER_OPERATORS, DEFAULT_FILTER_SUMMARY_LABELS, type DateFilterOperator, type FilterChoice, type FilterField, type FilterPreset, type FilterSummaryLabels, type FilterValue, type FilterValueKind, NUMBER_FILTER_OPERATORS, type NumberFilterOperator, type SummarizeFilterOptions, TEXT_FILTER_OPERATORS, type TextFilterOperator, dateFilterBounds, emptyFilterValue, isFilterEmpty, isUnresolvedChoice, matchFilterPreset, orderFilterRange, resolveFilterLabels, summarizeFilter };
|
package/dist/filter-model.js
CHANGED
|
@@ -19,10 +19,14 @@ const DEFAULT_FILTER_SUMMARY_LABELS = {
|
|
|
19
19
|
from: "from {from}",
|
|
20
20
|
until: "until {to}",
|
|
21
21
|
more: "{first} +{count}",
|
|
22
|
-
preset: "{label}"
|
|
22
|
+
preset: "{label}",
|
|
23
|
+
on: "{date}",
|
|
24
|
+
before: "before {date}",
|
|
25
|
+
after: "after {date}"
|
|
23
26
|
};
|
|
24
27
|
const TEXT_FILTER_OPERATORS = ["contains", "not-contains", "is", "is-not", "starts-with"];
|
|
25
28
|
const NUMBER_FILTER_OPERATORS = ["eq", "neq", "lt", "lte", "gt", "gte", "between"];
|
|
29
|
+
const DATE_FILTER_OPERATORS = ["on", "before", "after", "between"];
|
|
26
30
|
function emptyFilterValue(kind) {
|
|
27
31
|
switch (kind) {
|
|
28
32
|
case "enum":
|
|
@@ -33,10 +37,14 @@ function emptyFilterValue(kind) {
|
|
|
33
37
|
return { kind, operator: "eq" };
|
|
34
38
|
case "date-range":
|
|
35
39
|
return { kind };
|
|
40
|
+
case "date":
|
|
41
|
+
return { kind, operator: "on" };
|
|
36
42
|
case "boolean":
|
|
37
43
|
return { kind };
|
|
38
44
|
case "reference":
|
|
39
45
|
return { kind, values: [] };
|
|
46
|
+
case "tags":
|
|
47
|
+
return { kind, values: [] };
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
function isFilterEmpty(value) {
|
|
@@ -44,6 +52,7 @@ function isFilterEmpty(value) {
|
|
|
44
52
|
switch (value.kind) {
|
|
45
53
|
case "enum":
|
|
46
54
|
case "reference":
|
|
55
|
+
case "tags":
|
|
47
56
|
return value.values.length === 0;
|
|
48
57
|
case "text":
|
|
49
58
|
return value.text.trim() === "";
|
|
@@ -51,10 +60,53 @@ function isFilterEmpty(value) {
|
|
|
51
60
|
return value.value === void 0 || value.operator === "between" && value.to === void 0;
|
|
52
61
|
case "date-range":
|
|
53
62
|
return !value.from && !value.to;
|
|
63
|
+
case "date":
|
|
64
|
+
return !value.from || value.operator === "between" && !value.to;
|
|
54
65
|
case "boolean":
|
|
55
66
|
return value.value === void 0;
|
|
56
67
|
}
|
|
57
68
|
}
|
|
69
|
+
function addDays(date, days) {
|
|
70
|
+
const [y, m, d] = date.split("-").map(Number);
|
|
71
|
+
const next = new Date(Date.UTC(y, m - 1, d + days));
|
|
72
|
+
return next.toISOString().slice(0, 10);
|
|
73
|
+
}
|
|
74
|
+
function orderFilterRange(value) {
|
|
75
|
+
if (!value) return value;
|
|
76
|
+
if (value.kind === "number" && value.operator === "between" && value.value !== void 0 && value.to !== void 0 && value.value > value.to) {
|
|
77
|
+
return { ...value, value: value.to, to: value.value };
|
|
78
|
+
}
|
|
79
|
+
if ((value.kind === "date-range" || value.kind === "date" && value.operator === "between") && value.from && value.to && value.from > value.to) {
|
|
80
|
+
return { ...value, from: value.to, to: value.from };
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
function dateFilterBounds(input) {
|
|
85
|
+
const value = orderFilterRange(input);
|
|
86
|
+
if (value?.kind === "date-range") {
|
|
87
|
+
return { ...value.from ? { from: value.from } : {}, ...value.to ? { until: addDays(value.to, 1) } : {} };
|
|
88
|
+
}
|
|
89
|
+
if (value?.kind !== "date" || isFilterEmpty(value)) return {};
|
|
90
|
+
const day = value.from;
|
|
91
|
+
switch (value.operator) {
|
|
92
|
+
case "on":
|
|
93
|
+
return { from: day, until: addDays(day, 1) };
|
|
94
|
+
case "before":
|
|
95
|
+
return { until: day };
|
|
96
|
+
case "after":
|
|
97
|
+
return { from: addDays(day, 1) };
|
|
98
|
+
case "between":
|
|
99
|
+
return { from: day, until: addDays(value.to, 1) };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const isUnresolvedChoice = (choice) => !choice.label || choice.label === choice.value;
|
|
103
|
+
async function resolveFilterLabels(field, value) {
|
|
104
|
+
if (value?.kind !== "reference" || !field.resolveLabels) return value;
|
|
105
|
+
const missing = value.values.filter(isUnresolvedChoice).map((v) => v.value);
|
|
106
|
+
if (missing.length === 0) return value;
|
|
107
|
+
const resolved = new Map((await field.resolveLabels(missing)).map((c) => [c.value, c.label]));
|
|
108
|
+
return { kind: "reference", values: value.values.map((v) => resolved.has(v.value) ? { value: v.value, label: resolved.get(v.value) } : v) };
|
|
109
|
+
}
|
|
58
110
|
function matchFilterPreset(field, value) {
|
|
59
111
|
if (value?.kind !== "enum" || !field.presets?.length) return void 0;
|
|
60
112
|
const chosen = new Set(value.values);
|
|
@@ -64,7 +116,8 @@ function matchFilterPreset(field, value) {
|
|
|
64
116
|
});
|
|
65
117
|
}
|
|
66
118
|
const fill = (template, values) => template.replace(/\{(\w+)\}/g, (match, key) => key in values ? String(values[key]) : match);
|
|
67
|
-
function summarizeFilter(field,
|
|
119
|
+
function summarizeFilter(field, input, options = {}) {
|
|
120
|
+
const value = orderFilterRange(input);
|
|
68
121
|
if (!value || isFilterEmpty(value)) return "";
|
|
69
122
|
const labels = { ...DEFAULT_FILTER_SUMMARY_LABELS, ...options.labels, operators: { ...DEFAULT_FILTER_SUMMARY_LABELS.operators, ...options.labels?.operators } };
|
|
70
123
|
const date = options.formatDate ?? ((d) => d);
|
|
@@ -82,22 +135,34 @@ function summarizeFilter(field, value, options = {}) {
|
|
|
82
135
|
}
|
|
83
136
|
case "reference":
|
|
84
137
|
return `${field.label}: ${list(value.values.map((v) => v.label))}`;
|
|
138
|
+
case "tags":
|
|
139
|
+
return `${field.label}: ${list(value.values)}`;
|
|
85
140
|
case "text":
|
|
86
141
|
return `${field.label} ${labels.operators[value.operator]} \u201C${value.text.trim()}\u201D`;
|
|
87
142
|
case "number":
|
|
88
143
|
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
144
|
case "date-range":
|
|
90
145
|
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) })}`;
|
|
146
|
+
case "date": {
|
|
147
|
+
if (value.operator === "between") return `${field.label}: ${fill(labels.range, { from: date(value.from), to: date(value.to) })}`;
|
|
148
|
+
const template = labels[value.operator] ?? DEFAULT_FILTER_SUMMARY_LABELS[value.operator];
|
|
149
|
+
return `${field.label}: ${fill(template, { date: date(value.from) })}`;
|
|
150
|
+
}
|
|
91
151
|
case "boolean":
|
|
92
152
|
return `${field.label}: ${value.value ? field.trueLabel ?? labels.yes : field.falseLabel ?? labels.no}`;
|
|
93
153
|
}
|
|
94
154
|
}
|
|
95
155
|
export {
|
|
156
|
+
DATE_FILTER_OPERATORS,
|
|
96
157
|
DEFAULT_FILTER_SUMMARY_LABELS,
|
|
97
158
|
NUMBER_FILTER_OPERATORS,
|
|
98
159
|
TEXT_FILTER_OPERATORS,
|
|
160
|
+
dateFilterBounds,
|
|
99
161
|
emptyFilterValue,
|
|
100
162
|
isFilterEmpty,
|
|
163
|
+
isUnresolvedChoice,
|
|
101
164
|
matchFilterPreset,
|
|
165
|
+
orderFilterRange,
|
|
166
|
+
resolveFilterLabels,
|
|
102
167
|
summarizeFilter
|
|
103
168
|
};
|
|
@@ -29,12 +29,17 @@ function removeAnchorName(el, name) {
|
|
|
29
29
|
else el.style.removeProperty("anchor-name");
|
|
30
30
|
}
|
|
31
31
|
function translationOf(el) {
|
|
32
|
-
const
|
|
32
|
+
const style = getComputedStyle(el);
|
|
33
|
+
const t = style.transform;
|
|
33
34
|
if (!t || t === "none" || typeof DOMMatrixReadOnly === "undefined") return { x: 0, y: 0 };
|
|
34
35
|
try {
|
|
35
36
|
const m = new DOMMatrixReadOnly(t);
|
|
37
|
+
const [ox = 0, oy = 0] = style.transformOrigin.split(" ").map((v) => parseFloat(v) || 0);
|
|
38
|
+
const cx = el.offsetWidth / 2 - ox;
|
|
39
|
+
const cy = el.offsetHeight / 2 - oy;
|
|
40
|
+
const p = m.transformPoint({ x: cx, y: cy });
|
|
36
41
|
const z = zoomOf(el);
|
|
37
|
-
return { x:
|
|
42
|
+
return { x: (p.x - cx) * z, y: (p.y - cy) * z };
|
|
38
43
|
} catch {
|
|
39
44
|
return { x: 0, y: 0 };
|
|
40
45
|
}
|
|
@@ -131,6 +136,13 @@ function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-st
|
|
|
131
136
|
const measured = size.width > 0 || size.height > 0;
|
|
132
137
|
if (measured && session.current) side.current = pos.side;
|
|
133
138
|
if (floating.dataset.placement !== pos.placement) floating.dataset.placement = pos.placement;
|
|
139
|
+
if (measured) {
|
|
140
|
+
const clamp = (v, max) => Math.min(Math.max(v, 0), max);
|
|
141
|
+
const ox = pos.side === "left" ? size.width : pos.side === "right" ? 0 : clamp(a.x + a.width / 2 - pos.x, size.width);
|
|
142
|
+
const oy = pos.side === "top" ? size.height : pos.side === "bottom" ? 0 : clamp(a.y + a.height / 2 - pos.y, size.height);
|
|
143
|
+
const origin = `${Math.round(ox / zoom)}px ${Math.round(oy / zoom)}px`;
|
|
144
|
+
if (s.getPropertyValue("--uix-anchor-origin") !== origin) s.setProperty("--uix-anchor-origin", origin);
|
|
145
|
+
}
|
|
134
146
|
const writeFixed = (at) => {
|
|
135
147
|
const left2 = `${Math.round(at.x / zoom)}px`;
|
|
136
148
|
const top2 = `${Math.round(at.y / zoom)}px`;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ChangeHighlightOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Said through the page's polite live region when the value changes, e.g. "Status changed to
|
|
6
|
+
* Resolved by A. Novak". The highlight is the signal and its place is the location; this is
|
|
7
|
+
* the description for people who cannot see either (motion standard §2.5).
|
|
8
|
+
*/
|
|
9
|
+
announce?: string;
|
|
10
|
+
/** Turn the highlight off, e.g. while the person is editing the value themselves. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Flash `.uix-changed` on an element when `value` changes after mount — never on the first
|
|
15
|
+
* render (HAR-2585). A second change restarts the highlight. Compare by `Object.is`: pass a
|
|
16
|
+
* primitive (a status, a count, an `updatedAt`), not a fresh object.
|
|
17
|
+
*
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const ref = useChangeHighlight<HTMLTableCellElement>(row.status, { announce: `Status: ${row.status}` });
|
|
20
|
+
* <Td ref={ref}>{row.status}</Td>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The hook sets `data-uix-changed` on the element (React never renders that attribute, so a
|
|
24
|
+
* re-render cannot clear it) and removes it when the animation ends.
|
|
25
|
+
*/
|
|
26
|
+
declare function useChangeHighlight<T extends HTMLElement = HTMLElement>(value: unknown, { announce, disabled }?: ChangeHighlightOptions): RefObject<T | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Flash `.uix-changed` on `el` now (restarting a highlight already running). For changes that
|
|
29
|
+
* do not flow through a React value, e.g. a row a live update replaced.
|
|
30
|
+
*/
|
|
31
|
+
declare function flashChanged(el: HTMLElement): void;
|
|
32
|
+
|
|
33
|
+
export { type ChangeHighlightOptions, flashChanged, useChangeHighlight };
|