@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,123 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, Fragment, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
const allowed = (text, mode) => {
|
|
6
|
+
const plain = text.normalize("NFKC");
|
|
7
|
+
return mode === "numeric" ? plain.replace(/\D+/g, "") : plain.replace(/[^0-9a-z]+/gi, "").toUpperCase();
|
|
8
|
+
};
|
|
9
|
+
const clean = (text, mode, length) => allowed(text, mode).slice(0, length);
|
|
10
|
+
const OtpInput = forwardRef(function OtpInput2({
|
|
11
|
+
length: lengthProp = 6,
|
|
12
|
+
value: valueProp,
|
|
13
|
+
defaultValue = "",
|
|
14
|
+
onValueChange,
|
|
15
|
+
onComplete,
|
|
16
|
+
mode = "numeric",
|
|
17
|
+
groupSize,
|
|
18
|
+
invalid,
|
|
19
|
+
"aria-invalid": ariaInvalid,
|
|
20
|
+
className,
|
|
21
|
+
style,
|
|
22
|
+
autoComplete = "one-time-code",
|
|
23
|
+
onFocus,
|
|
24
|
+
onBlur,
|
|
25
|
+
onSelect,
|
|
26
|
+
disabled,
|
|
27
|
+
...props
|
|
28
|
+
}, forwardedRef) {
|
|
29
|
+
const length = Math.max(1, Math.floor(lengthProp));
|
|
30
|
+
const [internal, setInternal] = useState(() => clean(defaultValue, mode, length));
|
|
31
|
+
const value = valueProp !== void 0 ? clean(valueProp, mode, length) : internal;
|
|
32
|
+
const [focused, setFocused] = useState(false);
|
|
33
|
+
const inputRef = useRef(null);
|
|
34
|
+
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
35
|
+
const completed = useRef(value.length === length ? value : null);
|
|
36
|
+
const update = (raw) => {
|
|
37
|
+
const next = clean(raw, mode, length);
|
|
38
|
+
if (valueProp === void 0) setInternal(next);
|
|
39
|
+
if (next !== value) onValueChange?.(next);
|
|
40
|
+
};
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (value.length === length && completed.current !== value) {
|
|
43
|
+
completed.current = value;
|
|
44
|
+
onComplete?.(value);
|
|
45
|
+
} else if (value.length < length) {
|
|
46
|
+
completed.current = null;
|
|
47
|
+
}
|
|
48
|
+
}, [value, length]);
|
|
49
|
+
const toEnd = () => {
|
|
50
|
+
const input = inputRef.current;
|
|
51
|
+
if (!input) return;
|
|
52
|
+
const all = input.value.length > 0 && input.selectionStart === 0 && input.selectionEnd === input.value.length;
|
|
53
|
+
if (!all && (input.selectionStart !== input.value.length || input.selectionEnd !== input.value.length)) {
|
|
54
|
+
try {
|
|
55
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const isInvalid = !!invalid || ariaInvalid === true || ariaInvalid === "true";
|
|
61
|
+
const active = focused ? Math.min(value.length, length - 1) : -1;
|
|
62
|
+
const group = groupSize && groupSize > 0 && groupSize < length ? Math.floor(groupSize) : 0;
|
|
63
|
+
return /* @__PURE__ */ jsxs(
|
|
64
|
+
"div",
|
|
65
|
+
{
|
|
66
|
+
className: cx("uix-otp", className),
|
|
67
|
+
style: { "--uix-otp-cells": length, "--uix-otp-seps": group ? Math.ceil(length / group) - 1 : 0, ...style },
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
"input",
|
|
71
|
+
{
|
|
72
|
+
...props,
|
|
73
|
+
ref: inputRef,
|
|
74
|
+
className: "uix-otp__input",
|
|
75
|
+
type: "text",
|
|
76
|
+
inputMode: mode === "numeric" ? "numeric" : "text",
|
|
77
|
+
autoComplete,
|
|
78
|
+
autoCapitalize: mode === "alphanumeric" ? "characters" : "off",
|
|
79
|
+
autoCorrect: "off",
|
|
80
|
+
spellCheck: false,
|
|
81
|
+
pattern: mode === "numeric" ? `[0-9]{${length}}` : `[0-9A-Za-z]{${length}}`,
|
|
82
|
+
dir: "ltr",
|
|
83
|
+
value,
|
|
84
|
+
disabled,
|
|
85
|
+
"aria-invalid": isInvalid || void 0,
|
|
86
|
+
onChange: (event) => update(event.target.value),
|
|
87
|
+
onPaste: (event) => {
|
|
88
|
+
const usable = allowed(event.clipboardData?.getData("text") ?? "", mode);
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
if (!usable) return;
|
|
91
|
+
const input = event.currentTarget;
|
|
92
|
+
const start = input.selectionStart ?? value.length;
|
|
93
|
+
const end = input.selectionEnd ?? value.length;
|
|
94
|
+
const whole = start === 0 && end === value.length;
|
|
95
|
+
update(whole ? usable : `${value.slice(0, start)}${usable}${value.slice(end)}`);
|
|
96
|
+
},
|
|
97
|
+
onFocus: (event) => {
|
|
98
|
+
setFocused(true);
|
|
99
|
+
toEnd();
|
|
100
|
+
onFocus?.(event);
|
|
101
|
+
},
|
|
102
|
+
onBlur: (event) => {
|
|
103
|
+
setFocused(false);
|
|
104
|
+
onBlur?.(event);
|
|
105
|
+
},
|
|
106
|
+
onSelect: (event) => {
|
|
107
|
+
toEnd();
|
|
108
|
+
onSelect?.(event);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
Array.from({ length }, (_, index) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
113
|
+
group > 0 && index > 0 && index % group === 0 && /* @__PURE__ */ jsx("span", { className: "uix-otp__sep", "aria-hidden": "true" }),
|
|
114
|
+
/* @__PURE__ */ jsx("span", { className: "uix-otp__cell", "aria-hidden": "true", "data-active": index === active || void 0, children: value[index] ?? "" })
|
|
115
|
+
] }, index))
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
OtpInput.displayName = "OtpInput";
|
|
121
|
+
export {
|
|
122
|
+
OtpInput
|
|
123
|
+
};
|
|
@@ -17,11 +17,17 @@ interface PageHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
|
17
17
|
helpLabel?: string;
|
|
18
18
|
/** Right-aligned action area (buttons, menus). */
|
|
19
19
|
actions?: ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* `display` is the staff type preset (HAR-1815; TENSOR, MOTUS ADR-0047): a 40 px heading-font
|
|
22
|
+
* title, a bold hushed eyebrow, a body-size hushed subtitle up to 42rem, and the actions
|
|
23
|
+
* aligned to the bottom of the titles. `default` is the 24 px header.
|
|
24
|
+
*/
|
|
25
|
+
size?: 'default' | 'display';
|
|
20
26
|
}
|
|
21
27
|
/**
|
|
22
28
|
* Page header — eyebrow / title (+ optional `help` ?) / subtitle + actions row, over `.uix-page-header`.
|
|
23
29
|
* The hairline + breathing room below come from the component CSS.
|
|
24
30
|
*/
|
|
25
|
-
declare function PageHeader({ eyebrow, title, subtitle, help, helpLabel, actions, as: TitleTag, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
31
|
+
declare function PageHeader({ eyebrow, title, subtitle, help, helpLabel, actions, as: TitleTag, size, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
26
32
|
|
|
27
33
|
export { PageHeader, type PageHeaderProps };
|
|
@@ -2,9 +2,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { cx } from "../cx.js";
|
|
3
3
|
import { InfoTip } from "./InfoTip.js";
|
|
4
4
|
import { hasHelp, helpLabelFor } from "../info-tip-model.js";
|
|
5
|
-
function PageHeader({ eyebrow, title, subtitle, help, helpLabel, actions, as: TitleTag = "h1", className, ...props }) {
|
|
5
|
+
function PageHeader({ eyebrow, title, subtitle, help, helpLabel, actions, as: TitleTag = "h1", size = "default", className, ...props }) {
|
|
6
6
|
const heading = /* @__PURE__ */ jsx(TitleTag, { className: "uix-page-header__title", children: title });
|
|
7
|
-
return /* @__PURE__ */ jsxs("header", { className: cx("uix-page-header", className), ...props, children: [
|
|
7
|
+
return /* @__PURE__ */ jsxs("header", { className: cx("uix-page-header", size === "display" && "uix-page-header--display", className), ...props, children: [
|
|
8
8
|
/* @__PURE__ */ jsxs("div", { className: "uix-page-header__titles", children: [
|
|
9
9
|
eyebrow != null && /* @__PURE__ */ jsx("div", { className: "uix-page-header__eyebrow", children: eyebrow }),
|
|
10
10
|
hasHelp(help) ? /* @__PURE__ */ jsxs("div", { className: "uix-page-header__title-row", children: [
|
|
@@ -2,6 +2,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Fragment as Fragment2 } from "react";
|
|
3
3
|
import { cx } from "../cx.js";
|
|
4
4
|
import { renderUixLink } from "../link.js";
|
|
5
|
+
import { ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon } from "../icons/components.js";
|
|
5
6
|
function getPageNumbers(page, total) {
|
|
6
7
|
if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
|
|
7
8
|
const pages = [1];
|
|
@@ -59,19 +60,19 @@ function Pagination({
|
|
|
59
60
|
if (mode === "cursor") {
|
|
60
61
|
const linked = firstHref != null || previousHref != null || nextHref != null;
|
|
61
62
|
controls = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
62
|
-
(onFirst || firstHref != null) && render({ key: "first", content:
|
|
63
|
-
render({ key: "prev", content:
|
|
63
|
+
(onFirst || firstHref != null) && render({ key: "first", content: /* @__PURE__ */ jsx(ChevronsLeftIcon, {}), label: labels.first, disabled: !hasPrevious, href: linked ? firstHref ?? "" : void 0, onClick: onFirst }),
|
|
64
|
+
render({ key: "prev", content: /* @__PURE__ */ jsx(ChevronLeftIcon, {}), label: labels.previous, disabled: !hasPrevious, href: linked ? previousHref ?? "" : void 0, onClick: onPrevious }),
|
|
64
65
|
summary != null && /* @__PURE__ */ jsx("span", { className: "uix-pagination__summary", children: summary }),
|
|
65
|
-
render({ key: "next", content:
|
|
66
|
+
render({ key: "next", content: /* @__PURE__ */ jsx(ChevronRightIcon, {}), label: labels.next, disabled: !hasNext, href: linked ? nextHref ?? "" : void 0, onClick: onNext })
|
|
66
67
|
] });
|
|
67
68
|
} else {
|
|
68
69
|
const go = (target) => hrefFor ? { href: hrefFor(target) } : { onClick: () => onChange?.(target) };
|
|
69
70
|
controls = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
70
|
-
render({ key: "prev", content:
|
|
71
|
+
render({ key: "prev", content: /* @__PURE__ */ jsx(ChevronLeftIcon, {}), label: labels.previous, disabled: page <= 1, ...go(page - 1) }),
|
|
71
72
|
getPageNumbers(page, pageCount).map(
|
|
72
73
|
(p, i) => p === "ellipsis" ? /* @__PURE__ */ jsx("span", { className: "uix-pagination__ellipsis", children: "\u2026" }, `e${i}`) : render({ key: `p${p}`, content: p, current: p === page, ...go(p) })
|
|
73
74
|
),
|
|
74
|
-
render({ key: "next", content:
|
|
75
|
+
render({ key: "next", content: /* @__PURE__ */ jsx(ChevronRightIcon, {}), label: labels.next, disabled: page >= pageCount, ...go(page + 1) })
|
|
75
76
|
] });
|
|
76
77
|
}
|
|
77
78
|
return /* @__PURE__ */ jsx("nav", { "aria-label": labels.region, className: cx("uix-pagination", className), ...props, children: controls });
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { InputProps } from './Input.js';
|
|
3
|
+
|
|
4
|
+
/** The words `PasswordInput` renders. */
|
|
5
|
+
interface PasswordInputLabels {
|
|
6
|
+
/**
|
|
7
|
+
* Name of the reveal toggle. It stays the same while the password shows: the toggle's pressed
|
|
8
|
+
* state (`aria-pressed`) says whether it is on.
|
|
9
|
+
*/
|
|
10
|
+
show: string;
|
|
11
|
+
}
|
|
12
|
+
declare const DEFAULT_PASSWORD_INPUT_LABELS: PasswordInputLabels;
|
|
13
|
+
interface PasswordInputProps extends Omit<InputProps, 'type'> {
|
|
14
|
+
/** Controlled: whether the password shows as text. */
|
|
15
|
+
revealed?: boolean;
|
|
16
|
+
/** Uncontrolled: shown as text at first. Default `false`. */
|
|
17
|
+
defaultRevealed?: boolean;
|
|
18
|
+
onRevealedChange?: (revealed: boolean) => void;
|
|
19
|
+
/** Each word defaults to `UixLabelsProvider` `passwordInput`, then to English. */
|
|
20
|
+
labels?: Partial<PasswordInputLabels>;
|
|
21
|
+
/** Class of the wrapping `InputGroup` (`className` goes on the input). */
|
|
22
|
+
groupClassName?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A password field with a reveal toggle (HAR-1376): an `Input type="password"` and, inside its end,
|
|
26
|
+
* a toggle button (`aria-pressed`) that shows the password as text and hides it again. A pointer
|
|
27
|
+
* press leaves focus in the field; the keyboard reaches the toggle with Tab. The password is
|
|
28
|
+
* hidden again when its form submits, so a password manager sees a password field. `autoComplete`
|
|
29
|
+
* passes through (`current-password`, `new-password`); inside `Field` the label, hint and error
|
|
30
|
+
* land on the input. `ref` is the input.
|
|
31
|
+
*/
|
|
32
|
+
declare const PasswordInput: react.ForwardRefExoticComponent<PasswordInputProps & react.RefAttributes<HTMLInputElement>>;
|
|
33
|
+
|
|
34
|
+
export { DEFAULT_PASSWORD_INPUT_LABELS, PasswordInput, type PasswordInputLabels, type PasswordInputProps };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
import { EyeIcon, EyeOffIcon } from "../icons/components.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
6
|
+
import { Input, InputGroup } from "./Input.js";
|
|
7
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
8
|
+
const DEFAULT_PASSWORD_INPUT_LABELS = { show: "Show password" };
|
|
9
|
+
const PasswordInput = forwardRef(function PasswordInput2({
|
|
10
|
+
revealed: revealedProp,
|
|
11
|
+
defaultRevealed = false,
|
|
12
|
+
onRevealedChange,
|
|
13
|
+
labels: labelOverrides,
|
|
14
|
+
groupClassName,
|
|
15
|
+
disabled,
|
|
16
|
+
size,
|
|
17
|
+
...props
|
|
18
|
+
}, forwardedRef) {
|
|
19
|
+
const labels = { ...DEFAULT_PASSWORD_INPUT_LABELS, ...useUixLabels().passwordInput, ...labelOverrides };
|
|
20
|
+
const [internal, setInternal] = useState(defaultRevealed);
|
|
21
|
+
const revealed = revealedProp ?? internal;
|
|
22
|
+
const inputRef = useRef(null);
|
|
23
|
+
useImperativeHandle(forwardedRef, () => inputRef.current);
|
|
24
|
+
const revealedRef = useRef(revealed);
|
|
25
|
+
revealedRef.current = revealed;
|
|
26
|
+
const setRevealed = (next) => {
|
|
27
|
+
if (revealedProp === void 0) setInternal(next);
|
|
28
|
+
onRevealedChange?.(next);
|
|
29
|
+
};
|
|
30
|
+
const setRef = useRef(setRevealed);
|
|
31
|
+
setRef.current = setRevealed;
|
|
32
|
+
const selection = useRef(null);
|
|
33
|
+
const keepSelection = () => {
|
|
34
|
+
const input = inputRef.current;
|
|
35
|
+
if (!input || input.ownerDocument.activeElement !== input) {
|
|
36
|
+
selection.current = null;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
selection.current = { start: input.selectionStart ?? input.value.length, end: input.selectionEnd ?? input.value.length, direction: input.selectionDirection ?? "none" };
|
|
40
|
+
};
|
|
41
|
+
useIsomorphicLayoutEffect(() => {
|
|
42
|
+
const input = inputRef.current;
|
|
43
|
+
const kept = selection.current;
|
|
44
|
+
selection.current = null;
|
|
45
|
+
if (!input || !kept || input.ownerDocument.activeElement !== input) return;
|
|
46
|
+
void input.offsetWidth;
|
|
47
|
+
try {
|
|
48
|
+
input.setSelectionRange(kept.start, kept.end, kept.direction);
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}, [revealed]);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const form = inputRef.current?.form;
|
|
54
|
+
if (!form) return void 0;
|
|
55
|
+
const onSubmit = () => {
|
|
56
|
+
if (revealedRef.current) setRef.current(false);
|
|
57
|
+
};
|
|
58
|
+
form.addEventListener("submit", onSubmit, true);
|
|
59
|
+
return () => form.removeEventListener("submit", onSubmit, true);
|
|
60
|
+
}, []);
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
InputGroup,
|
|
63
|
+
{
|
|
64
|
+
className: groupClassName,
|
|
65
|
+
trailingAction: /* @__PURE__ */ jsx(
|
|
66
|
+
"button",
|
|
67
|
+
{
|
|
68
|
+
type: "button",
|
|
69
|
+
className: "uix-input-group__action",
|
|
70
|
+
"aria-label": labels.show,
|
|
71
|
+
"aria-pressed": revealed,
|
|
72
|
+
"aria-controls": props.id,
|
|
73
|
+
disabled,
|
|
74
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
75
|
+
onClick: () => {
|
|
76
|
+
keepSelection();
|
|
77
|
+
setRevealed(!revealed);
|
|
78
|
+
},
|
|
79
|
+
children: revealed ? /* @__PURE__ */ jsx(EyeOffIcon, { size: "sm" }) : /* @__PURE__ */ jsx(EyeIcon, { size: "sm" })
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
children: /* @__PURE__ */ jsx(
|
|
83
|
+
Input,
|
|
84
|
+
{
|
|
85
|
+
...props,
|
|
86
|
+
ref: inputRef,
|
|
87
|
+
size,
|
|
88
|
+
disabled,
|
|
89
|
+
type: revealed ? "text" : "password",
|
|
90
|
+
spellCheck: false,
|
|
91
|
+
autoCapitalize: "none"
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
PasswordInput.displayName = "PasswordInput";
|
|
98
|
+
export {
|
|
99
|
+
DEFAULT_PASSWORD_INPUT_LABELS,
|
|
100
|
+
PasswordInput
|
|
101
|
+
};
|
package/dist/components/Peek.js
CHANGED
|
@@ -19,12 +19,12 @@ function Peek({ open, onClose, title, children, footer, onNavPrev, onNavNext, hi
|
|
|
19
19
|
return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-peek", className), "aria-labelledby": title ? titleId : void 0, ...rest, onClick: backdropDismiss(onClose, onClick), children: [
|
|
20
20
|
/* @__PURE__ */ jsxs("div", { className: "uix-peek__header", children: [
|
|
21
21
|
hasNav && /* @__PURE__ */ jsxs("div", { className: "uix-peek__nav", children: [
|
|
22
|
-
/* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": previousLabel, children: /* @__PURE__ */ jsx(ChevronUp, {}) }),
|
|
23
|
-
/* @__PURE__ */ jsx("button", { className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": nextLabel, children: /* @__PURE__ */ jsx(ChevronDown, {}) })
|
|
22
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-peek__navbtn", onClick: () => onNavPrev?.(), "aria-disabled": onNavPrev ? void 0 : true, "aria-label": previousLabel, children: /* @__PURE__ */ jsx(ChevronUp, {}) }),
|
|
23
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "uix-peek__navbtn", onClick: () => onNavNext?.(), "aria-disabled": onNavNext ? void 0 : true, "aria-label": nextLabel, children: /* @__PURE__ */ jsx(ChevronDown, {}) })
|
|
24
24
|
] }),
|
|
25
25
|
title && /* @__PURE__ */ jsx("h2", { className: "uix-peek__title", id: titleId, children: title }),
|
|
26
26
|
hint && /* @__PURE__ */ jsx("span", { className: "uix-peek__hint", children: hint }),
|
|
27
|
-
onClose && /* @__PURE__ */ jsx("button", { className: "uix-peek__close", onClick: onClose, "aria-label": closeLabel, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
27
|
+
onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-peek__close", onClick: onClose, "aria-label": closeLabel, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
28
28
|
] }),
|
|
29
29
|
children != null && /* @__PURE__ */ jsx("div", { className: "uix-peek__body", children }),
|
|
30
30
|
footer != null && /* @__PURE__ */ jsx("div", { className: "uix-peek__footer", children: footer })
|
|
@@ -3,6 +3,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
5
|
import { resolveAnchor, useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
|
|
6
|
+
import { markKeyOpened, watchInputModality } from "../motion.js";
|
|
6
7
|
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
7
8
|
const PLACING = "data-uix-placing";
|
|
8
9
|
const isPopoverOpen = (el) => {
|
|
@@ -54,6 +55,14 @@ function Popover({
|
|
|
54
55
|
el.hidePopover();
|
|
55
56
|
} : void 0
|
|
56
57
|
});
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
watchInputModality();
|
|
60
|
+
const el = ref.current;
|
|
61
|
+
if (!el) return;
|
|
62
|
+
const mark = () => markKeyOpened(el);
|
|
63
|
+
el.addEventListener("beforetoggle", mark);
|
|
64
|
+
return () => el.removeEventListener("beforetoggle", mark);
|
|
65
|
+
}, []);
|
|
57
66
|
useEffect(() => {
|
|
58
67
|
const el = ref.current;
|
|
59
68
|
if (!el || !anchor) return;
|
|
@@ -134,6 +143,8 @@ function Popover({
|
|
|
134
143
|
};
|
|
135
144
|
const onKeyDown = (event) => {
|
|
136
145
|
if (event.key !== "Escape" || !isPopoverOpen(el)) return;
|
|
146
|
+
event.preventDefault();
|
|
147
|
+
event.stopPropagation();
|
|
137
148
|
window.clearTimeout(timer);
|
|
138
149
|
const hadFocus = el.contains(el.ownerDocument.activeElement);
|
|
139
150
|
try {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/** The words of `PrintSignature` (HAR-986). */
|
|
5
|
+
interface PrintLayoutLabels {
|
|
6
|
+
/** The caption under the signature line. */
|
|
7
|
+
signature: string;
|
|
8
|
+
/** The caption of the stamp box. */
|
|
9
|
+
stamp: string;
|
|
10
|
+
}
|
|
11
|
+
declare const DEFAULT_PRINT_LAYOUT_LABELS: PrintLayoutLabels;
|
|
12
|
+
type PrintPageSize = 'A4' | 'A5';
|
|
13
|
+
interface PrintLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
14
|
+
/** The paper. Default `A4` (210 × 297 mm); `A5` is 148 × 210 mm (a poster, a slip). */
|
|
15
|
+
size?: PrintPageSize;
|
|
16
|
+
/** Default `portrait`. */
|
|
17
|
+
orientation?: 'portrait' | 'landscape';
|
|
18
|
+
/** Repeats at the top of every printed page: the practice or store identity. */
|
|
19
|
+
header?: ReactNode;
|
|
20
|
+
/** Repeats at the foot of every printed page, before the page number. */
|
|
21
|
+
footer?: ReactNode;
|
|
22
|
+
/** "page / pages" in the bottom-right page margin of every printed page. Default `true`; never on a `singlePage` layout. */
|
|
23
|
+
pageNumbers?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* One page and no more (a poster, a one-page readout): the header and footer are not
|
|
26
|
+
* repeated, the layout is kept in one piece, and the screen preview is exactly one page tall.
|
|
27
|
+
* When the content does not fit, the preview is outlined and `onOverflowChange(true)` is called,
|
|
28
|
+
* so the product can shorten it before anyone prints.
|
|
29
|
+
*/
|
|
30
|
+
singlePage?: boolean;
|
|
31
|
+
/** Called with `true` when a `singlePage` layout's content is taller than its page, and `false` once it fits again. */
|
|
32
|
+
onOverflowChange?: (overflowing: boolean) => void;
|
|
33
|
+
/**
|
|
34
|
+
* When printing, show only print layouts: everything else on the page (the app shell, the
|
|
35
|
+
* toolbar with the Print button, toasts, dialogs) is hidden. Default `true`.
|
|
36
|
+
*/
|
|
37
|
+
isolate?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A print document frame (HAR-986, HAR-1252; MEDx S20 "Nalaz i mišljenje" and the day list,
|
|
41
|
+
* SHOPx PRE-01 A5 poster and SHOP-03 one-page readout): an A4 or A5 page with the paper's margins,
|
|
42
|
+
* a header and a footer that repeat on every printed page, page numbers in the page margin, and
|
|
43
|
+
* black-on-white tokens for everything inside, on screen (as a paper preview) and on paper. The
|
|
44
|
+
* consumer supplies the sections; the browser's own print (`window.print()`) makes the pages.
|
|
45
|
+
*/
|
|
46
|
+
declare function PrintLayout({ size, orientation, header, footer, pageNumbers, singlePage, onOverflowChange, isolate, className, children, style, ...props }: PrintLayoutProps): react.JSX.Element;
|
|
47
|
+
interface PrintSignatureProps extends HTMLAttributes<HTMLDivElement> {
|
|
48
|
+
/** The caption under the line. Default `labels.signature`. */
|
|
49
|
+
label?: ReactNode;
|
|
50
|
+
/** The signer's printed name and title under the caption, e.g. "dr. Amra Hadžić, spec. pedijatrije". */
|
|
51
|
+
name?: ReactNode;
|
|
52
|
+
/** Place and date at the start of the block, e.g. "Sarajevo, 22. 10. 2026.". */
|
|
53
|
+
placeAndDate?: ReactNode;
|
|
54
|
+
/** A box for the stamp beside the signature. */
|
|
55
|
+
stamp?: boolean;
|
|
56
|
+
labels?: Partial<PrintLayoutLabels>;
|
|
57
|
+
}
|
|
58
|
+
/** A signature line (and an optional stamp box) that is never split across two pages. */
|
|
59
|
+
declare function PrintSignature({ label, name, placeAndDate, stamp, labels: labelsProp, className, ...props }: PrintSignatureProps): react.JSX.Element;
|
|
60
|
+
/** Starts a new printed page here. On screen it is a dashed line, so the preview shows where the page ends. */
|
|
61
|
+
declare function PrintPageBreak({ className, ...props }: HTMLAttributes<HTMLDivElement>): react.JSX.Element;
|
|
62
|
+
|
|
63
|
+
export { DEFAULT_PRINT_LAYOUT_LABELS, PrintLayout, type PrintLayoutLabels, type PrintLayoutProps, PrintPageBreak, type PrintPageSize, PrintSignature, type PrintSignatureProps };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
6
|
+
const DEFAULT_PRINT_LAYOUT_LABELS = { signature: "Signature", stamp: "Stamp" };
|
|
7
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
8
|
+
const PAPER_MM = { A4: [210, 297], A5: [148, 210] };
|
|
9
|
+
const PX_PER_MM = 96 / 25.4;
|
|
10
|
+
function PrintLayout({
|
|
11
|
+
size = "A4",
|
|
12
|
+
orientation = "portrait",
|
|
13
|
+
header,
|
|
14
|
+
footer,
|
|
15
|
+
pageNumbers = true,
|
|
16
|
+
singlePage = false,
|
|
17
|
+
onOverflowChange,
|
|
18
|
+
isolate = true,
|
|
19
|
+
className,
|
|
20
|
+
children,
|
|
21
|
+
style,
|
|
22
|
+
...props
|
|
23
|
+
}) {
|
|
24
|
+
const ref = useRef(null);
|
|
25
|
+
const fitRef = useRef(null);
|
|
26
|
+
const report = useRef(onOverflowChange);
|
|
27
|
+
report.current = onOverflowChange;
|
|
28
|
+
const [scale, setScale] = useState(1);
|
|
29
|
+
const paperMm = PAPER_MM[size][orientation === "landscape" ? 1 : 0];
|
|
30
|
+
const paperPx = paperMm * PX_PER_MM;
|
|
31
|
+
useIsomorphicLayoutEffect(() => {
|
|
32
|
+
const box = fitRef.current;
|
|
33
|
+
if (!box) return void 0;
|
|
34
|
+
const fit = () => {
|
|
35
|
+
const room = box.getBoundingClientRect().width;
|
|
36
|
+
if (room > 0) setScale(room >= paperPx - 0.5 ? 1 : room / paperPx);
|
|
37
|
+
};
|
|
38
|
+
fit();
|
|
39
|
+
if (typeof ResizeObserver === "undefined") return void 0;
|
|
40
|
+
const observer = new ResizeObserver(fit);
|
|
41
|
+
observer.observe(box);
|
|
42
|
+
return () => observer.disconnect();
|
|
43
|
+
}, [paperPx]);
|
|
44
|
+
const hasHeader = header != null;
|
|
45
|
+
const hasFooter = footer != null;
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const root = ref.current;
|
|
48
|
+
if (!singlePage || !root || typeof ResizeObserver === "undefined") return void 0;
|
|
49
|
+
const content = root.querySelector(":scope > .uix-print-layout__page");
|
|
50
|
+
if (!content) return void 0;
|
|
51
|
+
let last = false;
|
|
52
|
+
let frame = 0;
|
|
53
|
+
const measure = () => {
|
|
54
|
+
frame = 0;
|
|
55
|
+
const over = content.scrollHeight > content.clientHeight + 1;
|
|
56
|
+
if (over === last) return;
|
|
57
|
+
last = over;
|
|
58
|
+
root.toggleAttribute("data-overflowing", over);
|
|
59
|
+
report.current?.(over);
|
|
60
|
+
};
|
|
61
|
+
const observer = new ResizeObserver(() => {
|
|
62
|
+
if (!frame) frame = requestAnimationFrame(measure);
|
|
63
|
+
});
|
|
64
|
+
observer.observe(content);
|
|
65
|
+
for (const cell of Array.from(content.querySelectorAll(":scope > * > tr > td"))) observer.observe(cell);
|
|
66
|
+
measure();
|
|
67
|
+
return () => {
|
|
68
|
+
observer.disconnect();
|
|
69
|
+
if (frame) cancelAnimationFrame(frame);
|
|
70
|
+
};
|
|
71
|
+
}, [singlePage, hasHeader, hasFooter]);
|
|
72
|
+
const page = `uix-print-${size.toLowerCase()}${orientation === "landscape" ? "-landscape" : ""}${pageNumbers && !singlePage ? "" : "-plain"}`;
|
|
73
|
+
return /* @__PURE__ */ jsx("div", { ref: fitRef, className: "uix-print-preview", style: { inlineSize: `${paperMm}mm` }, children: /* @__PURE__ */ jsx(
|
|
74
|
+
"div",
|
|
75
|
+
{
|
|
76
|
+
ref,
|
|
77
|
+
className: cx("uix-print-layout", className),
|
|
78
|
+
"data-size": size,
|
|
79
|
+
"data-orientation": orientation,
|
|
80
|
+
"data-page": page,
|
|
81
|
+
"data-single-page": singlePage ? "" : void 0,
|
|
82
|
+
"data-isolate": isolate ? "" : void 0,
|
|
83
|
+
style: scale < 1 ? { "--uix-print-preview-scale": Number(scale.toFixed(4)), ...style } : style,
|
|
84
|
+
...props,
|
|
85
|
+
children: /* @__PURE__ */ jsxs("table", { className: "uix-print-layout__page", role: "presentation", children: [
|
|
86
|
+
header != null && /* @__PURE__ */ jsx("thead", { className: "uix-print-layout__header", children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("div", { className: "uix-print-layout__bar", children: header }) }) }) }),
|
|
87
|
+
/* @__PURE__ */ jsx("tbody", { className: "uix-print-layout__main", children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "uix-print-layout__body", children }) }) }),
|
|
88
|
+
footer != null && /* @__PURE__ */ jsx("tfoot", { className: "uix-print-layout__footer", children: /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("div", { className: "uix-print-layout__bar", children: footer }) }) }) })
|
|
89
|
+
] })
|
|
90
|
+
}
|
|
91
|
+
) });
|
|
92
|
+
}
|
|
93
|
+
function PrintSignature({ label, name, placeAndDate, stamp = false, labels: labelsProp, className, ...props }) {
|
|
94
|
+
const uixLabels = useUixLabels();
|
|
95
|
+
const labels = { ...DEFAULT_PRINT_LAYOUT_LABELS, ...uixLabels.printLayout, ...labelsProp };
|
|
96
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("uix-print-signature", className), ...props, children: [
|
|
97
|
+
placeAndDate != null && /* @__PURE__ */ jsx("div", { className: "uix-print-signature__place", children: placeAndDate }),
|
|
98
|
+
/* @__PURE__ */ jsxs("div", { className: "uix-print-signature__field", children: [
|
|
99
|
+
/* @__PURE__ */ jsx("div", { className: "uix-print-signature__line", "aria-hidden": "true" }),
|
|
100
|
+
/* @__PURE__ */ jsx("div", { className: "uix-print-signature__caption", children: label ?? labels.signature }),
|
|
101
|
+
name != null && /* @__PURE__ */ jsx("div", { className: "uix-print-signature__name", children: name })
|
|
102
|
+
] }),
|
|
103
|
+
stamp && /* @__PURE__ */ jsx("div", { className: "uix-print-signature__stamp", children: /* @__PURE__ */ jsx("span", { children: labels.stamp }) })
|
|
104
|
+
] });
|
|
105
|
+
}
|
|
106
|
+
function PrintPageBreak({ className, ...props }) {
|
|
107
|
+
return /* @__PURE__ */ jsx("div", { className: cx("uix-print-break", className), role: "presentation", ...props });
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
DEFAULT_PRINT_LAYOUT_LABELS,
|
|
111
|
+
PrintLayout,
|
|
112
|
+
PrintPageBreak,
|
|
113
|
+
PrintSignature
|
|
114
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { SVGAttributes } from 'react';
|
|
3
|
+
import { QrErrorCorrection } from '../qr-model.js';
|
|
4
|
+
|
|
5
|
+
/** The words of a `QrCode` (HAR-1251). */
|
|
6
|
+
interface QrCodeLabels {
|
|
7
|
+
/** Shown in place of the symbol when `value` does not fit in a QR code at the chosen level. */
|
|
8
|
+
tooLong: string;
|
|
9
|
+
}
|
|
10
|
+
/** The English words of `QrCode`, the base `UixLabelsProvider` `qrCode` and `labels` override. */
|
|
11
|
+
declare const DEFAULT_QR_CODE_LABELS: QrCodeLabels;
|
|
12
|
+
interface QrCodeProps extends Omit<SVGAttributes<SVGSVGElement>, 'children' | 'width' | 'height' | 'viewBox'> {
|
|
13
|
+
/** The text to encode, e.g. the campaign URL. Encoded as UTF-8 bytes. */
|
|
14
|
+
value: string;
|
|
15
|
+
/**
|
|
16
|
+
* The symbol's accessible name, e.g. "QR code: open the summer campaign". Say where it leads;
|
|
17
|
+
* the image itself tells a screen-reader user nothing.
|
|
18
|
+
*/
|
|
19
|
+
label: string;
|
|
20
|
+
/**
|
|
21
|
+
* `L` 7 %, `M` 15 % (default), `Q` 25 %, `H` 30 % of the symbol may be lost and still read.
|
|
22
|
+
* Use `Q` or `H` for a poster that may be creased, scuffed or partly covered.
|
|
23
|
+
*/
|
|
24
|
+
errorCorrection?: QrErrorCorrection;
|
|
25
|
+
/**
|
|
26
|
+
* The rendered width and height, quiet zone included: a number of CSS pixels (default 160) or
|
|
27
|
+
* any CSS length (`'32mm'` on paper). Keep at least 2 px per module on a screen and 0.4 mm per
|
|
28
|
+
* module on paper: a 30-character URL at `M` is version 3, 37 modules with the quiet zone, so
|
|
29
|
+
* at least 74 px or 15 mm.
|
|
30
|
+
*/
|
|
31
|
+
size?: number | string;
|
|
32
|
+
/** The smallest version to use, so a set of posters gets symbols of one size. */
|
|
33
|
+
minVersion?: number;
|
|
34
|
+
/** Mark non-ASCII text as UTF-8 with an ECI designator (see `QrEncodeOptions.eci`). Default false. */
|
|
35
|
+
eci?: boolean;
|
|
36
|
+
/** The words. Default: `UixLabelsProvider` `qrCode`, then English. */
|
|
37
|
+
labels?: Partial<QrCodeLabels>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A scannable, print-safe QR code (HAR-1251; SHOPx PRE-01 poster, voucher): `value` as an SVG
|
|
41
|
+
* with the standard four-module quiet zone, dark modules on light paper in every theme, in print
|
|
42
|
+
* and in forced colours (scanners read dark on light; an inverted symbol fails many of them).
|
|
43
|
+
* The encoder is the kit's own (`encodeQr`), so nothing is fetched and no library is added.
|
|
44
|
+
*/
|
|
45
|
+
declare function QrCode({ value, label, errorCorrection, size, minVersion, eci, labels: labelsProp, className, style, ...props }: QrCodeProps): react.JSX.Element;
|
|
46
|
+
|
|
47
|
+
export { DEFAULT_QR_CODE_LABELS, QrCode, type QrCodeLabels, type QrCodeProps };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { cx } from "../cx.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
6
|
+
import { encodeQr, qrPath, QrCapacityError } from "../qr-model.js";
|
|
7
|
+
const DEFAULT_QR_CODE_LABELS = { tooLong: "Too long for a QR code" };
|
|
8
|
+
const QUIET_ZONE = 4;
|
|
9
|
+
function QrCode({ value, label, errorCorrection = "M", size = 160, minVersion, eci = false, labels: labelsProp, className, style, ...props }) {
|
|
10
|
+
const uixLabels = useUixLabels();
|
|
11
|
+
const labels = { ...DEFAULT_QR_CODE_LABELS, ...uixLabels.qrCode, ...labelsProp };
|
|
12
|
+
const matrix = useMemo(() => {
|
|
13
|
+
try {
|
|
14
|
+
return encodeQr(value, { errorCorrection, minVersion, eci });
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof QrCapacityError) return void 0;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}, [value, errorCorrection, minVersion, eci]);
|
|
20
|
+
const length = typeof size === "number" ? `${size}px` : size;
|
|
21
|
+
if (!matrix) {
|
|
22
|
+
return /* @__PURE__ */ jsxs("span", { ...props, className: cx("uix-qr-code", "uix-qr-code--error", className), style: { inlineSize: length, ...style }, children: [
|
|
23
|
+
/* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
|
|
24
|
+
label,
|
|
25
|
+
": "
|
|
26
|
+
] }),
|
|
27
|
+
labels.tooLong
|
|
28
|
+
] });
|
|
29
|
+
}
|
|
30
|
+
const extent = matrix.size + QUIET_ZONE * 2;
|
|
31
|
+
return /* @__PURE__ */ jsxs(
|
|
32
|
+
"svg",
|
|
33
|
+
{
|
|
34
|
+
className: cx("uix-qr-code", className),
|
|
35
|
+
role: "img",
|
|
36
|
+
"aria-label": label,
|
|
37
|
+
viewBox: `0 0 ${extent} ${extent}`,
|
|
38
|
+
width: length,
|
|
39
|
+
height: length,
|
|
40
|
+
shapeRendering: "crispEdges",
|
|
41
|
+
"data-version": matrix.version,
|
|
42
|
+
"data-error-correction": matrix.errorCorrection,
|
|
43
|
+
style,
|
|
44
|
+
...props,
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ jsx("rect", { className: "uix-qr-code__paper", width: extent, height: extent }),
|
|
47
|
+
/* @__PURE__ */ jsx("path", { className: "uix-qr-code__ink", d: qrPath(matrix, QUIET_ZONE) })
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
DEFAULT_QR_CODE_LABELS,
|
|
54
|
+
QrCode
|
|
55
|
+
};
|