@camunda/design-system 0.58.0 → 0.60.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/dist/carbon-compat/accordion-skeleton.d.ts +8 -2
- package/dist/carbon-compat/accordion-skeleton.d.ts.map +1 -1
- package/dist/carbon-compat/accordion-skeleton.js +4 -2
- package/dist/carbon-compat/accordion-skeleton.js.map +1 -1
- package/dist/carbon-compat/c3-data-table.d.ts +7 -1
- package/dist/carbon-compat/c3-data-table.d.ts.map +1 -1
- package/dist/carbon-compat/c3-data-table.js +8 -6
- package/dist/carbon-compat/c3-data-table.js.map +1 -1
- package/dist/carbon-compat/code-snippet.d.ts +2 -2
- package/dist/carbon-compat/code-snippet.d.ts.map +1 -1
- package/dist/carbon-compat/code-snippet.js +8 -6
- package/dist/carbon-compat/code-snippet.js.map +1 -1
- package/dist/carbon-compat/error-boundary.d.ts +2 -1
- package/dist/carbon-compat/error-boundary.d.ts.map +1 -1
- package/dist/carbon-compat/error-boundary.js +11 -1
- package/dist/carbon-compat/error-boundary.js.map +1 -1
- package/dist/carbon-compat/filterable-multi-select.d.ts.map +1 -1
- package/dist/carbon-compat/filterable-multi-select.js +6 -1
- package/dist/carbon-compat/filterable-multi-select.js.map +1 -1
- package/dist/carbon-compat/modal.d.ts.map +1 -1
- package/dist/carbon-compat/modal.js +5 -1
- package/dist/carbon-compat/modal.js.map +1 -1
- package/dist/carbon-compat/multi-select.d.ts.map +1 -1
- package/dist/carbon-compat/multi-select.js +5 -1
- package/dist/carbon-compat/multi-select.js.map +1 -1
- package/dist/carbon-compat/search.d.ts +4 -1
- package/dist/carbon-compat/search.d.ts.map +1 -1
- package/dist/carbon-compat/search.js +4 -2
- package/dist/carbon-compat/search.js.map +1 -1
- package/dist/carbon-compat/table-batch-actions.d.ts +5 -1
- package/dist/carbon-compat/table-batch-actions.d.ts.map +1 -1
- package/dist/carbon-compat/table-batch-actions.js +8 -3
- package/dist/carbon-compat/table-batch-actions.js.map +1 -1
- package/dist/carbon-compat/table-expand.d.ts +5 -1
- package/dist/carbon-compat/table-expand.d.ts.map +1 -1
- package/dist/carbon-compat/table-expand.js +6 -2
- package/dist/carbon-compat/table-expand.js.map +1 -1
- package/dist/carbon-compat/table-select.d.ts +9 -2
- package/dist/carbon-compat/table-select.d.ts.map +1 -1
- package/dist/carbon-compat/table-select.js +5 -2
- package/dist/carbon-compat/table-select.js.map +1 -1
- package/dist/carbon-compat/table-toolbar.d.ts +1 -0
- package/dist/carbon-compat/table-toolbar.d.ts.map +1 -1
- package/dist/carbon-compat/table-toolbar.js +5 -2
- package/dist/carbon-compat/table-toolbar.js.map +1 -1
- package/dist/src/components/ui/alert-dialog.d.ts +11 -1
- package/dist/src/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/src/components/ui/alert-dialog.js +5 -4
- package/dist/src/components/ui/alert-dialog.js.map +1 -1
- package/dist/src/components/ui/alert.d.ts +3 -1
- package/dist/src/components/ui/alert.d.ts.map +1 -1
- package/dist/src/components/ui/alert.js +6 -2
- package/dist/src/components/ui/alert.js.map +1 -1
- package/dist/src/components/ui/app-header.d.ts +1 -0
- package/dist/src/components/ui/app-header.d.ts.map +1 -1
- package/dist/src/components/ui/app-header.js +4 -2
- package/dist/src/components/ui/app-header.js.map +1 -1
- package/dist/src/components/ui/app-sidebar.d.ts +49 -2
- package/dist/src/components/ui/app-sidebar.d.ts.map +1 -1
- package/dist/src/components/ui/app-sidebar.js +74 -22
- package/dist/src/components/ui/app-sidebar.js.map +1 -1
- package/dist/src/components/ui/breadcrumb.d.ts +7 -1
- package/dist/src/components/ui/breadcrumb.d.ts.map +1 -1
- package/dist/src/components/ui/breadcrumb.js +6 -3
- package/dist/src/components/ui/breadcrumb.js.map +1 -1
- package/dist/src/components/ui/c4-provider.d.ts +10 -0
- package/dist/src/components/ui/c4-provider.d.ts.map +1 -1
- package/dist/src/components/ui/c4-provider.js +11 -2
- package/dist/src/components/ui/c4-provider.js.map +1 -1
- package/dist/src/components/ui/card.d.ts +3 -1
- package/dist/src/components/ui/card.d.ts.map +1 -1
- package/dist/src/components/ui/card.js +18 -2
- package/dist/src/components/ui/card.js.map +1 -1
- package/dist/src/components/ui/chart.d.ts +1 -0
- package/dist/src/components/ui/chart.d.ts.map +1 -1
- package/dist/src/components/ui/chart.js +4 -2
- package/dist/src/components/ui/chart.js.map +1 -1
- package/dist/src/components/ui/code-block.d.ts +5 -0
- package/dist/src/components/ui/code-block.d.ts.map +1 -1
- package/dist/src/components/ui/code-block.js +15 -20
- package/dist/src/components/ui/code-block.js.map +1 -1
- package/dist/src/components/ui/combobox.d.ts +2 -0
- package/dist/src/components/ui/combobox.d.ts.map +1 -1
- package/dist/src/components/ui/combobox.js +55 -18
- package/dist/src/components/ui/combobox.js.map +1 -1
- package/dist/src/components/ui/data-table.d.ts +63 -6
- package/dist/src/components/ui/data-table.d.ts.map +1 -1
- package/dist/src/components/ui/data-table.js +38 -23
- package/dist/src/components/ui/data-table.js.map +1 -1
- package/dist/src/components/ui/date-picker.d.ts +1 -0
- package/dist/src/components/ui/date-picker.d.ts.map +1 -1
- package/dist/src/components/ui/date-picker.js +6 -2
- package/dist/src/components/ui/date-picker.js.map +1 -1
- package/dist/src/components/ui/date-range-picker.d.ts +3 -0
- package/dist/src/components/ui/date-range-picker.d.ts.map +1 -1
- package/dist/src/components/ui/date-range-picker.js +26 -5
- package/dist/src/components/ui/date-range-picker.js.map +1 -1
- package/dist/src/components/ui/dialog.d.ts +23 -3
- package/dist/src/components/ui/dialog.d.ts.map +1 -1
- package/dist/src/components/ui/dialog.js +15 -9
- package/dist/src/components/ui/dialog.js.map +1 -1
- package/dist/src/components/ui/file-uploader.d.ts +4 -4
- package/dist/src/components/ui/file-uploader.d.ts.map +1 -1
- package/dist/src/components/ui/file-uploader.js +38 -9
- package/dist/src/components/ui/file-uploader.js.map +1 -1
- package/dist/src/components/ui/form-error-summary.d.ts +13 -0
- package/dist/src/components/ui/form-error-summary.d.ts.map +1 -0
- package/dist/src/components/ui/form-error-summary.js +53 -0
- package/dist/src/components/ui/form-error-summary.js.map +1 -0
- package/dist/src/components/ui/inline-edit.d.ts +1 -1
- package/dist/src/components/ui/inline-edit.d.ts.map +1 -1
- package/dist/src/components/ui/inline-edit.js +17 -2
- package/dist/src/components/ui/inline-edit.js.map +1 -1
- package/dist/src/components/ui/meter.d.ts +1 -0
- package/dist/src/components/ui/meter.d.ts.map +1 -1
- package/dist/src/components/ui/meter.js +7 -3
- package/dist/src/components/ui/meter.js.map +1 -1
- package/dist/src/components/ui/multi-select.d.ts +16 -0
- package/dist/src/components/ui/multi-select.d.ts.map +1 -1
- package/dist/src/components/ui/multi-select.js +86 -23
- package/dist/src/components/ui/multi-select.js.map +1 -1
- package/dist/src/components/ui/multi-step-dialog.d.ts +35 -1
- package/dist/src/components/ui/multi-step-dialog.d.ts.map +1 -1
- package/dist/src/components/ui/multi-step-dialog.js +47 -8
- package/dist/src/components/ui/multi-step-dialog.js.map +1 -1
- package/dist/src/components/ui/nav-breadcrumb.d.ts +29 -12
- package/dist/src/components/ui/nav-breadcrumb.d.ts.map +1 -1
- package/dist/src/components/ui/nav-breadcrumb.js +69 -24
- package/dist/src/components/ui/nav-breadcrumb.js.map +1 -1
- package/dist/src/components/ui/notifications.d.ts +52 -5
- package/dist/src/components/ui/notifications.d.ts.map +1 -1
- package/dist/src/components/ui/notifications.js +25 -10
- package/dist/src/components/ui/notifications.js.map +1 -1
- package/dist/src/components/ui/overflow-strip.d.ts +18 -0
- package/dist/src/components/ui/overflow-strip.d.ts.map +1 -0
- package/dist/src/components/ui/overflow-strip.js +103 -0
- package/dist/src/components/ui/overflow-strip.js.map +1 -0
- package/dist/src/components/ui/scorecard.d.ts +1 -0
- package/dist/src/components/ui/scorecard.d.ts.map +1 -1
- package/dist/src/components/ui/scorecard.js +28 -4
- package/dist/src/components/ui/scorecard.js.map +1 -1
- package/dist/src/components/ui/search-input.d.ts +1 -1
- package/dist/src/components/ui/search-input.d.ts.map +1 -1
- package/dist/src/components/ui/search-input.js +4 -2
- package/dist/src/components/ui/search-input.js.map +1 -1
- package/dist/src/components/ui/select.d.ts +2 -2
- package/dist/src/components/ui/select.d.ts.map +1 -1
- package/dist/src/components/ui/select.js +123 -6
- package/dist/src/components/ui/select.js.map +1 -1
- package/dist/src/components/ui/sheet.d.ts +5 -2
- package/dist/src/components/ui/sheet.d.ts.map +1 -1
- package/dist/src/components/ui/sheet.js +4 -2
- package/dist/src/components/ui/sheet.js.map +1 -1
- package/dist/src/components/ui/sidebar-provider.d.ts.map +1 -1
- package/dist/src/components/ui/sidebar-provider.js +5 -1
- package/dist/src/components/ui/sidebar-provider.js.map +1 -1
- package/dist/src/components/ui/split-button.d.ts +1 -0
- package/dist/src/components/ui/split-button.d.ts.map +1 -1
- package/dist/src/components/ui/split-button.js +4 -2
- package/dist/src/components/ui/split-button.js.map +1 -1
- package/dist/src/components/ui/tabs.d.ts +2 -1
- package/dist/src/components/ui/tabs.d.ts.map +1 -1
- package/dist/src/components/ui/tabs.js +16 -4
- package/dist/src/components/ui/tabs.js.map +1 -1
- package/dist/src/components/ui/toast.d.ts +10 -4
- package/dist/src/components/ui/toast.d.ts.map +1 -1
- package/dist/src/components/ui/toast.js +213 -25
- package/dist/src/components/ui/toast.js.map +1 -1
- package/dist/src/components/ui/toggle-group.d.ts +7 -1
- package/dist/src/components/ui/toggle-group.d.ts.map +1 -1
- package/dist/src/components/ui/toggle-group.js +23 -4
- package/dist/src/components/ui/toggle-group.js.map +1 -1
- package/dist/src/components/ui/tree-view.d.ts +10 -2
- package/dist/src/components/ui/tree-view.d.ts.map +1 -1
- package/dist/src/components/ui/tree-view.js +67 -46
- package/dist/src/components/ui/tree-view.js.map +1 -1
- package/dist/src/components/ui/user-menu.d.ts +12 -1
- package/dist/src/components/ui/user-menu.d.ts.map +1 -1
- package/dist/src/components/ui/user-menu.js +5 -3
- package/dist/src/components/ui/user-menu.js.map +1 -1
- package/dist/src/hooks/use-is-truncated.d.ts +9 -5
- package/dist/src/hooks/use-is-truncated.d.ts.map +1 -1
- package/dist/src/hooks/use-is-truncated.js +20 -7
- package/dist/src/hooks/use-is-truncated.js.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/focus-visible-on-close.d.ts +28 -0
- package/dist/src/lib/focus-visible-on-close.d.ts.map +1 -1
- package/dist/src/lib/focus-visible-on-close.js +54 -1
- package/dist/src/lib/focus-visible-on-close.js.map +1 -1
- package/dist/src/lib/i18n/context.d.ts +48 -0
- package/dist/src/lib/i18n/context.d.ts.map +1 -0
- package/dist/src/lib/i18n/context.js +157 -0
- package/dist/src/lib/i18n/context.js.map +1 -0
- package/dist/src/lib/i18n/dictionary.d.ts +151 -0
- package/dist/src/lib/i18n/dictionary.d.ts.map +1 -0
- package/dist/src/lib/i18n/dictionary.js +134 -0
- package/dist/src/lib/i18n/dictionary.js.map +1 -0
- package/dist/src/lib/merge-refs.d.ts +8 -0
- package/dist/src/lib/merge-refs.d.ts.map +1 -0
- package/dist/src/lib/merge-refs.js +15 -0
- package/dist/src/lib/merge-refs.js.map +1 -0
- package/dist/src/lib/with-ref.d.ts +3 -1
- package/dist/src/lib/with-ref.d.ts.map +1 -1
- package/dist/src/lib/with-ref.js +0 -10
- package/dist/src/lib/with-ref.js.map +1 -1
- package/dist/styles.css +65 -6
- package/package.json +4 -2
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
* Shared horizontal-overflow affordance for `TabsList` and `ToggleGroup`.
|
|
4
|
+
* Not part of the public API — never exported from `src/index.ts`.
|
|
5
|
+
*/
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
export type OverflowStripDirection = "before" | "after";
|
|
8
|
+
export declare function useOverflowStrip<T extends HTMLElement>(): {
|
|
9
|
+
ref: React.RefObject<T | null>;
|
|
10
|
+
hasMoreBefore: boolean;
|
|
11
|
+
hasMoreAfter: boolean;
|
|
12
|
+
scrollByStep: (direction: OverflowStripDirection) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function OverflowStripArrow({ direction, onScroll, }: {
|
|
15
|
+
direction: OverflowStripDirection;
|
|
16
|
+
onScroll: () => void;
|
|
17
|
+
}): React.JSX.Element;
|
|
18
|
+
//# sourceMappingURL=overflow-strip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overflow-strip.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/overflow-strip.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAExD,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW;;;;8BAsDtC,sBAAsB;EAsBrC;AAED,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,sBAAsB,CAAC;IAClC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB,qBAqCA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
* Shared horizontal-overflow affordance for `TabsList` and `ToggleGroup`.
|
|
5
|
+
* Not part of the public API — never exported from `src/index.ts`.
|
|
6
|
+
*/
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { ChevronLeft, ChevronRight } from "../../icons/lucide.js";
|
|
9
|
+
import { useC4Dictionary } from "../../lib/i18n/context.js";
|
|
10
|
+
import { Button } from "./button.js";
|
|
11
|
+
const SCROLL_EDGE_EPSILON_PX = 1;
|
|
12
|
+
const SCROLL_STEP_RATIO = 0.8;
|
|
13
|
+
export function useOverflowStrip() {
|
|
14
|
+
const ref = React.useRef(null);
|
|
15
|
+
const [hasMoreBefore, setHasMoreBefore] = React.useState(false);
|
|
16
|
+
const [hasMoreAfter, setHasMoreAfter] = React.useState(false);
|
|
17
|
+
const recompute = React.useCallback(() => {
|
|
18
|
+
const node = ref.current;
|
|
19
|
+
if (node == null)
|
|
20
|
+
return;
|
|
21
|
+
// Vertical Tabs/ToggleGroup never scroll horizontally and never get
|
|
22
|
+
// arrows — Radix stamps `data-orientation` on this same list/root
|
|
23
|
+
// element, so no extra prop has to be threaded through to read it here.
|
|
24
|
+
if (node.dataset.orientation === "vertical") {
|
|
25
|
+
setHasMoreBefore(false);
|
|
26
|
+
setHasMoreAfter(false);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setHasMoreBefore(node.scrollLeft > SCROLL_EDGE_EPSILON_PX);
|
|
30
|
+
setHasMoreAfter(node.scrollLeft <
|
|
31
|
+
node.scrollWidth - node.clientWidth - SCROLL_EDGE_EPSILON_PX);
|
|
32
|
+
}, []);
|
|
33
|
+
// No dependency list on purpose: items added to a strip that is already
|
|
34
|
+
// clamped change its `scrollWidth` but not its box, so neither the
|
|
35
|
+
// ResizeObserver below nor a scroll event would notice them.
|
|
36
|
+
React.useLayoutEffect(recompute);
|
|
37
|
+
React.useLayoutEffect(() => {
|
|
38
|
+
const node = ref.current;
|
|
39
|
+
if (node == null)
|
|
40
|
+
return;
|
|
41
|
+
node.addEventListener("scroll", recompute, { passive: true });
|
|
42
|
+
// A web font that lands after mount widens the labels of a strip whose
|
|
43
|
+
// own box is already clamped — no resize, no scroll, only a new
|
|
44
|
+
// `scrollWidth`. jsdom has no `document.fonts`.
|
|
45
|
+
document.fonts?.ready.then(recompute);
|
|
46
|
+
// jsdom (unit tests, and the non-browser half of the Storybook toolchain)
|
|
47
|
+
// has no ResizeObserver — skip live width tracking there instead of
|
|
48
|
+
// throwing on mount.
|
|
49
|
+
let resizeObserver;
|
|
50
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
51
|
+
resizeObserver = new ResizeObserver(recompute);
|
|
52
|
+
resizeObserver.observe(node);
|
|
53
|
+
}
|
|
54
|
+
return () => {
|
|
55
|
+
node.removeEventListener("scroll", recompute);
|
|
56
|
+
resizeObserver?.disconnect();
|
|
57
|
+
};
|
|
58
|
+
}, [recompute]);
|
|
59
|
+
const scrollByStep = React.useCallback((direction) => {
|
|
60
|
+
const node = ref.current;
|
|
61
|
+
if (node == null)
|
|
62
|
+
return;
|
|
63
|
+
const delta = node.clientWidth *
|
|
64
|
+
SCROLL_STEP_RATIO *
|
|
65
|
+
(direction === "before" ? -1 : 1);
|
|
66
|
+
// No explicit `behavior` option: the list itself carries
|
|
67
|
+
// `motion-safe:scroll-smooth`, and `scrollBy`'s default ("auto") defers
|
|
68
|
+
// to that CSS property, unlike assigning `scrollLeft` directly, which is
|
|
69
|
+
// always instant regardless of `scroll-behavior`.
|
|
70
|
+
if (typeof node.scrollBy === "function") {
|
|
71
|
+
node.scrollBy({ left: delta });
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// jsdom has no `scrollBy` implementation.
|
|
75
|
+
node.scrollLeft += delta;
|
|
76
|
+
}
|
|
77
|
+
}, []);
|
|
78
|
+
return { ref, hasMoreBefore, hasMoreAfter, scrollByStep };
|
|
79
|
+
}
|
|
80
|
+
export function OverflowStripArrow({ direction, onScroll, }) {
|
|
81
|
+
const { t } = useC4Dictionary();
|
|
82
|
+
const Icon = direction === "before" ? ChevronLeft : ChevronRight;
|
|
83
|
+
// Two literal `t()` call sites rather than one call with a computed key —
|
|
84
|
+
// dictionary-usage.test.mjs's static audit only recognizes a key that
|
|
85
|
+
// appears as `t("literal.key")` verbatim, not one assembled at runtime.
|
|
86
|
+
const label = direction === "before"
|
|
87
|
+
? t("overflowStrip.previous")
|
|
88
|
+
: t("overflowStrip.next");
|
|
89
|
+
return (_jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", "data-slot": "overflow-strip-arrow", "data-direction": direction,
|
|
90
|
+
// Pointer/touch affordance only: keyboard users already move between
|
|
91
|
+
// items with the arrow keys, and the browser scrolls the newly
|
|
92
|
+
// focused item into view on its own — so this button is never part of
|
|
93
|
+
// the keyboard path, and `aria-hidden` keeps it out of the tablist/
|
|
94
|
+
// group's accessible child count. The `aria-label` still follows the
|
|
95
|
+
// usual icon-only-element rule, since assistive tech attached
|
|
96
|
+
// directly to the DOM node (rather than walking the a11y tree) can
|
|
97
|
+
// still surface it.
|
|
98
|
+
tabIndex: -1, "aria-hidden": "true", "aria-label": label,
|
|
99
|
+
// `tabIndex={-1}` still takes focus on click; keeping it on the active
|
|
100
|
+
// item means the arrow keys keep working right after a click.
|
|
101
|
+
onPointerDown: (event) => event.preventDefault(), onClick: onScroll, className: "shrink-0", children: _jsx(Icon, { "aria-hidden": "true" }) }));
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=overflow-strip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overflow-strip.js","sourceRoot":"","sources":["../../../../src/components/ui/overflow-strip.tsx"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,7 @@ interface ScorecardProps {
|
|
|
18
18
|
size?: "default" | "sm";
|
|
19
19
|
loading?: boolean;
|
|
20
20
|
empty?: boolean;
|
|
21
|
+
/** Content shown in place of `value` when `empty` is set. Sourced from the i18n dictionary at key `"scorecard.empty"` when omitted. */
|
|
21
22
|
emptyMessage?: string;
|
|
22
23
|
className?: string;
|
|
23
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scorecard.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/scorecard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"scorecard.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/scorecard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC5C;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uIAAuI;IACvI,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyHD,iBAAS,SAAS,CAAC,EACjB,KAAK,EACL,OAAO,EACP,KAAK,EACL,KAAK,EACL,SAAS,EACT,MAAa,EACb,IAAgB,EAChB,OAAe,EACf,KAAa,EACb,YAAY,EACZ,SAAS,GACV,EAAE,cAAc,qBA6DhB;AAED,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -2,11 +2,23 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { ArrowDownRight, ArrowUpRight } from "../../icons/lucide.js";
|
|
4
4
|
import { cn } from "../../lib/utils.js";
|
|
5
|
+
import { useC4Dictionary } from "../../lib/i18n/context.js";
|
|
6
|
+
import { useIsTruncated } from "../../hooks/use-is-truncated.js";
|
|
5
7
|
import { Badge } from "./badge.js";
|
|
6
8
|
import { Card, CardHeader, CardTitle } from "./card.js";
|
|
7
9
|
import { Skeleton } from "./skeleton.js";
|
|
8
10
|
import { Sparkline } from "./sparkline.js";
|
|
11
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
|
|
9
12
|
function ScorecardBody({ label, tooltip, value, delta, sparkline, size, loading, empty, emptyMessage, asCard, }) {
|
|
13
|
+
// Hook must run unconditionally, above the `loading` early return below, or
|
|
14
|
+
// the loading→loaded transition changes this component's hook count. During
|
|
15
|
+
// loading the value span isn't rendered, so `ref.current` is null and
|
|
16
|
+
// `measure()` is a no-op (same situation multi-select's placeholder hook is
|
|
17
|
+
// in while chips are showing instead).
|
|
18
|
+
const { ref: valueRef, isTruncated: valueTruncated, measure: measureValue, } = useIsTruncated();
|
|
19
|
+
React.useLayoutEffect(() => {
|
|
20
|
+
measureValue();
|
|
21
|
+
});
|
|
10
22
|
if (loading) {
|
|
11
23
|
return (_jsxs(_Fragment, { children: [_jsx(Skeleton, { className: "h-4 w-24" }), _jsx(Skeleton, { className: cn("h-7 w-20", size === "sm" && "h-6 w-16") })] }));
|
|
12
24
|
}
|
|
@@ -21,12 +33,22 @@ function ScorecardBody({ label, tooltip, value, delta, sparkline, size, loading,
|
|
|
21
33
|
const sparklineElement = sparkline ? (_jsx(Sparkline, { data: sparkline.data, dataKey: sparkline.dataKey, loading: false, activeDot: false, className: asCard
|
|
22
34
|
? "w-auto -mx-4 -mb-4 group-data-[size=sm]/card:-mx-3 group-data-[size=sm]/card:-mb-3"
|
|
23
35
|
: undefined })) : null;
|
|
24
|
-
|
|
36
|
+
const valueContent = empty ? emptyMessage : value;
|
|
37
|
+
// No focusable ancestor wraps this KPI value — plain static text, per the
|
|
38
|
+
// "Truncated Text" decision tree in docs/specs/shadcn/patterns.md.
|
|
39
|
+
// `tabIndex={0}` goes directly on the value span. `ring-inset`: this span
|
|
40
|
+
// lives inside `<Card>`'s own `overflow-hidden` (when `asCard`), same clip
|
|
41
|
+
// risk nav-breadcrumb.tsx's label button ring documents.
|
|
42
|
+
const valueSpan = (_jsx("span", { ref: valueRef, tabIndex: 0, onPointerEnter: measureValue, onFocus: measureValue, className: cn("min-w-0 truncate rounded-sm text-2xl font-semibold text-neutral-foreground-strong outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset", size === "sm" && "text-xl"), children: valueContent }));
|
|
43
|
+
const valueElement = valueTruncated ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: valueSpan }), _jsx(TooltipContent, { children: valueContent })] })) : (valueSpan);
|
|
44
|
+
return (_jsxs(_Fragment, { children: [_jsx(CardTitle, { tooltip: tooltip, className: "text-sm font-normal", children: label }), _jsx("div", { className: "flex min-w-0 items-center", children: _jsxs("span", { className: "inline-flex min-w-0 max-w-full items-center gap-2", children: [valueElement, !empty && delta && (_jsxs(Badge, { variant: delta.variant, className: "shrink-0", children: [_jsx(DeltaIcon, { "data-icon": "inline-start", "aria-hidden": "true" }), _jsx("span", { className: "sr-only", children: delta.direction === "increase"
|
|
25
45
|
? "Increased by "
|
|
26
46
|
: "Decreased by " }), delta.value] }))] }) }), !empty && sparklineElement] }));
|
|
27
47
|
}
|
|
28
|
-
function Scorecard({ label, tooltip, value, delta, sparkline, asCard = true, size = "default", loading = false, empty = false, emptyMessage
|
|
29
|
-
const
|
|
48
|
+
function Scorecard({ label, tooltip, value, delta, sparkline, asCard = true, size = "default", loading = false, empty = false, emptyMessage, className, }) {
|
|
49
|
+
const { t } = useC4Dictionary();
|
|
50
|
+
const resolvedEmptyMessage = emptyMessage ?? t("scorecard.empty");
|
|
51
|
+
const content = (_jsx(ScorecardBody, { label: label, tooltip: tooltip, value: value, delta: delta, sparkline: sparkline, size: size, loading: loading, empty: empty, emptyMessage: resolvedEmptyMessage, asCard: asCard }));
|
|
30
52
|
// Loading removes the title from the accessible tree (it becomes a
|
|
31
53
|
// Skeleton), so `aria-busy` alone would leave the busy region unnamed.
|
|
32
54
|
// `aria-label` alone isn't enough either: the root is a plain `<div>`
|
|
@@ -34,7 +56,9 @@ function Scorecard({ label, tooltip, value, delta, sparkline, asCard = true, siz
|
|
|
34
56
|
// — axe flags it as `aria-prohibited-attr`. `role="status"` (same fix
|
|
35
57
|
// `tree-view.tsx`'s own loading state already uses) both permits the name
|
|
36
58
|
// and gets the busy→loaded transition announced as a polite live region.
|
|
37
|
-
const loadingLabel = typeof label === "string"
|
|
59
|
+
const loadingLabel = typeof label === "string"
|
|
60
|
+
? t("scorecard.loading", { label })
|
|
61
|
+
: t("scorecard.loadingFallback");
|
|
38
62
|
if (asCard) {
|
|
39
63
|
return (_jsx(Card, { size: size, "aria-busy": loading || undefined, role: loading ? "status" : undefined, "aria-live": loading ? "polite" : undefined, "aria-label": loading ? loadingLabel : undefined, className: cn("text-neutral-foreground-subtle", className), children: _jsx(CardHeader, { children: content }) }));
|
|
40
64
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scorecard.js","sourceRoot":"","sources":["../../../../src/components/ui/scorecard.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"scorecard.js","sourceRoot":"","sources":["../../../../src/components/ui/scorecard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
type SearchInputProps = Omit<React.ComponentProps<"input">, "type"> & {
|
|
3
3
|
/** Called after the clear path resets the value and fires the synthesized `onChange("")`. */
|
|
4
4
|
onClear?: () => void;
|
|
5
|
-
/** Accessible name for the clear button. Only name assistive tech announces — native cancel button is suppressed. */
|
|
5
|
+
/** Accessible name for the clear button. Only name assistive tech announces — native cancel button is suppressed. Sourced from the i18n dictionary at key `"searchInput.clear"` when omitted. */
|
|
6
6
|
clearLabel?: string;
|
|
7
7
|
};
|
|
8
8
|
declare const SearchInput: React.ForwardRefExoticComponent<Omit<SearchInputProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-input.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/search-input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"search-input.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/search-input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,KAAK,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IACpE,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,iMAAiM;IACjM,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAwGF,QAAA,MAAM,WAAW,wGAEhB,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,CAAC"}
|
|
@@ -2,9 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { SearchIcon, XIcon } from "../../icons/lucide.js";
|
|
4
4
|
import { cn } from "../../lib/utils.js";
|
|
5
|
+
import { useC4Dictionary } from "../../lib/i18n/context.js";
|
|
5
6
|
import { withRef } from "../../lib/with-ref.js";
|
|
6
7
|
import { Input } from "./input.js";
|
|
7
|
-
function SearchInputImpl({ ref, value, defaultValue, disabled, onChange, onClear, onKeyDown, clearLabel
|
|
8
|
+
function SearchInputImpl({ ref, value, defaultValue, disabled, onChange, onClear, onKeyDown, clearLabel, className, ...props }) {
|
|
9
|
+
const { t } = useC4Dictionary();
|
|
8
10
|
const inputRef = React.useRef(null);
|
|
9
11
|
const [internalValue, setInternalValue] = React.useState(defaultValue !== undefined ? String(defaultValue) : "");
|
|
10
12
|
const isControlled = value !== undefined;
|
|
@@ -50,7 +52,7 @@ function SearchInputImpl({ ref, value, defaultValue, disabled, onChange, onClear
|
|
|
50
52
|
// `className` sizes/positions the whole search affordance (wrapper), not the
|
|
51
53
|
// bare input — the clear button is absolutely positioned against this wrapper,
|
|
52
54
|
// so routing width here keeps the button pinned to the field's real edge.
|
|
53
|
-
_jsxs("div", { "data-slot": "search-input", className: cn("relative", className), children: [_jsx(SearchIcon, { "aria-hidden": "true", className: "pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-neutral-foreground-subtle" }), _jsx(Input, { ...props, ref: setRefs, type: "search", role: "searchbox", value: currentValue, disabled: disabled, onChange: handleChange, onKeyDown: handleKeyDown, className: cn("pl-8 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none", showClear && "pr-9") }), showClear && (_jsx("button", { type: "button", "aria-label": clearLabel, onClick: handleClear, className: "absolute right-2 top-1/2 inline-flex size-7 -translate-y-1/2 items-center justify-center rounded-sm text-neutral-foreground-subtle hover:text-neutral-foreground-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(XIcon, { "aria-hidden": "true", className: "h-4 w-4" }) }))] }));
|
|
55
|
+
_jsxs("div", { "data-slot": "search-input", className: cn("relative", className), children: [_jsx(SearchIcon, { "aria-hidden": "true", className: "pointer-events-none absolute left-2 top-1/2 h-4 w-4 -translate-y-1/2 text-neutral-foreground-subtle" }), _jsx(Input, { ...props, ref: setRefs, type: "search", role: "searchbox", value: currentValue, disabled: disabled, onChange: handleChange, onKeyDown: handleKeyDown, className: cn("pl-8 [&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none", showClear && "pr-9") }), showClear && (_jsx("button", { type: "button", "aria-label": clearLabel ?? t("searchInput.clear"), onClick: handleClear, className: "absolute right-2 top-1/2 inline-flex size-7 -translate-y-1/2 items-center justify-center rounded-sm text-neutral-foreground-subtle hover:text-neutral-foreground-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(XIcon, { "aria-hidden": "true", className: "h-4 w-4" }) }))] }));
|
|
54
56
|
}
|
|
55
57
|
const SearchInput = withRef(SearchInputImpl);
|
|
56
58
|
export { SearchInput };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-input.js","sourceRoot":"","sources":["../../../../src/components/ui/search-input.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAShC,SAAS,eAAe,CAAC,EACvB,GAAG,EACH,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,SAAS,EACT,UAAU,
|
|
1
|
+
{"version":3,"file":"search-input.js","sourceRoot":"","sources":["../../../../src/components/ui/search-input.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAShC,SAAS,eAAe,CAAC,EACvB,GAAG,EACH,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,SAAS,EACT,UAAU,EACV,SAAS,EACT,GAAG,KAAK,EACS;IACjB,MAAM,EAAE,CAAC,EAAE,GAAG,eAAe,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAmB,IAAI,CAAC,CAAC;IACtD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,KAAK,CAAC,QAAQ,CACtD,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CACvD,CAAC;IACF,MAAM,YAAY,GAAG,KAAK,KAAK,SAAS,CAAC;IACzC,MAAM,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;IACxE,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;IAEvD,MAAM,OAAO,GAAG,CAAC,IAA6B,EAAE,EAAE;QAChD,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QACxB,IAAI,OAAO,GAAG,KAAK,UAAU;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC;aACpC,IAAI,GAAG;YAAE,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC;IACnC,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,KAA0C,EAAE,EAAE;QAClE,IAAI,CAAC,YAAY;YAAE,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxD,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC,CAAC;IAEF,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,wEAAwE;IACxE,MAAM,WAAW,GAAG,GAAG,EAAE;QACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC;QAC9B,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,MAAM,GAAG,MAAM,CAAC,wBAAwB,CAC5C,MAAM,CAAC,gBAAgB,CAAC,SAAS,EACjC,OAAO,CACR,EAAE,GAAG,CAAC;YACP,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACvB,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,EAAE,EAAE,CAAC;QACZ,IAAI,EAAE,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC;IAEF,sEAAsE;IACtE,yEAAyE;IACzE,yEAAyE;IACzE,MAAM,aAAa,GAAG,CAAC,KAA4C,EAAE,EAAE;QACrE,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;QACnB,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;IACL,6EAA6E;IAC7E,+EAA+E;IAC/E,0EAA0E;IAC1E,4BAAe,cAAc,EAAC,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,aAChE,KAAC,UAAU,mBACG,MAAM,EAClB,SAAS,EAAC,qGAAqG,GAC/G,EACF,KAAC,KAAK,OACA,KAAK,EACT,GAAG,EAAE,OAAO,EACZ,IAAI,EAAC,QAAQ,EACb,IAAI,EAAC,WAAW,EAChB,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,EAAE,CACX,uGAAuG,EACvG,SAAS,IAAI,MAAM,CACpB,GACD,EAKD,SAAS,IAAI,CACZ,iBACE,IAAI,EAAC,QAAQ,gBACD,UAAU,IAAI,CAAC,CAAC,mBAAmB,CAAC,EAChD,OAAO,EAAE,WAAW,EACpB,SAAS,EAAC,iPAAiP,YAE3P,KAAC,KAAK,mBAAa,MAAM,EAAC,SAAS,EAAC,SAAS,GAAG,GACzC,CACV,IACG,CACP,CAAC;AACJ,CAAC;AAED,MAAM,WAAW,GAAG,OAAO,CACzB,eAAe,CAChB,CAAC;AAEF,OAAO,EAAE,WAAW,EAAyB,CAAC"}
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { Select as SelectPrimitive } from "radix-ui";
|
|
3
3
|
declare function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>): React.JSX.Element;
|
|
4
4
|
declare function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>): React.JSX.Element;
|
|
5
|
-
declare function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>): React.JSX.Element;
|
|
5
|
+
declare function SelectValue({ children, ...props }: React.ComponentProps<typeof SelectPrimitive.Value>): React.JSX.Element;
|
|
6
6
|
type SelectTriggerProps = React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
|
7
7
|
size?: "sm" | "default";
|
|
8
8
|
/** Rendered below the trigger when `aria-invalid` is set. Also wires `aria-errormessage`/`aria-describedby`. */
|
|
@@ -10,7 +10,7 @@ type SelectTriggerProps = React.ComponentProps<typeof SelectPrimitive.Trigger> &
|
|
|
10
10
|
/** Rendered below the trigger when not invalid. Wires `aria-describedby`. */
|
|
11
11
|
helperText?: React.ReactNode;
|
|
12
12
|
};
|
|
13
|
-
declare function SelectTrigger({ className, size, children, id, invalidText, helperText, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, "aria-errormessage": ariaErrorMessage, ...props }: SelectTriggerProps): React.JSX.Element;
|
|
13
|
+
declare function SelectTrigger({ className, size, children, id, invalidText, helperText, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, "aria-errormessage": ariaErrorMessage, ref, onPointerEnter, onFocus, ...props }: SelectTriggerProps): React.JSX.Element;
|
|
14
14
|
declare function SelectContent({ className, children, position, onCloseAutoFocus, restoreFocusRef, ...props }: React.ComponentProps<typeof SelectPrimitive.Content> & {
|
|
15
15
|
/**
|
|
16
16
|
* Element to refocus when the select closes, instead of the default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/select.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;AA6BrD,iBAAS,MAAM,CAAC,EACd,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,qBAEnD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAEpD;AAED,iBAAS,WAAW,CAAC,EACnB,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAsCpD;AAED,KAAK,kBAAkB,GAAG,KAAK,CAAC,cAAc,CAC5C,OAAO,eAAe,CAAC,OAAO,CAC/B,GAAG;IACF,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACxB,gHAAgH;IAChH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,6EAA6E;IAC7E,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B,CAAC;AAEF,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,IAAgB,EAChB,QAAQ,EACR,EAAE,EACF,WAAW,EACX,UAAU,EACV,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,mBAAmB,EAAE,gBAAgB,EACrC,GAAG,EACH,cAAc,EACd,OAAO,EACP,GAAG,KAAK,EACT,EAAE,kBAAkB,qBAsJpB;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,GAAG;IACxD;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACvD,qBAwCA;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAWpD;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,qBAkBnD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,qBAQxD;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,cAAc,CAAC,qBAa7D;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,gBAAgB,CAAC,qBAa/D;AAED,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAC;AACF,YAAY,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -4,18 +4,59 @@ import * as React from "react";
|
|
|
4
4
|
import { Select as SelectPrimitive } from "radix-ui";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
6
|
import { composeCloseAutoFocus, useDialogFocusReturn, } from "../../lib/focus-visible-on-close.js";
|
|
7
|
+
import { useIsTruncated } from "../../hooks/use-is-truncated.js";
|
|
7
8
|
import { C4Provider } from "./c4-provider.js";
|
|
8
9
|
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "../../icons/lucide.js";
|
|
10
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
|
|
11
|
+
// No existing ref-merge utility in this package (single-consumer need so
|
|
12
|
+
// far, same reasoning as `code-block.tsx`'s own copy): the forwarded `ref`
|
|
13
|
+
// (an external consumer's, if passed via `...props` — SelectTrigger doesn't
|
|
14
|
+
// destructure it today) and the internal ref this file needs to read the
|
|
15
|
+
// rendered `[data-slot="select-value"]` node both have to land on the same
|
|
16
|
+
// `SelectPrimitive.Trigger` DOM node.
|
|
17
|
+
function mergeRefs(a, b) {
|
|
18
|
+
return (node) => {
|
|
19
|
+
if (typeof a === "function")
|
|
20
|
+
a(node);
|
|
21
|
+
else if (a)
|
|
22
|
+
a.current = node;
|
|
23
|
+
b.current = node;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
9
26
|
function Select({ ...props }) {
|
|
10
27
|
return _jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
11
28
|
}
|
|
12
29
|
function SelectGroup({ ...props }) {
|
|
13
30
|
return _jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
|
|
14
31
|
}
|
|
15
|
-
function SelectValue({ ...props }) {
|
|
16
|
-
|
|
32
|
+
function SelectValue({ children, ...props }) {
|
|
33
|
+
// Radix's `Select.Value` destructures `className`/`style` out of props and
|
|
34
|
+
// never forwards them to the rendered node (see
|
|
35
|
+
// `@radix-ui/react-select`'s own `SelectValue` implementation) — so styling
|
|
36
|
+
// this node's own display/truncation can't be done via a `className` prop
|
|
37
|
+
// on it directly, no matter how it's computed here. `data-*` attributes
|
|
38
|
+
// aren't stripped, though: they land in Radix's own `...valueProps` spread
|
|
39
|
+
// and reach the DOM, so `SelectTrigger`'s className reaches this node via
|
|
40
|
+
// a `[data-slot=select-value]` DESCENDANT selector (not the `*:` direct-
|
|
41
|
+
// child variant the previous, broken attempt used — this node isn't
|
|
42
|
+
// always a direct child of the trigger; see the icon+value composition in
|
|
43
|
+
// `filter-bar.stories.tsx`, which nests `SelectValue` inside a consumer
|
|
44
|
+
// span). `data-has-children` distinguishes the two layouts that selector
|
|
45
|
+
// needs to produce: plain `truncate` when Radix auto-renders the selected
|
|
46
|
+
// item's text (no `children` passed here — the common case, and the only
|
|
47
|
+
// one any consumer in this repo actually uses), or `flex` when a consumer
|
|
48
|
+
// passes its own `children` instead of relying on that auto-text.
|
|
49
|
+
//
|
|
50
|
+
// Radix's own `hasChildren` (`children !== void 0`) gates whether it
|
|
51
|
+
// auto-renders the selected item's text at all: passing this component
|
|
52
|
+
// *any* non-undefined `children` — even conditionally, even `null` — would
|
|
53
|
+
// suppress that auto-text for every consumer that doesn't. So this only
|
|
54
|
+
// ever wraps `children` when the consumer actually supplied some; it never
|
|
55
|
+
// passes an empty wrapper through in the common case.
|
|
56
|
+
const hasChildren = children !== undefined;
|
|
57
|
+
return (_jsx(SelectPrimitive.Value, { "data-slot": "select-value", "data-has-children": hasChildren ? "" : undefined, ...props, children: hasChildren ? (_jsx("span", { "data-slot": "select-value-text", className: "min-w-0 truncate", children: children })) : undefined }));
|
|
17
58
|
}
|
|
18
|
-
function SelectTrigger({ className, size = "default", children, id, invalidText, helperText, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, "aria-errormessage": ariaErrorMessage, ...props }) {
|
|
59
|
+
function SelectTrigger({ className, size = "default", children, id, invalidText, helperText, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, "aria-errormessage": ariaErrorMessage, ref, onPointerEnter, onFocus, ...props }) {
|
|
19
60
|
const generatedId = React.useId();
|
|
20
61
|
const resolvedId = id ?? generatedId;
|
|
21
62
|
const isInvalid = ariaInvalid === true || ariaInvalid === "true";
|
|
@@ -26,13 +67,89 @@ function SelectTrigger({ className, size = "default", children, id, invalidText,
|
|
|
26
67
|
const errorMessageId = ariaErrorMessage ?? errorId;
|
|
27
68
|
const describedBy = [errorId, helperId, ariaDescribedBy].filter(Boolean).join(" ") || undefined;
|
|
28
69
|
const hasMessage = showInvalidText || showHelperText;
|
|
29
|
-
|
|
70
|
+
// `SelectValue`'s content is opaque to `SelectTrigger` — it's rendered as
|
|
71
|
+
// `children`, an arbitrary consumer-authored subtree — so unlike the other
|
|
72
|
+
// 3 truncation-reveal sites there is no JSX ref to attach directly to the
|
|
73
|
+
// node the truncation utility clips (`data-slot="select-value"`, or its
|
|
74
|
+
// inner `data-slot="select-value-text"` span when `SelectValue` was given
|
|
75
|
+
// `children` — see the `[data-slot=select-value]` descendant selectors in
|
|
76
|
+
// this trigger's className below). `triggerRef` reads the rendered DOM
|
|
77
|
+
// instead: an ADDITIONAL internal ref alongside any consumer `ref` (merged
|
|
78
|
+
// via `mergeRefs`, not a replacement — `props` may still carry a consumer
|
|
79
|
+
// ref since it's not destructured above).
|
|
80
|
+
const triggerRef = React.useRef(null);
|
|
81
|
+
const mergedRef = React.useMemo(() => mergeRefs(ref, triggerRef), [ref, triggerRef]);
|
|
82
|
+
const { ref: valueRef, isTruncated, measure } = useIsTruncated();
|
|
83
|
+
const [tooltipText, setTooltipText] = React.useState("");
|
|
84
|
+
// Every commit, not just on a `children` change: which node currently
|
|
85
|
+
// carries `data-slot="select-value"`, and the room the trigger has, can
|
|
86
|
+
// both change with no reference-identity change on `children` to key on
|
|
87
|
+
// (e.g. picking a different option re-renders `SelectValue` under the same
|
|
88
|
+
// trigger). See "Truncated Text" in `docs/specs/shadcn/patterns.md`. No
|
|
89
|
+
// deps array is intentional (same reasoning `nav-breadcrumb.tsx` documents
|
|
90
|
+
// for its own equivalent effect) — the lint rule flags the direct
|
|
91
|
+
// `setTooltipText` call as a possible infinite-update chain, but `useState`
|
|
92
|
+
// bails out via `Object.is` when the computed text is unchanged, same as
|
|
93
|
+
// `measure()`'s own `setIsTruncated` call one layer down (which the rule
|
|
94
|
+
// can't see into, and so doesn't flag).
|
|
95
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
96
|
+
React.useLayoutEffect(() => {
|
|
97
|
+
// Prefer the inner `select-value-text` span — present only when
|
|
98
|
+
// `SelectValue` was given `children` — over the outer `select-value`
|
|
99
|
+
// node itself: that outer node is `flex` in that case (to lay out
|
|
100
|
+
// `children` alongside other content), and measuring `scrollWidth` /
|
|
101
|
+
// `clientWidth` on a flex container doesn't reflect text clipping the
|
|
102
|
+
// way it does on the actual `truncate`d node. See `select.tsx`'s
|
|
103
|
+
// `SelectValue` for why the split exists at all.
|
|
104
|
+
const valueEl = triggerRef.current?.querySelector('[data-slot="select-value-text"]') ??
|
|
105
|
+
triggerRef.current?.querySelector('[data-slot="select-value"]');
|
|
106
|
+
valueRef.current = valueEl ?? null;
|
|
107
|
+
measure();
|
|
108
|
+
setTooltipText(valueEl?.textContent ?? "");
|
|
109
|
+
});
|
|
110
|
+
const triggerEl = (_jsxs(SelectPrimitive.Trigger, { ref: mergedRef, id: resolvedId, "data-slot": "select-trigger", "data-size": size, "aria-invalid": ariaInvalid, "aria-errormessage": errorMessageId, "aria-describedby": describedBy,
|
|
111
|
+
// Composed rather than clobbered: `props` may still carry a consumer
|
|
112
|
+
// handler (only `ref` is destructured above, not these two), and
|
|
113
|
+
// `{...props}` spreads AFTER this element's own attributes — the
|
|
114
|
+
// opposite order from `combobox.tsx`, so the risk here is a consumer
|
|
115
|
+
// handler silently winning over `measure()`, not the other way round.
|
|
116
|
+
onPointerEnter: (e) => {
|
|
117
|
+
onPointerEnter?.(e);
|
|
118
|
+
measure();
|
|
119
|
+
}, onFocus: (e) => {
|
|
120
|
+
onFocus?.(e);
|
|
121
|
+
measure();
|
|
122
|
+
}, className: cn("flex w-fit min-w-0 items-center justify-between gap-2 rounded-lg border border-input bg-input-background px-3 py-2 text-sm whitespace-nowrap transition-all duration-200 outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/20 dark:focus-visible:ring-ring/40 data-[focus-visible-restore]:border-ring data-[focus-visible-restore]:ring-3 data-[focus-visible-restore]:ring-ring/20 dark:data-[focus-visible-restore]:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-danger-foreground-subtle aria-invalid:ring-3 aria-invalid:ring-danger-foreground-subtle/20 data-placeholder:text-neutral-foreground-subtle data-[size=default]:h-9 data-[size=sm]:h-8 [&_[data-slot=select-value]]:min-w-0 [&_[data-slot=select-value]:not([data-has-children])]:truncate [&_[data-slot=select-value][data-has-children]]:flex [&_[data-slot=select-value][data-has-children]]:items-center [&_[data-slot=select-value][data-has-children]]:gap-1.5 dark:hover:bg-neutral-background-medium dark:aria-invalid:ring-danger-foreground-subtle/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDownIcon, { className: "pointer-events-none size-4 opacity-50" }) })] }));
|
|
123
|
+
// DEVIATES from the other 3 truncation-reveal sites (and from
|
|
124
|
+
// `nav-breadcrumb.tsx`'s reference pattern), and empirically so — not by
|
|
125
|
+
// assumption: `Tooltip`/`TooltipTrigger` are mounted UNCONDITIONALLY here
|
|
126
|
+
// (only `TooltipContent` is conditional on `isTruncated`), instead of
|
|
127
|
+
// conditionally mounting the whole `Tooltip` subtree. Conditionally
|
|
128
|
+
// mounting the whole subtree was tried first and measurably broken —
|
|
129
|
+
// toggling `isTruncated` changes the JSX shape at `SelectPrimitive.Trigger`'s
|
|
130
|
+
// own tree position (bare element vs. wrapped in `Tooltip`), which makes
|
|
131
|
+
// React unmount and remount it, and with it Radix Select's own internal
|
|
132
|
+
// `SelectValue` registration. The remounted value node measures 0×0 on the
|
|
133
|
+
// commit right after the remount (confirmed with a throwaway console probe:
|
|
134
|
+
// `onPointerEnter`'s `measure()` read the correct 260×110 and set
|
|
135
|
+
// `isTruncated` true; the very next commit's layout effect then read the
|
|
136
|
+
// SAME query on the freshly-remounted node as 0×0 and flipped it back to
|
|
137
|
+
// false), so the tooltip opened and immediately closed on every hover
|
|
138
|
+
// instead of staying open — a real correctness bug, not a style
|
|
139
|
+
// preference. `SelectPrimitive.Trigger` needs a permanently stable tree
|
|
140
|
+
// position for its internal state to survive a truncation toggle; the
|
|
141
|
+
// other 3 sites don't have that constraint; the accepted cost versus
|
|
142
|
+
// `nav-breadcrumb.tsx`'s document (an unconditional `Tooltip` still runs
|
|
143
|
+
// Radix's open-request handling and touches `TooltipProvider`'s
|
|
144
|
+
// delay-skip state on every hover of a SHORT value too) is real but minor
|
|
145
|
+
// next to a tooltip that cannot stay open.
|
|
146
|
+
const wrappedTriggerEl = (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: triggerEl }), isTruncated ? _jsx(TooltipContent, { children: tooltipText }) : null] }));
|
|
30
147
|
if (!hasMessage) {
|
|
31
|
-
return
|
|
148
|
+
return wrappedTriggerEl;
|
|
32
149
|
}
|
|
33
150
|
// `w-fit` here would make a `width: 100%` trigger circular against its own
|
|
34
151
|
// shrink-to-fit parent, silently dropping a `w-full` className override.
|
|
35
|
-
return (_jsxs("div", { className: "flex w-full flex-col gap-1.5", children: [
|
|
152
|
+
return (_jsxs("div", { className: "flex w-full flex-col gap-1.5", children: [wrappedTriggerEl, showInvalidText ? (_jsx("span", { id: errorId, className: "text-xs text-danger-foreground-subtle", children: invalidText })) : null, showHelperText ? (_jsx("span", { id: helperId, className: "text-xs text-neutral-foreground-subtle", children: helperText })) : null] }));
|
|
36
153
|
}
|
|
37
154
|
function SelectContent({ className, children, position = "popper", onCloseAutoFocus, restoreFocusRef, ...props }) {
|
|
38
155
|
const dialogFocusReturn = useDialogFocusReturn(restoreFocusRef);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../../../src/components/ui/select.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;AAErD,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../../../src/components/ui/select.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,UAAU,CAAC;AAErD,OAAO,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAC/B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpE,yEAAyE;AACzE,2EAA2E;AAC3E,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,sCAAsC;AACtC,SAAS,SAAS,CAChB,CAA2B,EAC3B,CAA4B;IAE5B,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,IAAI,OAAO,CAAC,KAAK,UAAU;YAAE,CAAC,CAAC,IAAI,CAAC,CAAC;aAChC,IAAI,CAAC;YAAG,CAA+B,CAAC,OAAO,GAAG,IAAI,CAAC;QAC5D,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC;IACnB,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,EACd,GAAG,KAAK,EAC0C;IAClD,OAAO,KAAC,eAAe,CAAC,IAAI,iBAAW,QAAQ,KAAK,KAAK,GAAI,CAAC;AAChE,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EAC2C;IACnD,OAAO,KAAC,eAAe,CAAC,KAAK,iBAAW,cAAc,KAAK,KAAK,GAAI,CAAC;AACvE,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,QAAQ,EACR,GAAG,KAAK,EAC2C;IACnD,2EAA2E;IAC3E,gDAAgD;IAChD,4EAA4E;IAC5E,0EAA0E;IAC1E,wEAAwE;IACxE,2EAA2E;IAC3E,0EAA0E;IAC1E,yEAAyE;IACzE,oEAAoE;IACpE,0EAA0E;IAC1E,wEAAwE;IACxE,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,kEAAkE;IAClE,EAAE;IACF,qEAAqE;IACrE,uEAAuE;IACvE,2EAA2E;IAC3E,wEAAwE;IACxE,2EAA2E;IAC3E,sDAAsD;IACtD,MAAM,WAAW,GAAG,QAAQ,KAAK,SAAS,CAAC;IAC3C,OAAO,CACL,KAAC,eAAe,CAAC,KAAK,iBACV,cAAc,uBACL,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAC3C,KAAK,YAER,WAAW,CAAC,CAAC,CAAC,CACb,4BAAgB,mBAAmB,EAAC,SAAS,EAAC,kBAAkB,YAC7D,QAAQ,GACJ,CACR,CAAC,CAAC,CAAC,SAAS,GACS,CACzB,CAAC;AACJ,CAAC;AAYD,SAAS,aAAa,CAAC,EACrB,SAAS,EACT,IAAI,GAAG,SAAS,EAChB,QAAQ,EACR,EAAE,EACF,WAAW,EACX,UAAU,EACV,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,mBAAmB,EAAE,gBAAgB,EACrC,GAAG,EACH,cAAc,EACd,OAAO,EACP,GAAG,KAAK,EACW;IACnB,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,EAAE,IAAI,WAAW,CAAC;IACrC,MAAM,SAAS,GAAG,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,MAAM,CAAC;IACjE,MAAM,eAAe,GAAG,SAAS,IAAI,WAAW,KAAK,SAAS,CAAC;IAC/D,MAAM,cAAc,GAAG,CAAC,SAAS,IAAI,UAAU,KAAK,SAAS,CAAC;IAC9D,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,UAAU,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,UAAU,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1E,MAAM,cAAc,GAAG,gBAAgB,IAAI,OAAO,CAAC;IACnD,MAAM,WAAW,GACf,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;IAE9E,MAAM,UAAU,GAAG,eAAe,IAAI,cAAc,CAAC;IAErD,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,wEAAwE;IACxE,0EAA0E;IAC1E,0EAA0E;IAC1E,uEAAuE;IACvE,2EAA2E;IAC3E,0EAA0E;IAC1E,0CAA0C;IAC1C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAoB,IAAI,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAC7B,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,UAAU,CAAC,EAChC,CAAC,GAAG,EAAE,UAAU,CAAC,CAClB,CAAC;IACF,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,cAAc,EAAe,CAAC;IAC9E,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEzD,sEAAsE;IACtE,wEAAwE;IACxE,wEAAwE;IACxE,2EAA2E;IAC3E,wEAAwE;IACxE,2EAA2E;IAC3E,kEAAkE;IAClE,4EAA4E;IAC5E,yEAAyE;IACzE,yEAAyE;IACzE,wCAAwC;IACxC,uDAAuD;IACvD,KAAK,CAAC,eAAe,CAAC,GAAG,EAAE;QACzB,gEAAgE;QAChE,qEAAqE;QACrE,kEAAkE;QAClE,qEAAqE;QACrE,sEAAsE;QACtE,iEAAiE;QACjE,iDAAiD;QACjD,MAAM,OAAO,GACX,UAAU,CAAC,OAAO,EAAE,aAAa,CAC/B,iCAAiC,CAClC;YACD,UAAU,CAAC,OAAO,EAAE,aAAa,CAC/B,4BAA4B,CAC7B,CAAC;QACJ,QAAQ,CAAC,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC;QACnC,OAAO,EAAE,CAAC;QACV,cAAc,CAAC,OAAO,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,CAChB,MAAC,eAAe,CAAC,OAAO,IACtB,GAAG,EAAE,SAAS,EACd,EAAE,EAAE,UAAU,eACJ,gBAAgB,eACf,IAAI,kBACD,WAAW,uBACN,cAAc,sBACf,WAAW;QAC7B,qEAAqE;QACrE,iEAAiE;QACjE,iEAAiE;QACjE,qEAAqE;QACrE,sEAAsE;QACtE,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE;YACpB,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC,EACD,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;YACb,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YACb,OAAO,EAAE,CAAC;QACZ,CAAC,EACD,SAAS,EAAE,EAAE,CACX,woCAAwoC,EACxoC,SAAS,CACV,KACG,KAAK,aAER,QAAQ,EACT,KAAC,eAAe,CAAC,IAAI,IAAC,OAAO,kBAC3B,KAAC,eAAe,IAAC,SAAS,EAAC,uCAAuC,GAAG,GAChD,IACC,CAC3B,CAAC;IAEF,8DAA8D;IAC9D,yEAAyE;IACzE,0EAA0E;IAC1E,sEAAsE;IACtE,oEAAoE;IACpE,qEAAqE;IACrE,8EAA8E;IAC9E,yEAAyE;IACzE,wEAAwE;IACxE,2EAA2E;IAC3E,4EAA4E;IAC5E,kEAAkE;IAClE,yEAAyE;IACzE,yEAAyE;IACzE,sEAAsE;IACtE,gEAAgE;IAChE,wEAAwE;IACxE,sEAAsE;IACtE,qEAAqE;IACrE,yEAAyE;IACzE,gEAAgE;IAChE,0EAA0E;IAC1E,2CAA2C;IAC3C,MAAM,gBAAgB,GAAG,CACvB,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBAAE,SAAS,GAAkB,EACnD,WAAW,CAAC,CAAC,CAAC,KAAC,cAAc,cAAE,WAAW,GAAkB,CAAC,CAAC,CAAC,IAAI,IAC5D,CACX,CAAC;IAEF,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,2EAA2E;IAC3E,yEAAyE;IACzE,OAAO,CACL,eAAK,SAAS,EAAC,8BAA8B,aAC1C,gBAAgB,EAChB,eAAe,CAAC,CAAC,CAAC,CACjB,eAAM,EAAE,EAAE,OAAO,EAAE,SAAS,EAAC,uCAAuC,YACjE,WAAW,GACP,CACR,CAAC,CAAC,CAAC,IAAI,EACP,cAAc,CAAC,CAAC,CAAC,CAChB,eAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAC,wCAAwC,YACnE,UAAU,GACN,CACR,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAQ,GAAG,QAAQ,EACnB,gBAAgB,EAChB,eAAe,EACf,GAAG,KAAK,EAUT;IACC,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,eAAe,CAAC,CAAC;IAChE,OAAO,CACL,KAAC,eAAe,CAAC,MAAM,cACrB,KAAC,UAAU,cACT,MAAC,eAAe,CAAC,OAAO,iBACZ,gBAAgB;gBAC1B,gEAAgE;gBAChE,iEAAiE;gBACjE,oDAAoD;gBACpD,oEAAoE;gBACpE,mDAAmD;gBACnD,gBAAgB,EAAE,qBAAqB,CACrC,gBAAgB,EAChB,iBAAiB,CAAC,gBAAgB,CACnC,EACD,SAAS,EAAE,EAAE,CACX,wgBAAwgB,EACxgB,QAAQ,KAAK,QAAQ;oBACnB,iIAAiI,EACnI,SAAS,CACV,EACD,QAAQ,EAAE,QAAQ,KACd,KAAK,aAET,KAAC,oBAAoB,KAAG,EACxB,KAAC,eAAe,CAAC,QAAQ,IACvB,SAAS,EAAE,EAAE,CACX,KAAK,EACL,QAAQ,KAAK,QAAQ;4BACnB,2FAA2F,CAC9F,YAEA,QAAQ,GACgB,EAC3B,KAAC,sBAAsB,KAAG,IACF,GACf,GACU,CAC1B,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EAC2C;IACnD,OAAO,CACL,KAAC,eAAe,CAAC,KAAK,iBACV,cAAc,EACxB,SAAS,EAAE,EAAE,CACX,oDAAoD,EACpD,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EAC0C;IAClD,OAAO,CACL,MAAC,eAAe,CAAC,IAAI,iBACT,aAAa,EACvB,SAAS,EAAE,EAAE,CACX,ygBAAygB,EACzgB,SAAS,CACV,KACG,KAAK,aAET,eAAM,SAAS,EAAC,4DAA4D,YAC1E,KAAC,eAAe,CAAC,aAAa,cAC5B,KAAC,SAAS,IAAC,SAAS,EAAC,oCAAoC,GAAG,GAC9B,GAC3B,EACP,KAAC,eAAe,CAAC,QAAQ,cAAE,QAAQ,GAA4B,IAC1C,CACxB,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EAC+C;IACvD,OAAO,CACL,KAAC,eAAe,CAAC,SAAS,iBACd,kBAAkB,EAC5B,SAAS,EAAE,EAAE,CAAC,+CAA+C,EAAE,SAAS,CAAC,KACrE,KAAK,GACT,CACH,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACoD;IAC5D,OAAO,CACL,KAAC,eAAe,CAAC,cAAc,iBACnB,yBAAyB,EACnC,SAAS,EAAE,EAAE,CACX,sDAAsD,EACtD,SAAS,CACV,KACG,KAAK,YAET,KAAC,aAAa,IAAC,SAAS,EAAC,QAAQ,GAAG,GACL,CAClC,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACsD;IAC9D,OAAO,CACL,KAAC,eAAe,CAAC,gBAAgB,iBACrB,2BAA2B,EACrC,SAAS,EAAE,EAAE,CACX,sDAAsD,EACtD,SAAS,CACV,KACG,KAAK,YAET,KAAC,eAAe,IAAC,SAAS,EAAC,QAAQ,GAAG,GACL,CACpC,CAAC;AACJ,CAAC;AAED,OAAO,EACL,MAAM,EACN,aAAa,EACb,WAAW,EACX,UAAU,EACV,WAAW,EACX,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,WAAW,GACZ,CAAC"}
|
|
@@ -9,7 +9,7 @@ declare function SheetOverlay({ className, ...props }: React.ComponentProps<type
|
|
|
9
9
|
declare const sheetVariants: (props?: ({
|
|
10
10
|
side?: "bottom" | "left" | "right" | "top" | null | undefined;
|
|
11
11
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
12
|
-
|
|
12
|
+
type SheetContentProps = React.ComponentProps<typeof SheetPrimitive.Content> & VariantProps<typeof sheetVariants> & {
|
|
13
13
|
showCloseButton?: boolean;
|
|
14
14
|
/**
|
|
15
15
|
* Element to refocus when the sheet closes, instead of the auto-captured
|
|
@@ -18,7 +18,10 @@ declare function SheetContent({ className, children, side, showCloseButton, onOp
|
|
|
18
18
|
* still-mounted element, typically the menu's own trigger.
|
|
19
19
|
*/
|
|
20
20
|
restoreFocusRef?: React.RefObject<HTMLElement | null>;
|
|
21
|
-
|
|
21
|
+
/** Accessible name for the close button. Sourced from the i18n dictionary, overridable via this prop. */
|
|
22
|
+
closeLabel?: string;
|
|
23
|
+
};
|
|
24
|
+
declare function SheetContent({ className, children, side, showCloseButton, onOpenAutoFocus, onCloseAutoFocus, restoreFocusRef, closeLabel, ...props }: SheetContentProps): React.JSX.Element;
|
|
22
25
|
declare function SheetHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
23
26
|
declare function SheetFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
24
27
|
declare function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>): React.JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/sheet.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/sheet.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAclE,iBAAS,KAAK,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,IAAI,CAAC,qBAE5E;AAED,iBAAS,YAAY,CAAC,EACpB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,OAAO,CAAC,qBAErD;AAED,iBAAS,UAAU,CAAC,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,KAAK,CAAC,qBAEnD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,MAAM,CAAC,qBAEpD;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,OAAO,CAAC,qBAcrD;AAED,QAAA,MAAM,aAAa;;8EAelB,CAAC;AAEF,KAAK,iBAAiB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,OAAO,CAAC,GAC1E,YAAY,CAAC,OAAO,aAAa,CAAC,GAAG;IACnC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEJ,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,QAAQ,EACR,IAAa,EACb,eAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,GAAG,KAAK,EACT,EAAE,iBAAiB,qBA6CnB;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQxE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQxE;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,KAAK,CAAC,qBAUnD;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,cAAc,CAAC,WAAW,CAAC,qBAQzD;AAED,OAAO,EACL,KAAK,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,YAAY,GACb,CAAC"}
|
|
@@ -5,6 +5,7 @@ import { Dialog as SheetPrimitive } from "radix-ui";
|
|
|
5
5
|
import { cva } from "class-variance-authority";
|
|
6
6
|
import { XIcon } from "../../icons/lucide.js";
|
|
7
7
|
import { cn } from "../../lib/utils.js";
|
|
8
|
+
import { useC4Dictionary } from "../../lib/i18n/context.js";
|
|
8
9
|
import { composeCloseAutoFocus, composeOpenAutoFocus, useDialogFocusReturn, } from "../../lib/focus-visible-on-close.js";
|
|
9
10
|
import { C4Provider } from "./c4-provider.js";
|
|
10
11
|
import { Button } from "../ui/button.js";
|
|
@@ -39,8 +40,9 @@ const sheetVariants = cva("fixed z-50 flex flex-col bg-popover text-sm text-popo
|
|
|
39
40
|
},
|
|
40
41
|
defaultVariants: { side: "left" },
|
|
41
42
|
});
|
|
42
|
-
function SheetContent({ className, children, side = "left", showCloseButton = true, onOpenAutoFocus, onCloseAutoFocus, restoreFocusRef, ...props }) {
|
|
43
|
+
function SheetContent({ className, children, side = "left", showCloseButton = true, onOpenAutoFocus, onCloseAutoFocus, restoreFocusRef, closeLabel, ...props }) {
|
|
43
44
|
const dialogFocusReturn = useDialogFocusReturn(restoreFocusRef);
|
|
45
|
+
const { t } = useC4Dictionary();
|
|
44
46
|
return (_jsx(SheetPortal, { children: _jsxs(C4Provider, { children: [_jsx(SheetOverlay, {}), _jsxs(SheetPrimitive.Content, { "data-slot": "sheet-content", "data-side": side,
|
|
45
47
|
// #26413 — restore focus to the real launcher on close, even for
|
|
46
48
|
// controlled-`open` usage with no `SheetTrigger` (see
|
|
@@ -48,7 +50,7 @@ function SheetContent({ className, children, side = "left", showCloseButton = tr
|
|
|
48
50
|
onOpenAutoFocus: composeOpenAutoFocus(onOpenAutoFocus, dialogFocusReturn.onOpenAutoFocus),
|
|
49
51
|
// #259 — make the trigger's restored focus ring visible on a mouse
|
|
50
52
|
// close (see focus-visible-on-close).
|
|
51
|
-
onCloseAutoFocus: composeCloseAutoFocus(onCloseAutoFocus, dialogFocusReturn.onCloseAutoFocus), className: cn(sheetVariants({ side }), className), ...props, children: [children, showCloseButton && (_jsx(SheetPrimitive.Close, { "data-slot": "sheet-close", asChild: true, children: _jsxs(Button, { variant: "ghost", className: "absolute top-2 right-2", size: "icon-sm", children: [_jsx(XIcon, {}), _jsx("span", { className: "sr-only", children: "
|
|
53
|
+
onCloseAutoFocus: composeCloseAutoFocus(onCloseAutoFocus, dialogFocusReturn.onCloseAutoFocus), className: cn(sheetVariants({ side }), className), ...props, children: [children, showCloseButton && (_jsx(SheetPrimitive.Close, { "data-slot": "sheet-close", asChild: true, children: _jsxs(Button, { variant: "ghost", className: "absolute top-2 right-2", size: "icon-sm", children: [_jsx(XIcon, {}), _jsx("span", { className: "sr-only", children: closeLabel ?? t("sheet.close") })] }) }))] })] }) }));
|
|
52
54
|
}
|
|
53
55
|
function SheetHeader({ className, ...props }) {
|
|
54
56
|
return (_jsx("div", { "data-slot": "sheet-header", className: cn("flex flex-col gap-1.5 p-4", className), ...props }));
|