@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
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { UixRenderLink } from '../link.js';
|
|
4
|
+
|
|
5
|
+
/** The words of a `ContactCard` (HAR-1379). */
|
|
6
|
+
interface ContactCardLabels {
|
|
7
|
+
/** The email action's visible text. */
|
|
8
|
+
email: string;
|
|
9
|
+
/** The phone action's visible text. */
|
|
10
|
+
call: string;
|
|
11
|
+
/** The email action's accessible name; `{name}` is the contact's name. Starts with the visible text (WCAG 2.5.3). */
|
|
12
|
+
emailName: string;
|
|
13
|
+
/** The phone action's accessible name; `{name}`. */
|
|
14
|
+
callName: string;
|
|
15
|
+
/** Shown in place of the name when the person's data was erased and no name is left. */
|
|
16
|
+
purgedName: string;
|
|
17
|
+
/** The line under the name of an erased contact. */
|
|
18
|
+
purged: string;
|
|
19
|
+
/** The line under the name of a contact whose details cannot be shown (no access, or not loaded). */
|
|
20
|
+
unavailable: string;
|
|
21
|
+
}
|
|
22
|
+
declare const DEFAULT_CONTACT_CARD_LABELS: ContactCardLabels;
|
|
23
|
+
/**
|
|
24
|
+
* `active` shows everything given. `purged`: the person's data was erased; a neutral
|
|
25
|
+
* placeholder, no details and no actions. `unavailable`: the name stays, the details and actions
|
|
26
|
+
* do not (no access, or they could not be loaded).
|
|
27
|
+
*/
|
|
28
|
+
type ContactCardState = 'active' | 'purged' | 'unavailable';
|
|
29
|
+
interface ContactCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role'> {
|
|
30
|
+
/** The person's or the organisation's name. Leave it out for an erased contact. */
|
|
31
|
+
name?: string;
|
|
32
|
+
/** Role or relation, e.g. "Practice manager · Dom zdravlja Centar". */
|
|
33
|
+
role?: ReactNode;
|
|
34
|
+
/** An `Avatar` (size `lg` reads best). An erased contact always gets the placeholder. */
|
|
35
|
+
avatar?: ReactNode;
|
|
36
|
+
/** Shown as text and offered as a `mailto:` action. */
|
|
37
|
+
email?: string;
|
|
38
|
+
/** Shown as written and offered as a `tel:` action (spaces, dashes and brackets are dropped from the link). */
|
|
39
|
+
phone?: string;
|
|
40
|
+
/** More actions after Email and Call, e.g. a `ButtonLink` to the record. */
|
|
41
|
+
actions?: ReactNode;
|
|
42
|
+
/** Figures under the role (`.uix-contact__stats`). */
|
|
43
|
+
stats?: ReactNode;
|
|
44
|
+
/** The name as a link to the person's record. */
|
|
45
|
+
href?: string;
|
|
46
|
+
/** Your router's link for `href`. */
|
|
47
|
+
renderLink?: UixRenderLink;
|
|
48
|
+
/** The element of the name: a heading when the card stands for a section. Default `div`. */
|
|
49
|
+
nameAs?: 'h2' | 'h3' | 'h4' | 'div';
|
|
50
|
+
state?: ContactCardState;
|
|
51
|
+
/** Without the card frame, for a peek, a popover or a sidebar that is already a surface. */
|
|
52
|
+
bare?: boolean;
|
|
53
|
+
/** The words. Default: `UixLabelsProvider` `contactCard`, then English. */
|
|
54
|
+
labels?: Partial<ContactCardLabels>;
|
|
55
|
+
}
|
|
56
|
+
/** The digits and a leading + of a phone number, for a `tel:` link. */
|
|
57
|
+
declare const telHref: (phone: string) => string;
|
|
58
|
+
/**
|
|
59
|
+
* A person or an organisation at a glance (HAR-1379; MOTUS C-15, TENSOR incident people card):
|
|
60
|
+
* avatar, name, role, the email address and phone number as text, and Email / Call actions as
|
|
61
|
+
* `mailto:` / `tel:` links. An erased contact (`state="purged"`) shows a neutral placeholder
|
|
62
|
+
* and no way to reach anyone, so a purge is never undone on screen by a stale address.
|
|
63
|
+
*/
|
|
64
|
+
declare function ContactCard({ name, role, avatar, email, phone, actions, stats, href, renderLink, nameAs: NameTag, state, bare, labels: labelsProp, className, ...props }: ContactCardProps): react.JSX.Element;
|
|
65
|
+
|
|
66
|
+
export { ContactCard, type ContactCardLabels, type ContactCardProps, type ContactCardState, DEFAULT_CONTACT_CARD_LABELS, telHref };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cx } from "../cx.js";
|
|
4
|
+
import { fillLabel } from "../fill-label.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
6
|
+
import { renderUixLink } from "../link.js";
|
|
7
|
+
import { ButtonLink } from "./Button.js";
|
|
8
|
+
const DEFAULT_CONTACT_CARD_LABELS = {
|
|
9
|
+
email: "Email",
|
|
10
|
+
call: "Call",
|
|
11
|
+
emailName: "Email {name}",
|
|
12
|
+
callName: "Call {name}",
|
|
13
|
+
purgedName: "Removed contact",
|
|
14
|
+
purged: "Personal data erased",
|
|
15
|
+
unavailable: "Contact details unavailable"
|
|
16
|
+
};
|
|
17
|
+
const telHref = (phone) => {
|
|
18
|
+
const extension = /[\s,;]*(?:ext\.?|extn\.?|extension|x|#|lok(?:al)?\.?)\s*:?\s*(\d+)\s*$/i.exec(phone);
|
|
19
|
+
const number = (extension ? phone.slice(0, extension.index) : phone).trim().replace(/(?!^\+)[^\d]/g, "");
|
|
20
|
+
return `tel:${number}${extension ? `;ext=${extension[1]}` : ""}`;
|
|
21
|
+
};
|
|
22
|
+
const PersonGlyph = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [
|
|
23
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8", r: "4" }),
|
|
24
|
+
/* @__PURE__ */ jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })
|
|
25
|
+
] });
|
|
26
|
+
function ContactCard({
|
|
27
|
+
name,
|
|
28
|
+
role,
|
|
29
|
+
avatar,
|
|
30
|
+
email,
|
|
31
|
+
phone,
|
|
32
|
+
actions,
|
|
33
|
+
stats,
|
|
34
|
+
href,
|
|
35
|
+
renderLink,
|
|
36
|
+
nameAs: NameTag = "div",
|
|
37
|
+
state = "active",
|
|
38
|
+
bare = false,
|
|
39
|
+
labels: labelsProp,
|
|
40
|
+
className,
|
|
41
|
+
...props
|
|
42
|
+
}) {
|
|
43
|
+
const uixLabels = useUixLabels();
|
|
44
|
+
const labels = { ...DEFAULT_CONTACT_CARD_LABELS, ...uixLabels.contactCard, ...labelsProp };
|
|
45
|
+
const purged = state === "purged";
|
|
46
|
+
const reachable = state === "active";
|
|
47
|
+
const shownName = purged ? labels.purgedName : name ?? "";
|
|
48
|
+
const note = purged ? labels.purged : state === "unavailable" ? labels.unavailable : null;
|
|
49
|
+
const nameContent = href && !purged ? renderUixLink(renderLink, { href, children: shownName }) : shownName;
|
|
50
|
+
const hasActions = reachable && (email || phone || actions);
|
|
51
|
+
const contact = /* @__PURE__ */ jsxs("div", { className: cx("uix-contact", bare && "uix-contact--bare", bare && className), "data-state": state, ...bare ? props : {}, children: [
|
|
52
|
+
purged || avatar == null ? /* @__PURE__ */ jsx("span", { className: "uix-avatar uix-avatar--lg uix-contact__placeholder", "aria-hidden": "true", children: /* @__PURE__ */ jsx(PersonGlyph, {}) }) : avatar,
|
|
53
|
+
/* @__PURE__ */ jsx(NameTag, { className: "uix-contact__name", children: nameContent }),
|
|
54
|
+
role != null && !purged && /* @__PURE__ */ jsx("div", { className: "uix-contact__role", children: role }),
|
|
55
|
+
note && /* @__PURE__ */ jsx("div", { className: "uix-contact__note", children: note }),
|
|
56
|
+
reachable && (email || phone) && /* @__PURE__ */ jsxs("div", { className: "uix-contact__details", children: [
|
|
57
|
+
email && /* @__PURE__ */ jsx("span", { className: "uix-contact__detail", children: email }),
|
|
58
|
+
phone && /* @__PURE__ */ jsx("span", { className: "uix-contact__detail", children: phone })
|
|
59
|
+
] }),
|
|
60
|
+
stats != null && !purged && /* @__PURE__ */ jsx("div", { className: "uix-contact__stats", children: stats }),
|
|
61
|
+
hasActions && /* @__PURE__ */ jsxs("div", { className: "uix-contact__actions", children: [
|
|
62
|
+
email && /* @__PURE__ */ jsx(ButtonLink, { href: `mailto:${email}`, size: "sm", variant: "primary", "aria-label": fillLabel(labels.emailName, { name: shownName }), children: labels.email }),
|
|
63
|
+
phone && /* @__PURE__ */ jsx(ButtonLink, { href: telHref(phone), size: "sm", "aria-label": fillLabel(labels.callName, { name: shownName }), children: labels.call }),
|
|
64
|
+
actions
|
|
65
|
+
] })
|
|
66
|
+
] });
|
|
67
|
+
if (bare) return contact;
|
|
68
|
+
return /* @__PURE__ */ jsx("div", { className: cx("uix-card", className), ...props, children: contact });
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
ContactCard,
|
|
72
|
+
DEFAULT_CONTACT_CARD_LABELS,
|
|
73
|
+
telHref
|
|
74
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
interface CountProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
|
|
5
|
+
/** The number shown. */
|
|
6
|
+
value: number;
|
|
7
|
+
/**
|
|
8
|
+
* Formats the number, e.g. `n > 99 ? '99+' : String(n)` or your i18n formatter. The default is
|
|
9
|
+
* `toLocaleString()` in the runtime's locale: pass `format` when the server renders it, so the
|
|
10
|
+
* server's and the browser's thousands separators cannot differ.
|
|
11
|
+
*/
|
|
12
|
+
format?: (value: number) => string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A number that ticks once when it changes (HAR-2587, motion standard §2.8): a badge count, a
|
|
16
|
+
* stat tile's value, an unread total. The new figure slides 4 px in from below when it went
|
|
17
|
+
* up and from above when it went down, with a fade; never on the first render, never a loop,
|
|
18
|
+
* never a count-up through the numbers between. Tabular figures, so neighbours do not shift.
|
|
19
|
+
* Under reduced motion it only fades. It announces nothing: say what changed where it
|
|
20
|
+
* matters (a toast, `ChangeHighlight announce`).
|
|
21
|
+
*/
|
|
22
|
+
declare function Count({ value, format, className, ...props }: CountProps): react.JSX.Element;
|
|
23
|
+
|
|
24
|
+
export { Count, type CountProps };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
function Count({ value, format, className, ...props }) {
|
|
6
|
+
const ref = useRef(null);
|
|
7
|
+
const previous = useRef(value);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const before = previous.current;
|
|
10
|
+
previous.current = value;
|
|
11
|
+
const el = ref.current;
|
|
12
|
+
if (!el || Object.is(before, value) || !Number.isFinite(value) || !Number.isFinite(before)) return;
|
|
13
|
+
if (el.hasAttribute("data-uix-tick")) {
|
|
14
|
+
el.removeAttribute("data-uix-tick");
|
|
15
|
+
void el.offsetWidth;
|
|
16
|
+
}
|
|
17
|
+
el.setAttribute("data-uix-tick", value > before ? "up" : "down");
|
|
18
|
+
}, [value]);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const el = ref.current;
|
|
21
|
+
if (!el) return void 0;
|
|
22
|
+
const done = (event) => {
|
|
23
|
+
if (event.target === el) el.removeAttribute("data-uix-tick");
|
|
24
|
+
};
|
|
25
|
+
el.addEventListener("animationend", done);
|
|
26
|
+
return () => el.removeEventListener("animationend", done);
|
|
27
|
+
}, []);
|
|
28
|
+
return /* @__PURE__ */ jsx("span", { ref, className: cx("uix-count", className), ...props, children: format ? format(value) : value.toLocaleString() });
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
Count
|
|
32
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useId, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
|
+
import { settleDrop } from "../hooks/useKeyboardGrab.js";
|
|
5
6
|
import { fillLabel } from "../fill-label.js";
|
|
6
7
|
import { GripVerticalIcon, Maximize2Icon } from "../icons/components.js";
|
|
7
8
|
import {
|
|
@@ -58,12 +59,25 @@ function DashboardCanvas({
|
|
|
58
59
|
const live = useRef({ layout, onLayoutChange, labels, itemLabel });
|
|
59
60
|
live.current = { layout, onLayoutChange, labels, itemLabel };
|
|
60
61
|
useEffect(() => () => dragRef.current?.stop(), []);
|
|
62
|
+
const settle = useRef(null);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const pending = settle.current;
|
|
65
|
+
if (!pending || preview) return;
|
|
66
|
+
const item = layout.find((entry) => entry.id === pending.id);
|
|
67
|
+
if (!item || item.col !== pending.col || item.row !== pending.row) {
|
|
68
|
+
if (layout !== pending.layout) settle.current = null;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
settle.current = null;
|
|
72
|
+
settleDrop(Array.from(gridRef.current?.children ?? []).find((el) => el.dataset.canvasId === pending.id));
|
|
73
|
+
});
|
|
61
74
|
const describe = (item, kind) => kind === "move" ? fillLabel(labels.moved, { label: itemLabel(item), col: item.col + 1, row: item.row + 1 }) : fillLabel(labels.resized, { label: itemLabel(item), w: item.w, h: item.h });
|
|
62
75
|
const commit = (id, placement, kind, source) => {
|
|
63
76
|
const state = live.current;
|
|
64
77
|
const next = placeCanvasItem(state.layout, id, placement);
|
|
65
78
|
const placed = next.find((item) => item.id === id);
|
|
66
79
|
if (!placed) return;
|
|
80
|
+
if (source === "pointer" && kind === "move") settle.current = { id, col: placed.col, row: placed.row, layout: state.layout };
|
|
67
81
|
state.onLayoutChange?.(next, { id, kind, source });
|
|
68
82
|
setAnnouncement(describe(placed, kind));
|
|
69
83
|
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
import { UixRenderLink } from '../link.js';
|
|
4
|
+
import { FilterField, FilterValue, SummarizeFilterOptions } from '../filter-model.js';
|
|
5
|
+
import { TableDensity } from './Table.js';
|
|
6
|
+
import { SavedViewMenuProps, ViewMenuProps } from './TableControls.js';
|
|
7
|
+
|
|
8
|
+
/** Every word the workspace renders or announces. `{…}` are placeholders. */
|
|
9
|
+
interface DataWorkspaceLabels {
|
|
10
|
+
search: string;
|
|
11
|
+
searchPlaceholder: string;
|
|
12
|
+
addFilter: string;
|
|
13
|
+
clearFilters: string;
|
|
14
|
+
/** The saved-view trigger's name before the current view: "View: {name}". */
|
|
15
|
+
savedView: string;
|
|
16
|
+
view: string;
|
|
17
|
+
saveView: string;
|
|
18
|
+
saveViewAs: string;
|
|
19
|
+
maximize: string;
|
|
20
|
+
restore: string;
|
|
21
|
+
selectAll: string;
|
|
22
|
+
/** `{label}`: the row's id. */
|
|
23
|
+
selectRow: string;
|
|
24
|
+
/** `{count}` selected, in the bulk bar. */
|
|
25
|
+
selected: string;
|
|
26
|
+
bulkActions: string;
|
|
27
|
+
/** The record-id cell's ↗ button. `{label}`. */
|
|
28
|
+
open: string;
|
|
29
|
+
rowActions: string;
|
|
30
|
+
/** Offset footer: `{from}`, `{to}`, `{total}`. */
|
|
31
|
+
range: string;
|
|
32
|
+
rowsPerPage: string;
|
|
33
|
+
loading: string;
|
|
34
|
+
empty: string;
|
|
35
|
+
emptyDetail: string;
|
|
36
|
+
filteredEmpty: string;
|
|
37
|
+
filteredEmptyDetail: string;
|
|
38
|
+
error: string;
|
|
39
|
+
retry: string;
|
|
40
|
+
}
|
|
41
|
+
declare const DEFAULT_DATA_WORKSPACE_LABELS: DataWorkspaceLabels;
|
|
42
|
+
interface DataWorkspaceColumn<T> {
|
|
43
|
+
id: string;
|
|
44
|
+
header: ReactNode;
|
|
45
|
+
cell: (row: T) => ReactNode;
|
|
46
|
+
sortable?: boolean;
|
|
47
|
+
/** `end` for numbers and amounts. */
|
|
48
|
+
align?: 'start' | 'end';
|
|
49
|
+
/** A CSS width for the column, e.g. `12rem`. */
|
|
50
|
+
width?: string;
|
|
51
|
+
}
|
|
52
|
+
interface DataWorkspaceSort {
|
|
53
|
+
columnId: string;
|
|
54
|
+
direction: 'ascending' | 'descending';
|
|
55
|
+
}
|
|
56
|
+
/** The record-id column: the first column, the row's way into its record. */
|
|
57
|
+
interface DataWorkspaceIdColumn<T> {
|
|
58
|
+
header: ReactNode;
|
|
59
|
+
/** The id as shown, e.g. "INC-20418". Also names the row's checkbox and ↗ button. */
|
|
60
|
+
text: (row: T) => string;
|
|
61
|
+
/** The id button opens a peek of the record. */
|
|
62
|
+
onPeek?: (row: T) => void;
|
|
63
|
+
/** The ↗ link goes to the full record. */
|
|
64
|
+
href?: (row: T) => string;
|
|
65
|
+
renderLink?: UixRenderLink;
|
|
66
|
+
}
|
|
67
|
+
type DataWorkspacePagination = {
|
|
68
|
+
mode: 'offset';
|
|
69
|
+
page: number;
|
|
70
|
+
pageSize: number;
|
|
71
|
+
total: number;
|
|
72
|
+
onPageChange: (page: number) => void;
|
|
73
|
+
pageSizes?: readonly number[];
|
|
74
|
+
onPageSizeChange?: (size: number) => void;
|
|
75
|
+
} | {
|
|
76
|
+
mode: 'cursor';
|
|
77
|
+
hasPrevious: boolean;
|
|
78
|
+
hasNext: boolean;
|
|
79
|
+
onFirst?: () => void;
|
|
80
|
+
onPrevious: () => void;
|
|
81
|
+
onNext: () => void;
|
|
82
|
+
summary?: ReactNode;
|
|
83
|
+
pageSize?: number;
|
|
84
|
+
pageSizes?: readonly number[];
|
|
85
|
+
onPageSizeChange?: (size: number) => void;
|
|
86
|
+
};
|
|
87
|
+
interface DataWorkspaceProps<T> extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
|
|
88
|
+
/** Names the workspace region and the table. */
|
|
89
|
+
label: string;
|
|
90
|
+
rows: readonly T[];
|
|
91
|
+
getRowId: (row: T) => string;
|
|
92
|
+
columns: readonly DataWorkspaceColumn<T>[];
|
|
93
|
+
idColumn?: DataWorkspaceIdColumn<T>;
|
|
94
|
+
sort?: DataWorkspaceSort | null;
|
|
95
|
+
onSortChange?: (sort: DataWorkspaceSort | null) => void;
|
|
96
|
+
/** The selected row ids. Kept by id, so a refresh or a new page keeps them. */
|
|
97
|
+
selection?: ReadonlySet<string>;
|
|
98
|
+
onSelectionChange?: (selection: Set<string>) => void;
|
|
99
|
+
/** The bulk bar's actions for the selection (a `Menu` for the overflow). */
|
|
100
|
+
bulkActions?: (selectedIds: string[]) => ReactNode;
|
|
101
|
+
/**
|
|
102
|
+
* A row's actions (a `Menu`, quick buttons), in a column frozen at the right edge and sized to
|
|
103
|
+
* them. Return `null` for a row without actions; with none on the page there is no column.
|
|
104
|
+
*/
|
|
105
|
+
rowActions?: (row: T) => ReactNode;
|
|
106
|
+
/** More props for a row, e.g. a data attribute that marks it changed (see ChangeHighlight, PR #164). */
|
|
107
|
+
getRowProps?: (row: T) => HTMLAttributes<HTMLTableRowElement> & Record<`data-${string}`, string | undefined>;
|
|
108
|
+
density?: TableDensity;
|
|
109
|
+
/**
|
|
110
|
+
* Toolbar: the search field. Ctrl/⌘ + / focuses it from inside the workspace, or from the page
|
|
111
|
+
* when nothing has focus and this is the page's first workspace.
|
|
112
|
+
*/
|
|
113
|
+
search?: {
|
|
114
|
+
value: string;
|
|
115
|
+
onChange: (value: string) => void;
|
|
116
|
+
label?: string;
|
|
117
|
+
placeholder?: string;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Toolbar: filter chips and "+ Filter" over `FilterEditor`. A chip whose value holds ids
|
|
121
|
+
* restored from a URL or saved view asks `field.resolveLabels` for their names.
|
|
122
|
+
*/
|
|
123
|
+
filters?: {
|
|
124
|
+
fields: readonly FilterField[];
|
|
125
|
+
values: Readonly<Record<string, FilterValue | undefined>>;
|
|
126
|
+
onChange: (fieldId: string, value: FilterValue | undefined) => void;
|
|
127
|
+
/** Rows matching each filter, shown on its chip. */
|
|
128
|
+
counts?: Readonly<Record<string, number>>;
|
|
129
|
+
summary?: SummarizeFilterOptions;
|
|
130
|
+
};
|
|
131
|
+
/** Toolbar: the saved-view trigger, named by the current view, opening a `SavedViewMenu`. */
|
|
132
|
+
savedViews?: {
|
|
133
|
+
current: ReactNode;
|
|
134
|
+
menu: SavedViewMenuProps;
|
|
135
|
+
};
|
|
136
|
+
/** Toolbar: the View button, opening a `ViewMenu` (columns, density). */
|
|
137
|
+
viewMenu?: ViewMenuProps;
|
|
138
|
+
/** The view differs from its saved version: Save changes and Save as show. */
|
|
139
|
+
viewChanged?: boolean;
|
|
140
|
+
onSaveView?: () => void;
|
|
141
|
+
onSaveViewAs?: () => void;
|
|
142
|
+
/** Toolbar: a maximize toggle; Escape restores, unless an open popover or menu takes it first. */
|
|
143
|
+
maximized?: boolean;
|
|
144
|
+
onMaximizedChange?: (maximized: boolean) => void;
|
|
145
|
+
/** Toolbar: the product's own actions at the end ("New incident"). */
|
|
146
|
+
toolbarEnd?: ReactNode;
|
|
147
|
+
pagination?: DataWorkspacePagination;
|
|
148
|
+
/** `loading`: the first load (skeleton rows). `error`: with `onRetry`. */
|
|
149
|
+
status?: 'ready' | 'loading' | 'error';
|
|
150
|
+
/** A refetch (filter, sort, page, saved view): the rows stay under `TableWrap refreshing`. */
|
|
151
|
+
refreshing?: boolean;
|
|
152
|
+
onRetry?: () => void;
|
|
153
|
+
labels?: Partial<DataWorkspaceLabels>;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The generic presentation half of a product's data table (HAR-1388; TENSOR `data-table.tsx`,
|
|
157
|
+
* MOTUS `UixDataWorkspace.tsx`; UIx ADR-0005). Composes the kit's pieces in one order: saved
|
|
158
|
+
* view · Save changes / Save as (when the view changed) · filter chips and "+ Filter" ·
|
|
159
|
+
* search (Ctrl/⌘ + /) · View · maximize · the product's actions; a `Table` with a record-id cell (id → peek, ↗ → record),
|
|
160
|
+
* selection with Shift-click ranges, a frozen row-actions column and a `BulkBar`; an "x–y of N"
|
|
161
|
+
* footer with rows per page and `Pagination` (offset or cursor); first-load, empty,
|
|
162
|
+
* filtered-empty and error states, and a refetch that keeps the rows. Prop-driven: the product
|
|
163
|
+
* keeps its surface registry, URL state, data fetching and permissions.
|
|
164
|
+
*/
|
|
165
|
+
declare function DataWorkspace<T>({ label, rows, getRowId, columns, idColumn, sort, onSortChange, selection, onSelectionChange, bulkActions, rowActions, getRowProps, density, search, filters, savedViews, viewMenu, viewChanged, onSaveView, onSaveViewAs, maximized, onMaximizedChange, toolbarEnd, pagination, status, refreshing, onRetry, labels: labelOverrides, className, ...props }: DataWorkspaceProps<T>): react.JSX.Element;
|
|
166
|
+
|
|
167
|
+
export { DEFAULT_DATA_WORKSPACE_LABELS, DataWorkspace, type DataWorkspaceColumn, type DataWorkspaceIdColumn, type DataWorkspaceLabels, type DataWorkspacePagination, type DataWorkspaceProps, type DataWorkspaceSort };
|