@blakesteve/roster 5.0.1 → 5.2.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 +110 -9
- package/dist/components/atoms/Chip/Chip.cjs +1 -1
- package/dist/components/atoms/Chip/Chip.js +27 -27
- package/dist/components/atoms/Disclosure/Disclosure.cjs +1 -1
- package/dist/components/atoms/Disclosure/Disclosure.js +42 -44
- package/dist/components/atoms/Input/Input.cjs +1 -1
- package/dist/components/atoms/Input/Input.js +46 -41
- package/dist/components/atoms/Spinner/Spinner.cjs +1 -1
- package/dist/components/atoms/Spinner/Spinner.d.ts +6 -1
- package/dist/components/atoms/Spinner/Spinner.js +9 -8
- package/dist/components/atoms/Stat/Stat.cjs +1 -1
- package/dist/components/atoms/Stat/Stat.d.ts +27 -2
- package/dist/components/atoms/Stat/Stat.js +38 -13
- package/dist/components/atoms/Switch/Switch.cjs +1 -1
- package/dist/components/atoms/Switch/Switch.d.ts +11 -1
- package/dist/components/atoms/Switch/Switch.js +38 -36
- package/dist/components/atoms/Switch/switch-variants.cjs +1 -1
- package/dist/components/atoms/Switch/switch-variants.js +1 -1
- package/dist/components/molecules/Carousel/Carousel.cjs +1 -0
- package/dist/components/molecules/Carousel/Carousel.d.ts +146 -0
- package/dist/components/molecules/Carousel/Carousel.js +363 -0
- package/dist/components/molecules/Carousel/carousel-layout.cjs +1 -0
- package/dist/components/molecules/Carousel/carousel-layout.d.ts +23 -0
- package/dist/components/molecules/Carousel/carousel-layout.js +22 -0
- package/dist/components/molecules/Carousel/carousel-math.cjs +1 -0
- package/dist/components/molecules/Carousel/carousel-math.d.ts +60 -0
- package/dist/components/molecules/Carousel/carousel-math.js +58 -0
- package/dist/components/molecules/EmptyState/EmptyState.cjs +1 -1
- package/dist/components/molecules/EmptyState/EmptyState.d.ts +7 -1
- package/dist/components/molecules/EmptyState/EmptyState.js +18 -14
- package/dist/components/molecules/LiquidTabs/LiquidNav.cjs +1 -0
- package/dist/components/molecules/LiquidTabs/LiquidNav.d.ts +62 -0
- package/dist/components/molecules/LiquidTabs/LiquidNav.js +78 -0
- package/dist/components/molecules/LiquidTabs/LiquidTabs.cjs +1 -0
- package/dist/components/molecules/LiquidTabs/LiquidTabs.d.ts +69 -0
- package/dist/components/molecules/LiquidTabs/LiquidTabs.js +92 -0
- package/dist/components/molecules/LiquidTabs/liquid-pill.cjs +1 -0
- package/dist/components/molecules/LiquidTabs/liquid-pill.d.ts +56 -0
- package/dist/components/molecules/LiquidTabs/liquid-pill.js +129 -0
- package/dist/components/molecules/LiquidTabs/liquid-tab-id.cjs +1 -0
- package/dist/components/molecules/LiquidTabs/liquid-tab-id.d.ts +12 -0
- package/dist/components/molecules/LiquidTabs/liquid-tab-id.js +6 -0
- package/dist/components/molecules/Toast/Toaster.cjs +1 -1
- package/dist/components/molecules/Toast/Toaster.d.ts +7 -0
- package/dist/components/molecules/Toast/Toaster.js +67 -45
- package/dist/components/organisms/Dialog/Dialog.cjs +1 -1
- package/dist/components/organisms/Dialog/Dialog.js +102 -79
- package/dist/components/organisms/Navbar/Navbar.cjs +1 -1
- package/dist/components/organisms/Navbar/Navbar.js +157 -157
- package/dist/components/organisms/Sheet/Sheet.cjs +1 -0
- package/dist/components/organisms/Sheet/Sheet.d.ts +85 -0
- package/dist/components/organisms/Sheet/Sheet.js +304 -0
- package/dist/components/organisms/Sheet/sheet-drag.cjs +1 -0
- package/dist/components/organisms/Sheet/sheet-drag.d.ts +40 -0
- package/dist/components/organisms/Sheet/sheet-drag.js +22 -0
- package/dist/components/organisms/Sheet/sheet-title.cjs +1 -0
- package/dist/components/organisms/Sheet/sheet-title.d.ts +10 -0
- package/dist/components/organisms/Sheet/sheet-title.js +17 -0
- package/dist/index.d.ts +5 -1
- package/dist/internal/motion.cjs +1 -0
- package/dist/internal/motion.d.ts +12 -0
- package/dist/internal/motion.js +21 -0
- package/dist/internal/page-inert.cjs +1 -0
- package/dist/internal/page-inert.d.ts +51 -0
- package/dist/internal/page-inert.js +36 -0
- package/dist/meta.json +3 -3
- package/dist/roster.cjs +1 -1
- package/dist/roster.css +1 -1
- package/dist/roster.js +130 -122
- package/package.json +1 -1
- package/dist/components/atoms/LiquidTabs/LiquidTabs.cjs +0 -1
- package/dist/components/atoms/LiquidTabs/LiquidTabs.d.ts +0 -39
- package/dist/components/atoms/LiquidTabs/LiquidTabs.js +0 -120
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const E = 6, u = 1;
|
|
2
|
+
function a(t) {
|
|
3
|
+
return Math.max(0, t.scrollWidth - t.clientWidth);
|
|
4
|
+
}
|
|
5
|
+
function L(t) {
|
|
6
|
+
return a(t) > 1;
|
|
7
|
+
}
|
|
8
|
+
function h(t) {
|
|
9
|
+
return t.scrollLeft > 1;
|
|
10
|
+
}
|
|
11
|
+
function d(t) {
|
|
12
|
+
return t.scrollLeft < a(t) - 1;
|
|
13
|
+
}
|
|
14
|
+
function o(t, e) {
|
|
15
|
+
const n = t.items[e];
|
|
16
|
+
if (!n) return 0;
|
|
17
|
+
const r = t.align === "start" ? n.start - t.paddingStart : (n.start + n.end) / 2 - (t.paddingStart + (t.clientWidth - t.paddingStart - t.paddingEnd) / 2);
|
|
18
|
+
return Math.min(Math.max(0, r), a(t));
|
|
19
|
+
}
|
|
20
|
+
function l(t) {
|
|
21
|
+
const e = t.scrollLeft - 1, n = t.scrollLeft + t.clientWidth + 1, r = [];
|
|
22
|
+
return t.items.forEach((i, s) => {
|
|
23
|
+
i.start >= e && i.end <= n && r.push(s);
|
|
24
|
+
}), r;
|
|
25
|
+
}
|
|
26
|
+
function f(t) {
|
|
27
|
+
let e = 0, n = 1 / 0;
|
|
28
|
+
return t.items.forEach((r, i) => {
|
|
29
|
+
const s = Math.abs(o(t, i) - t.scrollLeft);
|
|
30
|
+
s < n && (e = i, n = s);
|
|
31
|
+
}), e;
|
|
32
|
+
}
|
|
33
|
+
function R(t) {
|
|
34
|
+
const e = l(t);
|
|
35
|
+
return e.length ? e[0] : f(t);
|
|
36
|
+
}
|
|
37
|
+
function A(t, e) {
|
|
38
|
+
const n = t.items.length;
|
|
39
|
+
if (!n) return 0;
|
|
40
|
+
const r = l(t), i = r.length ? r[0] : f(t), s = Math.max(1, r.length);
|
|
41
|
+
let c = Math.min(n - 1, Math.max(0, i + e * s));
|
|
42
|
+
for (; c > 0 && c < n - 1 && Math.abs(o(t, c) - t.scrollLeft) <= 1; )
|
|
43
|
+
c += e;
|
|
44
|
+
return o(t, c);
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
E as CAROUSEL_DRAG_THRESHOLD,
|
|
48
|
+
u as CAROUSEL_TOLERANCE,
|
|
49
|
+
d as canScrollNext,
|
|
50
|
+
h as canScrollPrev,
|
|
51
|
+
R as currentIndex,
|
|
52
|
+
l as fullyVisible,
|
|
53
|
+
a as maxScroll,
|
|
54
|
+
f as nearestIndex,
|
|
55
|
+
L as overflows,
|
|
56
|
+
A as pageTarget,
|
|
57
|
+
o as snapPosition
|
|
58
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime");require("react");const
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime");require("react");const d=require("../../../lib/utils.cjs"),x=require("./empty-state-variants.cjs"),g=({title:a,description:s,icon:r,action:e,headingLevel:l=3,variant:i,className:n,...c})=>{const m=`h${l}`;return t.jsxs("div",{className:d.cn(x.emptyStateVariants({variant:i}),n),...c,children:[r&&t.jsx("div",{className:"rst:mb-4 rst:flex rst:h-12 rst:w-12 rst:items-center rst:justify-center rst:rounded-full rst:bg-gray-100 rst:text-gray-400 rst:dark:bg-gray-800 rst:dark:text-gray-500",children:t.jsx("div",{className:"rst:h-6 rst:w-6 rst:[&>svg]:h-full rst:[&>svg]:w-full",children:r})}),t.jsx(m,{className:"rst:text-lg rst:font-semibold rst:text-gray-900 rst:dark:text-gray-100",children:a}),s&&t.jsx("p",{className:"rst:mt-1 rst:max-w-sm rst:text-sm rst:text-gray-500 rst:dark:text-gray-400",children:s}),e&&t.jsx("div",{className:"rst:mt-6",children:e})]})};exports.EmptyState=g;
|
|
@@ -6,6 +6,12 @@ export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement>, V
|
|
|
6
6
|
description?: string;
|
|
7
7
|
icon?: React.ReactNode;
|
|
8
8
|
action?: React.ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* The title's heading level. `3` by default; set it to follow the heading
|
|
11
|
+
* the empty state sits under, so the page's outline doesn't skip a level.
|
|
12
|
+
* Only the element changes; its classes are the same at every level.
|
|
13
|
+
*/
|
|
14
|
+
headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
9
15
|
}
|
|
10
|
-
declare const EmptyState: ({ title, description, icon, action, variant, className, ...props }: EmptyStateProps) => import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
declare const EmptyState: ({ title, description, icon, action, headingLevel, variant, className, ...props }: EmptyStateProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
17
|
export { EmptyState };
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
2
|
+
import { jsxs as g, jsx as t } from "react/jsx-runtime";
|
|
3
3
|
import "react";
|
|
4
|
-
import { cn as
|
|
5
|
-
import { emptyStateVariants as
|
|
6
|
-
const
|
|
4
|
+
import { cn as n } from "../../../lib/utils.js";
|
|
5
|
+
import { emptyStateVariants as x } from "./empty-state-variants.js";
|
|
6
|
+
const p = ({
|
|
7
7
|
title: a,
|
|
8
8
|
description: r,
|
|
9
9
|
icon: s,
|
|
10
10
|
action: e,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
headingLevel: m = 3,
|
|
12
|
+
variant: l,
|
|
13
|
+
className: d,
|
|
14
|
+
...i
|
|
15
|
+
}) => {
|
|
16
|
+
const c = `h${m}`;
|
|
17
|
+
return /* @__PURE__ */ g("div", { className: n(x({ variant: l }), d), ...i, children: [
|
|
18
|
+
s && /* @__PURE__ */ t("div", { className: "rst:mb-4 rst:flex rst:h-12 rst:w-12 rst:items-center rst:justify-center rst:rounded-full rst:bg-gray-100 rst:text-gray-400 rst:dark:bg-gray-800 rst:dark:text-gray-500", children: /* @__PURE__ */ t("div", { className: "rst:h-6 rst:w-6 rst:[&>svg]:h-full rst:[&>svg]:w-full", children: s }) }),
|
|
19
|
+
/* @__PURE__ */ t(c, { className: "rst:text-lg rst:font-semibold rst:text-gray-900 rst:dark:text-gray-100", children: a }),
|
|
20
|
+
r && /* @__PURE__ */ t("p", { className: "rst:mt-1 rst:max-w-sm rst:text-sm rst:text-gray-500 rst:dark:text-gray-400", children: r }),
|
|
21
|
+
e && /* @__PURE__ */ t("div", { className: "rst:mt-6", children: e })
|
|
22
|
+
] });
|
|
23
|
+
};
|
|
20
24
|
export {
|
|
21
|
-
|
|
25
|
+
p as EmptyState
|
|
22
26
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),b=require("react"),a=require("./liquid-pill.cjs"),N=require("../../../lib/utils.cjs");function I({href:r,className:t,children:d,onClick:c,"aria-current":l,"data-tab-id":s}){return u.jsx("a",{href:r,className:t,onClick:c,"aria-current":l,"data-tab-id":s,children:d})}function P({items:r,activeTab:t,onChange:d,linkComponent:c,variant:l="pill",fullWidth:s=!1,size:q="md","aria-label":y,"aria-labelledby":L,className:p}){const o=b.useRef(null),f=b.useRef(null),j=c??I;return a.useLiquidPill(o,f,t,r.map(e=>e.id).join("\0")),u.jsxs("nav",{ref:o,"aria-label":y,"aria-labelledby":L,"data-testid":"liquid-nav",style:a.LIQUID_STRIP_STYLE,className:a.liquidStripClassName(l,s,p),children:[u.jsx("div",{ref:f,"aria-hidden":!0,"data-testid":"liquid-tabs-pill",className:a.liquidPillClassName(l),style:a.LIQUID_PILL_STYLE}),r.map(e=>{const n=e.id===t;return u.jsx(j,{href:e.href,to:e.href,"aria-current":n?"page":void 0,"data-tab-id":e.id,onClick:i=>{i.defaultPrevented||i.button!==0||i.metaKey||i.ctrlKey||i.shiftKey||i.altKey||n||d?.(e.id)},className:N.cn(a.liquidItemClassName({variant:l,fullWidth:s,size:q,isActive:n}),"rst:inline-flex rst:items-center rst:justify-center rst:no-underline"),children:typeof e.label=="function"?e.label(n):e.label},e.id)})]})}exports.LiquidNav=P;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ComponentType, MouseEvent, ReactNode } from 'react';
|
|
2
|
+
import { LiquidSize, LiquidVariant } from './liquid-pill';
|
|
3
|
+
import { TabItem } from './LiquidTabs';
|
|
4
|
+
export interface LiquidNavItem extends Omit<TabItem, "panelId"> {
|
|
5
|
+
href: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The props every link is rendered with.
|
|
9
|
+
*
|
|
10
|
+
* `href` and `to` carry the same value, so `next/link`, React Router's `Link`
|
|
11
|
+
* and a plain `<a>` all work without a wrapper. `next/link` passes props it
|
|
12
|
+
* does not know through to its `<a>`, so the rendered link carries a `to`
|
|
13
|
+
* attribute too; harmless, and a wrapper can drop it. Forward `data-tab-id`
|
|
14
|
+
* to the DOM: the pill finds its item by it.
|
|
15
|
+
*/
|
|
16
|
+
export interface LiquidNavLinkProps {
|
|
17
|
+
href: string;
|
|
18
|
+
to: string;
|
|
19
|
+
className: string;
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
"aria-current"?: "page";
|
|
22
|
+
onClick: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
23
|
+
"data-tab-id": string;
|
|
24
|
+
}
|
|
25
|
+
export interface LiquidNavProps {
|
|
26
|
+
items: LiquidNavItem[];
|
|
27
|
+
/** The `id` of the current item. Gets `aria-current="page"` and the pill. */
|
|
28
|
+
activeTab: string;
|
|
29
|
+
/**
|
|
30
|
+
* Called on a plain left click, before navigation. Optional, because the
|
|
31
|
+
* route usually decides `activeTab`. Modified and middle clicks open
|
|
32
|
+
* elsewhere and do not call it.
|
|
33
|
+
*/
|
|
34
|
+
onChange?: (id: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Renders each link. Defaults to a plain `<a>`, which is right for a static
|
|
37
|
+
* site and wrong inside a router, where it makes every hop a full page load.
|
|
38
|
+
*
|
|
39
|
+
* **Pass it from a client component.** Functions can not cross the React
|
|
40
|
+
* Server Component boundary, so handing `next/link` over from a server
|
|
41
|
+
* layout fails the render with "Functions cannot be passed directly to
|
|
42
|
+
* Client Components". Bind it in a small `"use client"` wrapper.
|
|
43
|
+
*/
|
|
44
|
+
linkComponent?: ComponentType<LiquidNavLinkProps>;
|
|
45
|
+
variant?: LiquidVariant;
|
|
46
|
+
fullWidth?: boolean;
|
|
47
|
+
size?: LiquidSize;
|
|
48
|
+
/** Name the navigation; a page with two `nav`s needs both named. */
|
|
49
|
+
"aria-label"?: string;
|
|
50
|
+
"aria-labelledby"?: string;
|
|
51
|
+
className?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `LiquidTabs`' look, for navigation between routes: a `nav` of links rather
|
|
55
|
+
* than a tablist of buttons.
|
|
56
|
+
*
|
|
57
|
+
* It is a separate component, not a mode, because the two are different
|
|
58
|
+
* things to assistive technology. Tabs switch panels on the same page and take
|
|
59
|
+
* one tab stop with arrow keys between them; links go somewhere, and each is
|
|
60
|
+
* its own tab stop, as in any nav.
|
|
61
|
+
*/
|
|
62
|
+
export declare function LiquidNav({ items, activeTab, onChange, linkComponent, variant, fullWidth, size, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, }: LiquidNavProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as q, jsx as u } from "react/jsx-runtime";
|
|
3
|
+
import { useRef as c } from "react";
|
|
4
|
+
import { useLiquidPill as I, LIQUID_STRIP_STYLE as N, LIQUID_PILL_STYLE as h, liquidPillClassName as P, liquidItemClassName as j, liquidStripClassName as x } from "./liquid-pill.js";
|
|
5
|
+
import { cn as C } from "../../../lib/utils.js";
|
|
6
|
+
function K({
|
|
7
|
+
href: e,
|
|
8
|
+
className: r,
|
|
9
|
+
children: n,
|
|
10
|
+
onClick: d,
|
|
11
|
+
"aria-current": l,
|
|
12
|
+
"data-tab-id": t
|
|
13
|
+
}) {
|
|
14
|
+
return /* @__PURE__ */ u("a", { href: e, className: r, onClick: d, "aria-current": l, "data-tab-id": t, children: n });
|
|
15
|
+
}
|
|
16
|
+
function D({
|
|
17
|
+
items: e,
|
|
18
|
+
activeTab: r,
|
|
19
|
+
onChange: n,
|
|
20
|
+
linkComponent: d,
|
|
21
|
+
variant: l = "pill",
|
|
22
|
+
fullWidth: t = !1,
|
|
23
|
+
size: b = "md",
|
|
24
|
+
"aria-label": p,
|
|
25
|
+
"aria-labelledby": m,
|
|
26
|
+
className: y
|
|
27
|
+
}) {
|
|
28
|
+
const o = c(null), f = c(null), L = d ?? K;
|
|
29
|
+
return I(o, f, r, e.map((a) => a.id).join("\0")), /* @__PURE__ */ q(
|
|
30
|
+
"nav",
|
|
31
|
+
{
|
|
32
|
+
ref: o,
|
|
33
|
+
"aria-label": p,
|
|
34
|
+
"aria-labelledby": m,
|
|
35
|
+
"data-testid": "liquid-nav",
|
|
36
|
+
style: N,
|
|
37
|
+
className: x(l, t, y),
|
|
38
|
+
children: [
|
|
39
|
+
/* @__PURE__ */ u(
|
|
40
|
+
"div",
|
|
41
|
+
{
|
|
42
|
+
ref: f,
|
|
43
|
+
"aria-hidden": !0,
|
|
44
|
+
"data-testid": "liquid-tabs-pill",
|
|
45
|
+
className: P(l),
|
|
46
|
+
style: h
|
|
47
|
+
}
|
|
48
|
+
),
|
|
49
|
+
e.map((a) => {
|
|
50
|
+
const s = a.id === r;
|
|
51
|
+
return /* @__PURE__ */ u(
|
|
52
|
+
L,
|
|
53
|
+
{
|
|
54
|
+
href: a.href,
|
|
55
|
+
to: a.href,
|
|
56
|
+
"aria-current": s ? "page" : void 0,
|
|
57
|
+
"data-tab-id": a.id,
|
|
58
|
+
onClick: (i) => {
|
|
59
|
+
i.defaultPrevented || i.button !== 0 || i.metaKey || i.ctrlKey || i.shiftKey || i.altKey || s || n?.(a.id);
|
|
60
|
+
},
|
|
61
|
+
className: C(
|
|
62
|
+
j({ variant: l, fullWidth: t, size: b, isActive: s }),
|
|
63
|
+
/* A button centers its label and draws no underline on its own;
|
|
64
|
+
a link does neither, and a host stylesheet may underline it. */
|
|
65
|
+
"rst:inline-flex rst:items-center rst:justify-center rst:no-underline"
|
|
66
|
+
),
|
|
67
|
+
children: typeof a.label == "function" ? a.label(s) : a.label
|
|
68
|
+
},
|
|
69
|
+
a.id
|
|
70
|
+
);
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
D as LiquidNav
|
|
78
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("react/jsx-runtime"),d=require("react"),s=require("./liquid-pill.cjs"),_=require("./liquid-tab-id.cjs");function E({tabs:l,activeTab:r,onChange:S,variant:n="pill",fullWidth:q=!1,size:R="md",activation:b="automatic",id:A,"aria-label":h,"aria-labelledby":j,className:k}){const u=d.useRef(null),y=d.useRef(null),w=d.useId(),I=A??w;s.useLiquidPill(u,y,r,l.map(e=>e.id).join("\0"));const p=e=>{e!==r&&S(e)},[a,L]=d.useState(null),x=l.some(e=>e.id===r),N=a!==null&&l.some(e=>e.id===a)?a:x?r:l[0]?.id,P=e=>{e.currentTarget.contains(e.relatedTarget)||L(null)},T=e=>{const i=u.current;if(!i)return;const c=Array.from(i.querySelectorAll('[role="tab"]')),o=c.indexOf(e.target);if(o===-1)return;const f=c.length-1,g=getComputedStyle(i).direction==="rtl",C=g?"ArrowLeft":"ArrowRight",D=g?"ArrowRight":"ArrowLeft";let t;if(e.key===C)t=o===f?0:o+1;else if(e.key===D)t=o===0?f:o-1;else if(e.key==="Home")t=0;else if(e.key==="End")t=f;else return;e.preventDefault(),c[t].focus(),b==="automatic"&&p(l[t].id)};return m.jsxs("div",{ref:u,id:I,role:"tablist","aria-label":h,"aria-labelledby":j,"data-testid":"liquid-tabs",style:s.LIQUID_STRIP_STYLE,className:s.liquidStripClassName(n,q,k),onKeyDown:T,onBlur:P,children:[m.jsx("div",{ref:y,"aria-hidden":!0,"data-testid":"liquid-tabs-pill",className:s.liquidPillClassName(n),style:s.LIQUID_PILL_STYLE}),l.map(e=>{const i=e.id===r;return m.jsx("button",{type:"button",role:"tab",id:_.liquidTabId(I,e.id),"aria-selected":i,"aria-controls":e.panelId,tabIndex:e.id===N?0:-1,"data-tab-id":e.id,"data-testid":`liquid-tab-${e.id}`,onClick:()=>p(e.id),onFocus:()=>L(e.id),className:s.liquidItemClassName({variant:n,fullWidth:q,size:R,isActive:i}),children:typeof e.label=="function"?e.label(i):e.label},e.id)})]})}exports.LiquidTabs=E;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { LiquidSize, LiquidVariant } from './liquid-pill';
|
|
3
|
+
export interface TabItem {
|
|
4
|
+
id: string;
|
|
5
|
+
/** Static label, or a render function that receives whether this tab is active. */
|
|
6
|
+
label: ReactNode | ((isActive: boolean) => ReactNode);
|
|
7
|
+
/**
|
|
8
|
+
* The `id` of the panel this tab controls, which sets `aria-controls`. Give
|
|
9
|
+
* every tab the same id when one panel swaps its content.
|
|
10
|
+
*/
|
|
11
|
+
panelId?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface LiquidTabsProps {
|
|
14
|
+
tabs: TabItem[];
|
|
15
|
+
/** The `id` of the currently active tab. */
|
|
16
|
+
activeTab: string;
|
|
17
|
+
/** Called with the new tab `id` when the user selects a different tab. */
|
|
18
|
+
onChange: (id: string) => void;
|
|
19
|
+
/**
|
|
20
|
+
* `"pill"` (default): a floating pill inside a padded container, `w-fit`
|
|
21
|
+
* unless `fullWidth` is set.
|
|
22
|
+
*
|
|
23
|
+
* `"filled"`: the active tab fills its whole cell, and the container is
|
|
24
|
+
* always `w-full`.
|
|
25
|
+
*
|
|
26
|
+
* Both squash the pill vertically (`scaleY`) while it stretches between tabs.
|
|
27
|
+
*/
|
|
28
|
+
variant?: LiquidVariant;
|
|
29
|
+
/** Stretch the `pill` variant container to full width with `flex-1` buttons. */
|
|
30
|
+
fullWidth?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* `"md"` (default) keeps the heights this component has always had. `"lg"`
|
|
33
|
+
* makes every tab at least 44px tall, the WCAG 2.5.5 target size.
|
|
34
|
+
*/
|
|
35
|
+
size?: LiquidSize;
|
|
36
|
+
/**
|
|
37
|
+
* `"automatic"` (default): arrowing onto a tab selects it.
|
|
38
|
+
* `"manual"`: arrows move focus only, and Enter or Space selects. Use it when
|
|
39
|
+
* selecting a tab is expensive, like swapping a whole form.
|
|
40
|
+
*/
|
|
41
|
+
activation?: "automatic" | "manual";
|
|
42
|
+
/**
|
|
43
|
+
* The tablist's `id`, and the base of every tab's id: see `liquidTabId`.
|
|
44
|
+
* Generated when omitted, which is fine unless a panel needs to point back at
|
|
45
|
+
* its tab with `aria-labelledby`.
|
|
46
|
+
*/
|
|
47
|
+
id?: string;
|
|
48
|
+
"aria-label"?: string;
|
|
49
|
+
"aria-labelledby"?: string;
|
|
50
|
+
/** Extra classes applied to the outer container. */
|
|
51
|
+
className?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A controlled tab strip with a liquid sliding indicator.
|
|
55
|
+
*
|
|
56
|
+
* Follows the WAI tabs pattern: only the selected tab is in the tab order,
|
|
57
|
+
* Left and Right move between tabs and wrap, Home and End go to the ends. If
|
|
58
|
+
* `activeTab` matches no tab, the first tab takes the tab stop and no pill
|
|
59
|
+
* shows.
|
|
60
|
+
*
|
|
61
|
+
* Until script has measured the pill, the active tab paints the pill's color
|
|
62
|
+
* itself, so server-rendered HTML shows the selection too.
|
|
63
|
+
*
|
|
64
|
+
* The pill follows `activeTab`, never the click, so a change the consumer
|
|
65
|
+
* vetoes, or one that waits on navigation, never leaves it on the wrong tab.
|
|
66
|
+
*
|
|
67
|
+
* For navigation between routes, use `LiquidNav`, which renders links.
|
|
68
|
+
*/
|
|
69
|
+
export declare function LiquidTabs({ tabs, activeTab, onChange, variant, fullWidth, size, activation, id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, }: LiquidTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as j, jsx as q } from "react/jsx-runtime";
|
|
3
|
+
import { useRef as L, useId as E, useState as T } from "react";
|
|
4
|
+
import { useLiquidPill as F, LIQUID_STRIP_STYLE as Q, LIQUID_PILL_STYLE as U, liquidPillClassName as Y, liquidItemClassName as B, liquidStripClassName as H } from "./liquid-pill.js";
|
|
5
|
+
import { liquidTabId as K } from "./liquid-tab-id.js";
|
|
6
|
+
function J({
|
|
7
|
+
tabs: l,
|
|
8
|
+
activeTab: r,
|
|
9
|
+
onChange: g,
|
|
10
|
+
variant: s = "pill",
|
|
11
|
+
fullWidth: u = !1,
|
|
12
|
+
size: A = "md",
|
|
13
|
+
activation: S = "automatic",
|
|
14
|
+
id: h,
|
|
15
|
+
"aria-label": k,
|
|
16
|
+
"aria-labelledby": w,
|
|
17
|
+
className: N
|
|
18
|
+
}) {
|
|
19
|
+
const d = L(null), f = L(null), R = E(), m = h ?? R;
|
|
20
|
+
F(d, f, r, l.map((e) => e.id).join("\0"));
|
|
21
|
+
const p = (e) => {
|
|
22
|
+
e !== r && g(e);
|
|
23
|
+
}, [n, y] = T(null), x = l.some((e) => e.id === r), C = n !== null && l.some((e) => e.id === n) ? n : x ? r : l[0]?.id, b = (e) => {
|
|
24
|
+
e.currentTarget.contains(e.relatedTarget) || y(null);
|
|
25
|
+
}, D = (e) => {
|
|
26
|
+
const i = d.current;
|
|
27
|
+
if (!i) return;
|
|
28
|
+
const a = Array.from(
|
|
29
|
+
i.querySelectorAll('[role="tab"]')
|
|
30
|
+
), o = a.indexOf(e.target);
|
|
31
|
+
if (o === -1) return;
|
|
32
|
+
const c = a.length - 1, I = getComputedStyle(i).direction === "rtl", P = I ? "ArrowLeft" : "ArrowRight", _ = I ? "ArrowRight" : "ArrowLeft";
|
|
33
|
+
let t;
|
|
34
|
+
if (e.key === P) t = o === c ? 0 : o + 1;
|
|
35
|
+
else if (e.key === _) t = o === 0 ? c : o - 1;
|
|
36
|
+
else if (e.key === "Home") t = 0;
|
|
37
|
+
else if (e.key === "End") t = c;
|
|
38
|
+
else return;
|
|
39
|
+
e.preventDefault(), a[t].focus(), S === "automatic" && p(l[t].id);
|
|
40
|
+
};
|
|
41
|
+
return /* @__PURE__ */ j(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
ref: d,
|
|
45
|
+
id: m,
|
|
46
|
+
role: "tablist",
|
|
47
|
+
"aria-label": k,
|
|
48
|
+
"aria-labelledby": w,
|
|
49
|
+
"data-testid": "liquid-tabs",
|
|
50
|
+
style: Q,
|
|
51
|
+
className: H(s, u, N),
|
|
52
|
+
onKeyDown: D,
|
|
53
|
+
onBlur: b,
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ q(
|
|
56
|
+
"div",
|
|
57
|
+
{
|
|
58
|
+
ref: f,
|
|
59
|
+
"aria-hidden": !0,
|
|
60
|
+
"data-testid": "liquid-tabs-pill",
|
|
61
|
+
className: Y(s),
|
|
62
|
+
style: U
|
|
63
|
+
}
|
|
64
|
+
),
|
|
65
|
+
l.map((e) => {
|
|
66
|
+
const i = e.id === r;
|
|
67
|
+
return /* @__PURE__ */ q(
|
|
68
|
+
"button",
|
|
69
|
+
{
|
|
70
|
+
type: "button",
|
|
71
|
+
role: "tab",
|
|
72
|
+
id: K(m, e.id),
|
|
73
|
+
"aria-selected": i,
|
|
74
|
+
"aria-controls": e.panelId,
|
|
75
|
+
tabIndex: e.id === C ? 0 : -1,
|
|
76
|
+
"data-tab-id": e.id,
|
|
77
|
+
"data-testid": `liquid-tab-${e.id}`,
|
|
78
|
+
onClick: () => p(e.id),
|
|
79
|
+
onFocus: () => y(e.id),
|
|
80
|
+
className: B({ variant: s, fullWidth: u, size: A, isActive: i }),
|
|
81
|
+
children: typeof e.label == "function" ? e.label(i) : e.label
|
|
82
|
+
},
|
|
83
|
+
e.id
|
|
84
|
+
);
|
|
85
|
+
})
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
J as LiquidTabs
|
|
92
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react"),d=require("../../../lib/utils.cjs"),L=require("../../../internal/motion.cjs");function v(s,i,n){return d.cn("rst:font-ui rst:relative rst:flex rst:border",s==="filled"?"rst:w-full rst:overflow-hidden rst:rounded-lg":d.cn("rst:gap-1 rst:rounded-xl rst:p-1",i?"rst:w-full":"rst:w-fit"),n)}const w={background:"var(--roster-lt-bg)",borderColor:"var(--roster-lt-border)"};function x(s){return d.cn("rst:absolute rst:rounded-lg",s==="filled"?"rst:top-0 rst:bottom-0":"rst:top-1 rst:bottom-1")}const q={opacity:0,left:0,width:0,background:"var(--roster-lt-pill)"};function C({variant:s,fullWidth:i,size:n,isActive:p}){return d.cn("rst:relative rst:z-10 rst:text-sm rst:font-medium rst:transition-colors rst:cursor-pointer","rst:focus-visible:outline-2 rst:focus-visible:-outline-offset-4 rst:focus-visible:outline-current",n==="lg"&&"rst:min-h-11",s==="filled"?"rst:flex-1 rst:py-2":d.cn("rst:rounded-lg rst:py-1.5",i?"rst:flex-1":"rst:px-4"),p?"rst:text-(--roster-lt-text-active) rst:bg-(--roster-lt-pill) rst:in-data-[pill-ready]:bg-transparent":"rst:text-(--roster-lt-text-inactive) rst:hover:text-(--roster-lt-text-hover)")}function S(s,i,n,p){const y=o.useRef(null),u=o.useRef(null),g=o.useRef(n),h=o.useRef(null),a=o.useCallback(e=>{const t=s.current;if(!t)return null;const l=Array.from(t.querySelectorAll("[data-tab-id]")).find(b=>b.dataset.tabId===e);if(!l)return null;const r=t.getBoundingClientRect(),c=l.getBoundingClientRect();return{left:c.left-r.left-t.clientLeft+t.scrollLeft,width:c.width}},[s]),f=o.useCallback((e,t,l)=>{const r=i.current;r&&(r.style.transition=t,r.style.left=`${e.left}px`,r.style.width=`${e.width}px`,r.style.transform=`scaleY(${l})`)},[i]),m=o.useCallback(()=>{const e=i.current,t=a(g.current);if(y.current=t,!!e){if(!t){e.style.opacity="0";return}f(t,"none",1),e.style.opacity="1",s.current?.setAttribute("data-pill-ready","")}},[f,s,a,i]);o.useLayoutEffect(()=>{g.current=n;const e=y.current,t=a(n),l=h.current!==null&&h.current!==n;if(h.current=n,u.current&&(clearTimeout(u.current),u.current=null),!e||!t||!l||L.prefersReducedMotion()){m();return}const r=Math.min(e.left,t.left),c=Math.max(e.left+e.width,t.left+t.width);f({left:r,width:c-r},"left 130ms ease-out, width 130ms ease-out, transform 130ms ease-out",.55),u.current=setTimeout(()=>{u.current=null;const b=a(n)??t;y.current=b,f(b,"left 160ms ease-in, width 160ms ease-in, transform 160ms ease-in",1)},130)},[n,f,a,m]),o.useEffect(()=>{const e=s.current;if(!e)return;const t=()=>{u.current||m()};let l;typeof ResizeObserver<"u"&&(l=new ResizeObserver(t),l.observe(e),e.querySelectorAll("[data-tab-id]").forEach(c=>l.observe(c)));let r=!0;return document.fonts?.ready.then(()=>{r&&t()}),()=>{r=!1,l?.disconnect()}},[s,p,m]),o.useEffect(()=>()=>{u.current&&clearTimeout(u.current)},[])}exports.LIQUID_PILL_STYLE=q;exports.LIQUID_STRIP_STYLE=w;exports.liquidItemClassName=C;exports.liquidPillClassName=x;exports.liquidStripClassName=v;exports.useLiquidPill=S;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* What `LiquidTabs` and `LiquidNav` share: the strip, the pill, and the motion.
|
|
4
|
+
*
|
|
5
|
+
* Internal. The two components differ in semantics only (a tablist of buttons,
|
|
6
|
+
* or a `nav` of links), so everything here is about how they look and move.
|
|
7
|
+
*/
|
|
8
|
+
export type LiquidVariant = "pill" | "filled";
|
|
9
|
+
export type LiquidSize = "md" | "lg";
|
|
10
|
+
export declare function liquidStripClassName(variant: LiquidVariant, fullWidth: boolean, className?: string): string;
|
|
11
|
+
export declare const LIQUID_STRIP_STYLE: {
|
|
12
|
+
readonly background: "var(--roster-lt-bg)";
|
|
13
|
+
readonly borderColor: "var(--roster-lt-border)";
|
|
14
|
+
};
|
|
15
|
+
export declare function liquidPillClassName(variant: LiquidVariant): string;
|
|
16
|
+
export declare const LIQUID_PILL_STYLE: {
|
|
17
|
+
readonly opacity: 0;
|
|
18
|
+
readonly left: 0;
|
|
19
|
+
readonly width: 0;
|
|
20
|
+
readonly background: "var(--roster-lt-pill)";
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* One tab or link.
|
|
24
|
+
*
|
|
25
|
+
* The focus indicator is an outline drawn inside the item, in its own text
|
|
26
|
+
* color. Inside, because the `filled` strip clips its overflow and an outer
|
|
27
|
+
* ring would lose its sides. The text color, because it is the one color
|
|
28
|
+
* guaranteed to read against whatever is behind the item: the pill under the
|
|
29
|
+
* active one, the strip under the rest. The library's `--roster-ring` would sit
|
|
30
|
+
* on a pill painted `primary-500` by default, which is the ring's own color.
|
|
31
|
+
* An outline rather than a box-shadow ring, because forced-colors mode drops
|
|
32
|
+
* box-shadows and keeps outlines.
|
|
33
|
+
*/
|
|
34
|
+
export declare function liquidItemClassName({ variant, fullWidth, size, isActive, }: {
|
|
35
|
+
variant: LiquidVariant;
|
|
36
|
+
fullWidth: boolean;
|
|
37
|
+
size: LiquidSize;
|
|
38
|
+
isActive: boolean;
|
|
39
|
+
}): string;
|
|
40
|
+
/**
|
|
41
|
+
* Drives the pill from `activeId`, and only from `activeId`.
|
|
42
|
+
*
|
|
43
|
+
* Earlier versions moved the pill on click, before the consumer had decided
|
|
44
|
+
* anything, so a vetoed change or a slow navigation left it on the wrong item.
|
|
45
|
+
* It now follows the controlled value, so it is wherever the consumer says the
|
|
46
|
+
* selection is.
|
|
47
|
+
*
|
|
48
|
+
* Positions are written straight to the DOM, so the animation itself never
|
|
49
|
+
* renders: only the change of `activeId` does. Two phases:
|
|
50
|
+
* 1. Stretch: the pill spans old and new item (130ms ease-out, `scaleY(0.55)`).
|
|
51
|
+
* 2. Contract: it settles on the new one (160ms ease-in).
|
|
52
|
+
* Under `prefers-reduced-motion` it moves without either.
|
|
53
|
+
*/
|
|
54
|
+
export declare function useLiquidPill(containerRef: RefObject<HTMLElement | null>, pillRef: RefObject<HTMLDivElement | null>, activeId: string,
|
|
55
|
+
/** Changes whenever the set of items does, so new items get observed. */
|
|
56
|
+
itemsKey: string): void;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRef as p, useCallback as g, useLayoutEffect as w, useEffect as v } from "react";
|
|
3
|
+
import { cn as a } from "../../../lib/utils.js";
|
|
4
|
+
import { prefersReducedMotion as L } from "../../../internal/motion.js";
|
|
5
|
+
function S(s, o, n) {
|
|
6
|
+
return a(
|
|
7
|
+
"rst:font-ui rst:relative rst:flex rst:border",
|
|
8
|
+
s === "filled" ? "rst:w-full rst:overflow-hidden rst:rounded-lg" : a("rst:gap-1 rst:rounded-xl rst:p-1", o ? "rst:w-full" : "rst:w-fit"),
|
|
9
|
+
n
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
const E = {
|
|
13
|
+
background: "var(--roster-lt-bg)",
|
|
14
|
+
borderColor: "var(--roster-lt-border)"
|
|
15
|
+
};
|
|
16
|
+
function A(s) {
|
|
17
|
+
return a(
|
|
18
|
+
"rst:absolute rst:rounded-lg",
|
|
19
|
+
s === "filled" ? "rst:top-0 rst:bottom-0" : "rst:top-1 rst:bottom-1"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const P = {
|
|
23
|
+
opacity: 0,
|
|
24
|
+
left: 0,
|
|
25
|
+
width: 0,
|
|
26
|
+
background: "var(--roster-lt-pill)"
|
|
27
|
+
};
|
|
28
|
+
function _({
|
|
29
|
+
variant: s,
|
|
30
|
+
fullWidth: o,
|
|
31
|
+
size: n,
|
|
32
|
+
isActive: b
|
|
33
|
+
}) {
|
|
34
|
+
return a(
|
|
35
|
+
"rst:relative rst:z-10 rst:text-sm rst:font-medium rst:transition-colors rst:cursor-pointer",
|
|
36
|
+
"rst:focus-visible:outline-2 rst:focus-visible:-outline-offset-4 rst:focus-visible:outline-current",
|
|
37
|
+
/* `lg` is the 44px target (WCAG 2.5.5). `md` keeps the heights every
|
|
38
|
+
existing consumer laid out against. */
|
|
39
|
+
n === "lg" && "rst:min-h-11",
|
|
40
|
+
s === "filled" ? "rst:flex-1 rst:py-2" : a("rst:rounded-lg rst:py-1.5", o ? "rst:flex-1" : "rst:px-4"),
|
|
41
|
+
/* Until the pill has been measured, which needs script, the active item
|
|
42
|
+
paints the pill's color itself. Without it, server-rendered HTML shows
|
|
43
|
+
the active label in the pill's ink on the strip's background, which by
|
|
44
|
+
default is white on white. */
|
|
45
|
+
b ? "rst:text-(--roster-lt-text-active) rst:bg-(--roster-lt-pill) rst:in-data-[pill-ready]:bg-transparent" : "rst:text-(--roster-lt-text-inactive) rst:hover:text-(--roster-lt-text-hover)"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function k(s, o, n, b) {
|
|
49
|
+
const y = p(null), i = p(null), x = p(n), h = p(null), c = g(
|
|
50
|
+
(r) => {
|
|
51
|
+
const t = s.current;
|
|
52
|
+
if (!t) return null;
|
|
53
|
+
const l = Array.from(
|
|
54
|
+
t.querySelectorAll("[data-tab-id]")
|
|
55
|
+
).find((m) => m.dataset.tabId === r);
|
|
56
|
+
if (!l) return null;
|
|
57
|
+
const e = t.getBoundingClientRect(), u = l.getBoundingClientRect();
|
|
58
|
+
return {
|
|
59
|
+
left: u.left - e.left - t.clientLeft + t.scrollLeft,
|
|
60
|
+
width: u.width
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
[s]
|
|
64
|
+
), f = g(
|
|
65
|
+
(r, t, l) => {
|
|
66
|
+
const e = o.current;
|
|
67
|
+
e && (e.style.transition = t, e.style.left = `${r.left}px`, e.style.width = `${r.width}px`, e.style.transform = `scaleY(${l})`);
|
|
68
|
+
},
|
|
69
|
+
[o]
|
|
70
|
+
), d = g(() => {
|
|
71
|
+
const r = o.current, t = c(x.current);
|
|
72
|
+
if (y.current = t, !!r) {
|
|
73
|
+
if (!t) {
|
|
74
|
+
r.style.opacity = "0";
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
f(t, "none", 1), r.style.opacity = "1", s.current?.setAttribute("data-pill-ready", "");
|
|
78
|
+
}
|
|
79
|
+
}, [f, s, c, o]);
|
|
80
|
+
w(() => {
|
|
81
|
+
x.current = n;
|
|
82
|
+
const r = y.current, t = c(n), l = h.current !== null && h.current !== n;
|
|
83
|
+
if (h.current = n, i.current && (clearTimeout(i.current), i.current = null), !r || !t || !l || L()) {
|
|
84
|
+
d();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const e = Math.min(r.left, t.left), u = Math.max(r.left + r.width, t.left + t.width);
|
|
88
|
+
f(
|
|
89
|
+
{ left: e, width: u - e },
|
|
90
|
+
"left 130ms ease-out, width 130ms ease-out, transform 130ms ease-out",
|
|
91
|
+
0.55
|
|
92
|
+
), i.current = setTimeout(() => {
|
|
93
|
+
i.current = null;
|
|
94
|
+
const m = c(n) ?? t;
|
|
95
|
+
y.current = m, f(
|
|
96
|
+
m,
|
|
97
|
+
"left 160ms ease-in, width 160ms ease-in, transform 160ms ease-in",
|
|
98
|
+
1
|
|
99
|
+
);
|
|
100
|
+
}, 130);
|
|
101
|
+
}, [n, f, c, d]), v(() => {
|
|
102
|
+
const r = s.current;
|
|
103
|
+
if (!r) return;
|
|
104
|
+
const t = () => {
|
|
105
|
+
i.current || d();
|
|
106
|
+
};
|
|
107
|
+
let l;
|
|
108
|
+
typeof ResizeObserver < "u" && (l = new ResizeObserver(t), l.observe(r), r.querySelectorAll("[data-tab-id]").forEach((u) => l.observe(u)));
|
|
109
|
+
let e = !0;
|
|
110
|
+
return document.fonts?.ready.then(() => {
|
|
111
|
+
e && t();
|
|
112
|
+
}), () => {
|
|
113
|
+
e = !1, l?.disconnect();
|
|
114
|
+
};
|
|
115
|
+
}, [s, b, d]), v(
|
|
116
|
+
() => () => {
|
|
117
|
+
i.current && clearTimeout(i.current);
|
|
118
|
+
},
|
|
119
|
+
[]
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
P as LIQUID_PILL_STYLE,
|
|
124
|
+
E as LIQUID_STRIP_STYLE,
|
|
125
|
+
_ as liquidItemClassName,
|
|
126
|
+
A as liquidPillClassName,
|
|
127
|
+
S as liquidStripClassName,
|
|
128
|
+
k as useLiquidPill
|
|
129
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function i(t,e){return`${t}-tab-${e}`}exports.liquidTabId=i;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `id` a `LiquidTabs` tab renders with, for a panel's `aria-labelledby`.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <LiquidTabs id="views" tabs={tabs} activeTab={view} onChange={setView} />
|
|
6
|
+
* <div role="tabpanel" id="views-panel" aria-labelledby={liquidTabId("views", view)}>
|
|
7
|
+
* ```
|
|
8
|
+
*
|
|
9
|
+
* The tab's own `id` is used verbatim, so keep it free of whitespace. A module
|
|
10
|
+
* of its own, with no React in it, so a server component can call it too.
|
|
11
|
+
*/
|
|
12
|
+
export declare function liquidTabId(listId: string, tabId: string): string;
|