@mediaradar/design-components 0.14.0 → 0.15.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/index.cjs +83 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -10
- package/dist/index.d.ts +38 -10
- package/dist/index.js +83 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -978,18 +978,29 @@ declare function StackedColumnPlaceholder({ height, width, legendRows, axis, }:
|
|
|
978
978
|
* escapes both `overflow` clipping inside scrollable panels and the broken
|
|
979
979
|
* `fixed` containing block a transformed ancestor creates.
|
|
980
980
|
* @tokens rounded-control, border-border-default, bg-surface, shadow-overlay,
|
|
981
|
-
* text-text-primary, bg-text-primary, text-surface
|
|
981
|
+
* text-text-primary, bg-text-primary, text-surface,
|
|
982
|
+
* duration-(--mr-motion-duration-interactive)
|
|
982
983
|
* @variant surface - Default. Themed surface/border bubble -- matches the
|
|
983
984
|
* Recharts chart tooltips this was originally built for.
|
|
984
985
|
* @variant inverted - Solid dark pill (no border) -- for a tooltip sitting
|
|
985
986
|
* over a nav rail or other icon-only surface, where the themed surface bubble
|
|
986
987
|
* doesn't read as clearly against a busy background.
|
|
987
988
|
* @behavior Shows on hover and on keyboard focus (so it stays reachable
|
|
988
|
-
* without a pointer) after `delay` ms (0 = immediate, the default).
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
989
|
+
* without a pointer) after `delay` ms (0 = immediate, the default). Fades in
|
|
990
|
+
* and out (opacity, `ease-in-out`, over the sys
|
|
991
|
+
* `--mr-motion-duration-interactive` token -- 200ms today); the bubble stays
|
|
992
|
+
* mounted through the fade-out and is removed from the DOM once it ends.
|
|
993
|
+
* Under `prefers-reduced-motion: reduce` there is no fade: it appears and is
|
|
994
|
+
* removed instantly. The hide starts on mouseleave/blur, cancelling a pending
|
|
995
|
+
* delayed show if it hasn't fired yet -- except in `interactive` mode, which
|
|
996
|
+
* delays the hide briefly so the pointer can travel from the trigger into the
|
|
997
|
+
* tooltip's own content. Re-hovering (or re-focusing) the trigger while the
|
|
998
|
+
* bubble is still fading out reverses the fade from where it is, without
|
|
999
|
+
* waiting out `delay` again, since it is already on screen; so does moving
|
|
1000
|
+
* the pointer back into an `interactive` bubble. A non-interactive bubble is
|
|
1001
|
+
* `pointer-events-none` throughout, fade-out included, so it never
|
|
1002
|
+
* intercepts clicks meant for what sits under it. Pass `whenTruncated` to
|
|
1003
|
+
* only surface it once the trigger's content
|
|
993
1004
|
* is actually clipped by ellipsis (`scrollWidth > clientWidth`). `placement`
|
|
994
1005
|
* 'top'/'bottom' centre (or `align="start"` left-align) on the trigger;
|
|
995
1006
|
* 'right' centres vertically beside the anchor and 'bottom-end' hangs below,
|
|
@@ -1402,9 +1413,20 @@ declare const buttonVariants: (props?: ({
|
|
|
1402
1413
|
size?: "icon" | "sm" | "md" | "lg" | null | undefined;
|
|
1403
1414
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
1404
1415
|
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
1405
|
-
/**
|
|
1416
|
+
/**
|
|
1417
|
+
* Render the button styling onto the single child element instead of a
|
|
1418
|
+
* `<button>` (e.g. a router `<Link>` or an `<a>`). The child must be exactly
|
|
1419
|
+
* one React element; it keeps its own tag, content and semantics.
|
|
1420
|
+
*/
|
|
1406
1421
|
asChild?: boolean;
|
|
1407
|
-
/**
|
|
1422
|
+
/**
|
|
1423
|
+
* Shows a spinner in place of the leading edge and disables the button.
|
|
1424
|
+
* With `asChild` there is no spinner (the child owns its content, and a
|
|
1425
|
+
* spinner belongs inside a real `<button>`): `loading` only sets
|
|
1426
|
+
* `aria-busy` and forwards `disabled` to the child, which only has an
|
|
1427
|
+
* effect on elements that support it -- an `<a>` stays clickable, so block
|
|
1428
|
+
* navigation yourself while loading.
|
|
1429
|
+
*/
|
|
1408
1430
|
loading?: boolean;
|
|
1409
1431
|
/** Button label and/or icon. With `asChild`, the single element that receives the button styling. */
|
|
1410
1432
|
children?: ReactNode;
|
|
@@ -1430,8 +1452,13 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantPr
|
|
|
1430
1452
|
* but Button suppresses it (`focus-visible:outline-hidden focus-visible:ring-0`)
|
|
1431
1453
|
* and relies on its per-variant hover/brightness treatment instead. `loading`
|
|
1432
1454
|
* implies `disabled` (and sets `aria-busy`), since a control mid-submit
|
|
1433
|
-
* shouldn't accept another click.
|
|
1434
|
-
*
|
|
1455
|
+
* shouldn't accept another click. `asChild` renders only the child element,
|
|
1456
|
+
* through Radix `Slot`, with the button classes, ref and remaining props
|
|
1457
|
+
* merged onto it -- no wrapping `<button>`. With `asChild`, `loading` draws
|
|
1458
|
+
* no spinner; it sets `aria-busy` and forwards `disabled`, which an `<a>`
|
|
1459
|
+
* ignores, so a loading link stays clickable unless the caller blocks it.
|
|
1460
|
+
* @a11y Renders a native <button>, so it keeps native keyboard behaviour
|
|
1461
|
+
* (with `asChild`, the child element's own semantics apply instead).
|
|
1435
1462
|
* `loading` sets aria-busy, and the spinner is aria-hidden. Icon-only
|
|
1436
1463
|
* usage needs an aria-label from the caller -- nothing here supplies one.
|
|
1437
1464
|
* @usage
|
|
@@ -1440,6 +1467,7 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantPr
|
|
|
1440
1467
|
* <Button variant="secondary" size="sm">Cancel</Button>
|
|
1441
1468
|
* <Button variant="danger">Delete</Button>
|
|
1442
1469
|
* <Button variant="primary" loading>Saving…</Button>
|
|
1470
|
+
* <Button asChild variant="secondary"><a href="/reports">View reports</a></Button>
|
|
1443
1471
|
* ```
|
|
1444
1472
|
*/
|
|
1445
1473
|
declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -978,18 +978,29 @@ declare function StackedColumnPlaceholder({ height, width, legendRows, axis, }:
|
|
|
978
978
|
* escapes both `overflow` clipping inside scrollable panels and the broken
|
|
979
979
|
* `fixed` containing block a transformed ancestor creates.
|
|
980
980
|
* @tokens rounded-control, border-border-default, bg-surface, shadow-overlay,
|
|
981
|
-
* text-text-primary, bg-text-primary, text-surface
|
|
981
|
+
* text-text-primary, bg-text-primary, text-surface,
|
|
982
|
+
* duration-(--mr-motion-duration-interactive)
|
|
982
983
|
* @variant surface - Default. Themed surface/border bubble -- matches the
|
|
983
984
|
* Recharts chart tooltips this was originally built for.
|
|
984
985
|
* @variant inverted - Solid dark pill (no border) -- for a tooltip sitting
|
|
985
986
|
* over a nav rail or other icon-only surface, where the themed surface bubble
|
|
986
987
|
* doesn't read as clearly against a busy background.
|
|
987
988
|
* @behavior Shows on hover and on keyboard focus (so it stays reachable
|
|
988
|
-
* without a pointer) after `delay` ms (0 = immediate, the default).
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
989
|
+
* without a pointer) after `delay` ms (0 = immediate, the default). Fades in
|
|
990
|
+
* and out (opacity, `ease-in-out`, over the sys
|
|
991
|
+
* `--mr-motion-duration-interactive` token -- 200ms today); the bubble stays
|
|
992
|
+
* mounted through the fade-out and is removed from the DOM once it ends.
|
|
993
|
+
* Under `prefers-reduced-motion: reduce` there is no fade: it appears and is
|
|
994
|
+
* removed instantly. The hide starts on mouseleave/blur, cancelling a pending
|
|
995
|
+
* delayed show if it hasn't fired yet -- except in `interactive` mode, which
|
|
996
|
+
* delays the hide briefly so the pointer can travel from the trigger into the
|
|
997
|
+
* tooltip's own content. Re-hovering (or re-focusing) the trigger while the
|
|
998
|
+
* bubble is still fading out reverses the fade from where it is, without
|
|
999
|
+
* waiting out `delay` again, since it is already on screen; so does moving
|
|
1000
|
+
* the pointer back into an `interactive` bubble. A non-interactive bubble is
|
|
1001
|
+
* `pointer-events-none` throughout, fade-out included, so it never
|
|
1002
|
+
* intercepts clicks meant for what sits under it. Pass `whenTruncated` to
|
|
1003
|
+
* only surface it once the trigger's content
|
|
993
1004
|
* is actually clipped by ellipsis (`scrollWidth > clientWidth`). `placement`
|
|
994
1005
|
* 'top'/'bottom' centre (or `align="start"` left-align) on the trigger;
|
|
995
1006
|
* 'right' centres vertically beside the anchor and 'bottom-end' hangs below,
|
|
@@ -1402,9 +1413,20 @@ declare const buttonVariants: (props?: ({
|
|
|
1402
1413
|
size?: "icon" | "sm" | "md" | "lg" | null | undefined;
|
|
1403
1414
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
1404
1415
|
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
1405
|
-
/**
|
|
1416
|
+
/**
|
|
1417
|
+
* Render the button styling onto the single child element instead of a
|
|
1418
|
+
* `<button>` (e.g. a router `<Link>` or an `<a>`). The child must be exactly
|
|
1419
|
+
* one React element; it keeps its own tag, content and semantics.
|
|
1420
|
+
*/
|
|
1406
1421
|
asChild?: boolean;
|
|
1407
|
-
/**
|
|
1422
|
+
/**
|
|
1423
|
+
* Shows a spinner in place of the leading edge and disables the button.
|
|
1424
|
+
* With `asChild` there is no spinner (the child owns its content, and a
|
|
1425
|
+
* spinner belongs inside a real `<button>`): `loading` only sets
|
|
1426
|
+
* `aria-busy` and forwards `disabled` to the child, which only has an
|
|
1427
|
+
* effect on elements that support it -- an `<a>` stays clickable, so block
|
|
1428
|
+
* navigation yourself while loading.
|
|
1429
|
+
*/
|
|
1408
1430
|
loading?: boolean;
|
|
1409
1431
|
/** Button label and/or icon. With `asChild`, the single element that receives the button styling. */
|
|
1410
1432
|
children?: ReactNode;
|
|
@@ -1430,8 +1452,13 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantPr
|
|
|
1430
1452
|
* but Button suppresses it (`focus-visible:outline-hidden focus-visible:ring-0`)
|
|
1431
1453
|
* and relies on its per-variant hover/brightness treatment instead. `loading`
|
|
1432
1454
|
* implies `disabled` (and sets `aria-busy`), since a control mid-submit
|
|
1433
|
-
* shouldn't accept another click.
|
|
1434
|
-
*
|
|
1455
|
+
* shouldn't accept another click. `asChild` renders only the child element,
|
|
1456
|
+
* through Radix `Slot`, with the button classes, ref and remaining props
|
|
1457
|
+
* merged onto it -- no wrapping `<button>`. With `asChild`, `loading` draws
|
|
1458
|
+
* no spinner; it sets `aria-busy` and forwards `disabled`, which an `<a>`
|
|
1459
|
+
* ignores, so a loading link stays clickable unless the caller blocks it.
|
|
1460
|
+
* @a11y Renders a native <button>, so it keeps native keyboard behaviour
|
|
1461
|
+
* (with `asChild`, the child element's own semantics apply instead).
|
|
1435
1462
|
* `loading` sets aria-busy, and the spinner is aria-hidden. Icon-only
|
|
1436
1463
|
* usage needs an aria-label from the caller -- nothing here supplies one.
|
|
1437
1464
|
* @usage
|
|
@@ -1440,6 +1467,7 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantPr
|
|
|
1440
1467
|
* <Button variant="secondary" size="sm">Cancel</Button>
|
|
1441
1468
|
* <Button variant="danger">Delete</Button>
|
|
1442
1469
|
* <Button variant="primary" loading>Saving…</Button>
|
|
1470
|
+
* <Button asChild variant="secondary"><a href="/reports">View reports</a></Button>
|
|
1443
1471
|
* ```
|
|
1444
1472
|
*/
|
|
1445
1473
|
declare const Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
package/dist/index.js
CHANGED
|
@@ -1836,6 +1836,19 @@ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
|
1836
1836
|
function cx6(...parts) {
|
|
1837
1837
|
return parts.filter(Boolean).join(" ");
|
|
1838
1838
|
}
|
|
1839
|
+
function exitDurationMs(el) {
|
|
1840
|
+
if (typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return 0;
|
|
1841
|
+
const cs = window.getComputedStyle(el);
|
|
1842
|
+
if (cs.transitionProperty === "none") return 0;
|
|
1843
|
+
const toMs = (v) => {
|
|
1844
|
+
const n = parseFloat(v);
|
|
1845
|
+
if (!Number.isFinite(n)) return 0;
|
|
1846
|
+
return v.trim().endsWith("ms") ? n : n * 1e3;
|
|
1847
|
+
};
|
|
1848
|
+
const durations = cs.transitionDuration.split(",").map(toMs);
|
|
1849
|
+
const delays = cs.transitionDelay.split(",").map(toMs);
|
|
1850
|
+
return Math.max(0, ...durations.map((d, i) => d + (delays[i] ?? delays[0] ?? 0)));
|
|
1851
|
+
}
|
|
1839
1852
|
function Tooltip({
|
|
1840
1853
|
label,
|
|
1841
1854
|
children,
|
|
@@ -1855,6 +1868,10 @@ function Tooltip({
|
|
|
1855
1868
|
const boxRef = useRef9(null);
|
|
1856
1869
|
const hideTimer = useRef9(null);
|
|
1857
1870
|
const showTimer = useRef9(null);
|
|
1871
|
+
const exitTimer = useRef9(null);
|
|
1872
|
+
const [shown, setShown] = useState8(false);
|
|
1873
|
+
const openRef = useRef9(false);
|
|
1874
|
+
const mountedRef = useRef9(false);
|
|
1858
1875
|
const id = useId();
|
|
1859
1876
|
const clearHideTimer = () => {
|
|
1860
1877
|
if (hideTimer.current) {
|
|
@@ -1868,10 +1885,17 @@ function Tooltip({
|
|
|
1868
1885
|
showTimer.current = null;
|
|
1869
1886
|
}
|
|
1870
1887
|
};
|
|
1888
|
+
const clearExitTimer = () => {
|
|
1889
|
+
if (exitTimer.current) {
|
|
1890
|
+
clearTimeout(exitTimer.current);
|
|
1891
|
+
exitTimer.current = null;
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1871
1894
|
useEffect8(
|
|
1872
1895
|
() => () => {
|
|
1873
1896
|
clearHideTimer();
|
|
1874
1897
|
clearShowTimer();
|
|
1898
|
+
clearExitTimer();
|
|
1875
1899
|
},
|
|
1876
1900
|
[]
|
|
1877
1901
|
);
|
|
@@ -1879,26 +1903,56 @@ function Tooltip({
|
|
|
1879
1903
|
const el = anchorRef ? anchorRef.current : ref.current;
|
|
1880
1904
|
if (!el) return;
|
|
1881
1905
|
if (whenTruncated && el.scrollWidth <= el.clientWidth) return;
|
|
1906
|
+
clearExitTimer();
|
|
1907
|
+
openRef.current = true;
|
|
1882
1908
|
const r = el.getBoundingClientRect();
|
|
1883
|
-
setBoxWidth(null);
|
|
1909
|
+
if (!mountedRef.current) setBoxWidth(null);
|
|
1910
|
+
mountedRef.current = true;
|
|
1884
1911
|
setAnchor({ top: r.top, left: r.left, width: r.width, height: r.height, bottom: r.bottom, right: r.right });
|
|
1885
1912
|
if (anchorRef) el.setAttribute("aria-describedby", id);
|
|
1886
1913
|
};
|
|
1887
1914
|
const show = () => {
|
|
1888
1915
|
clearHideTimer();
|
|
1889
|
-
|
|
1916
|
+
clearShowTimer();
|
|
1917
|
+
if (delay > 0 && !mountedRef.current) {
|
|
1890
1918
|
showTimer.current = setTimeout(reveal, delay);
|
|
1891
1919
|
} else {
|
|
1892
1920
|
reveal();
|
|
1893
1921
|
}
|
|
1894
1922
|
};
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1923
|
+
const unmount = () => {
|
|
1924
|
+
clearExitTimer();
|
|
1925
|
+
if (openRef.current || !mountedRef.current) return;
|
|
1926
|
+
mountedRef.current = false;
|
|
1898
1927
|
setAnchor(null);
|
|
1899
1928
|
setBoxWidth(null);
|
|
1900
1929
|
if (anchorRef?.current) anchorRef.current.removeAttribute("aria-describedby");
|
|
1901
1930
|
};
|
|
1931
|
+
const hideNow = () => {
|
|
1932
|
+
clearShowTimer();
|
|
1933
|
+
clearHideTimer();
|
|
1934
|
+
openRef.current = false;
|
|
1935
|
+
if (!mountedRef.current) return;
|
|
1936
|
+
setShown(false);
|
|
1937
|
+
clearExitTimer();
|
|
1938
|
+
const ms = boxRef.current ? exitDurationMs(boxRef.current) : 0;
|
|
1939
|
+
if (ms === 0) {
|
|
1940
|
+
unmount();
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
exitTimer.current = setTimeout(unmount, ms + 50);
|
|
1944
|
+
};
|
|
1945
|
+
const keepOpen = () => {
|
|
1946
|
+
clearHideTimer();
|
|
1947
|
+
if (openRef.current || !mountedRef.current) return;
|
|
1948
|
+
clearExitTimer();
|
|
1949
|
+
openRef.current = true;
|
|
1950
|
+
setShown(true);
|
|
1951
|
+
};
|
|
1952
|
+
const onBoxTransitionEnd = (e) => {
|
|
1953
|
+
if (e.target !== e.currentTarget || e.propertyName !== "opacity") return;
|
|
1954
|
+
if (!openRef.current) unmount();
|
|
1955
|
+
};
|
|
1902
1956
|
const hide = () => {
|
|
1903
1957
|
clearShowTimer();
|
|
1904
1958
|
if (!interactive) {
|
|
@@ -1928,6 +1982,7 @@ function Tooltip({
|
|
|
1928
1982
|
const el = boxRef.current;
|
|
1929
1983
|
if (!el) return;
|
|
1930
1984
|
setBoxWidth(el.getBoundingClientRect().width);
|
|
1985
|
+
if (openRef.current) setShown(true);
|
|
1931
1986
|
}, [anchor, label]);
|
|
1932
1987
|
const margin = 8;
|
|
1933
1988
|
let style = { top: 0, left: 0 };
|
|
@@ -1955,10 +2010,19 @@ function Tooltip({
|
|
|
1955
2010
|
id,
|
|
1956
2011
|
"data-testid": testIdPrefix && `${testIdPrefix}-tooltip`,
|
|
1957
2012
|
style: { top: style.top, left: style.left, right: style.right, visibility: boxWidth == null ? "hidden" : "visible" },
|
|
1958
|
-
onMouseEnter: interactive ?
|
|
2013
|
+
onMouseEnter: interactive ? keepOpen : void 0,
|
|
1959
2014
|
onMouseLeave: interactive ? hide : void 0,
|
|
2015
|
+
onTransitionEnd: onBoxTransitionEnd,
|
|
1960
2016
|
className: cx6(
|
|
1961
2017
|
"fixed z-[90] w-max max-w-xs whitespace-normal break-words px-2.5 py-1.5 text-xs leading-snug",
|
|
2018
|
+
// Fade in/out. The sys duration token rather than a ref one:
|
|
2019
|
+
// it is what Button's hover/press transition uses, and a
|
|
2020
|
+
// tooltip's fade is the same kind of interactive feedback.
|
|
2021
|
+
// design-tokens has no in-out curve (ease-interactive is the
|
|
2022
|
+
// decelerating "standard" curve), so ease-in-out is Tailwind's.
|
|
2023
|
+
"transition-opacity duration-(--mr-motion-duration-interactive) ease-in-out motion-reduce:transition-none",
|
|
2024
|
+
shown ? "opacity-100" : "opacity-0",
|
|
2025
|
+
// Non-interactive: never a pointer target, fade-out included.
|
|
1962
2026
|
interactive ? "pointer-events-auto" : "pointer-events-none",
|
|
1963
2027
|
variant === "inverted" ? "rounded-control bg-text-primary text-surface" : "rounded-control border border-border-default bg-surface text-text-primary shadow-overlay",
|
|
1964
2028
|
translate
|
|
@@ -2698,21 +2762,19 @@ var buttonVariants = cva(
|
|
|
2698
2762
|
);
|
|
2699
2763
|
var Button = forwardRef2(
|
|
2700
2764
|
({ className, variant, size, asChild = false, loading = false, disabled, children, ...props }, ref) => {
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
}
|
|
2715
|
-
);
|
|
2765
|
+
const shared = {
|
|
2766
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
2767
|
+
disabled: disabled || loading,
|
|
2768
|
+
"aria-busy": loading || void 0,
|
|
2769
|
+
...props
|
|
2770
|
+
};
|
|
2771
|
+
if (asChild) {
|
|
2772
|
+
return /* @__PURE__ */ jsx25(Slot, { ref, ...shared, children });
|
|
2773
|
+
}
|
|
2774
|
+
return /* @__PURE__ */ jsxs23("button", { ref, ...shared, children: [
|
|
2775
|
+
loading && /* @__PURE__ */ jsx25(Loader22, { className: "size-4 shrink-0 animate-spin", "aria-hidden": "true" }),
|
|
2776
|
+
children
|
|
2777
|
+
] });
|
|
2716
2778
|
}
|
|
2717
2779
|
);
|
|
2718
2780
|
Button.displayName = "Button";
|