@calcifer-design/ui 0.1.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/components/Button/Button.d.ts +12 -0
- package/dist/components/Button/Button.js +26 -0
- package/dist/components/Button/Button.module.js +6 -0
- package/dist/components/Button/Button_module.css +79 -0
- package/dist/components/Button/LinkButton.d.ts +19 -0
- package/dist/components/Button/LinkButton.js +24 -0
- package/dist/components/Card/Card.d.ts +15 -0
- package/dist/components/Card/Card.js +31 -0
- package/dist/components/Card/Card.module.js +9 -0
- package/dist/components/Card/Card_module.css +55 -0
- package/dist/components/DataTable/DataTable.d.ts +46 -0
- package/dist/components/DataTable/DataTable.js +98 -0
- package/dist/components/DataTable/DataTable.module.js +13 -0
- package/dist/components/DataTable/DataTable_module.css +149 -0
- package/dist/components/LiveRegion/LiveRegion.d.ts +5 -0
- package/dist/components/LiveRegion/LiveRegion.js +13 -0
- package/dist/components/LiveRegion/LiveRegion.module.js +5 -0
- package/dist/components/LiveRegion/LiveRegion_module.css +12 -0
- package/dist/components/NavMenu/NavMenu.d.ts +19 -0
- package/dist/components/NavMenu/NavMenu.js +86 -0
- package/dist/components/NavMenu/NavMenu.module.js +12 -0
- package/dist/components/NavMenu/NavMenu_module.css +85 -0
- package/dist/components/PageHeading/PageHeading.d.ts +16 -0
- package/dist/components/PageHeading/PageHeading.js +32 -0
- package/dist/components/PageHeading/PageHeading.module.js +7 -0
- package/dist/components/PageHeading/PageHeading_module.css +53 -0
- package/dist/components/SkipLink/SkipLink.d.ts +6 -0
- package/dist/components/SkipLink/SkipLink.js +11 -0
- package/dist/components/SkipLink/SkipLink.module.js +5 -0
- package/dist/components/SkipLink/SkipLink_module.css +24 -0
- package/dist/components/StatusDot/StatusDot.d.ts +17 -0
- package/dist/components/StatusDot/StatusDot.js +26 -0
- package/dist/components/StatusDot/StatusDot.module.js +9 -0
- package/dist/components/StatusDot/StatusDot_module.css +73 -0
- package/dist/components/Tabs/Tabs.d.ts +16 -0
- package/dist/components/Tabs/Tabs.js +35 -0
- package/dist/components/Tabs/Tabs.module.js +8 -0
- package/dist/components/Tabs/Tabs_module.css +53 -0
- package/dist/components/Tag/Tag.d.ts +6 -0
- package/dist/components/Tag/Tag.js +10 -0
- package/dist/components/Tag/Tag.module.js +5 -0
- package/dist/components/Tag/Tag_module.css +20 -0
- package/dist/hooks/useMediaQuery.d.ts +3 -0
- package/dist/hooks/useMediaQuery.js +22 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +12 -0
- package/dist/styles/base.css +58 -0
- package/package.json +69 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Menu } from "@base-ui/react/menu";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { minWidth, useMediaQuery } from "../../hooks/useMediaQuery.js";
|
|
5
|
+
import NavMenu_module from "./NavMenu.module.js";
|
|
6
|
+
function defaultRenderLink(item, { children, ...linkProps }) {
|
|
7
|
+
return /*#__PURE__*/ jsx("a", {
|
|
8
|
+
href: item.href,
|
|
9
|
+
...linkProps,
|
|
10
|
+
children: children
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function isCurrent(item, currentPath) {
|
|
14
|
+
if ('/' === item.href) return '/' === currentPath;
|
|
15
|
+
return currentPath === item.href || currentPath.startsWith(`${item.href}/`);
|
|
16
|
+
}
|
|
17
|
+
function linkPropsFor(item, currentPath) {
|
|
18
|
+
return {
|
|
19
|
+
className: NavMenu_module.link,
|
|
20
|
+
'aria-current': isCurrent(item, currentPath) ? 'page' : void 0,
|
|
21
|
+
children: item.label
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function NavMenu({ items, currentPath, label = 'Primary', renderLink = defaultRenderLink }) {
|
|
25
|
+
const isWide = useMediaQuery(minWidth('md'));
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
const [previousPath, setPreviousPath] = useState(currentPath);
|
|
28
|
+
if (currentPath !== previousPath) {
|
|
29
|
+
setPreviousPath(currentPath);
|
|
30
|
+
setOpen(false);
|
|
31
|
+
}
|
|
32
|
+
if (isWide) return /*#__PURE__*/ jsx("nav", {
|
|
33
|
+
className: NavMenu_module.root,
|
|
34
|
+
"aria-label": label,
|
|
35
|
+
children: /*#__PURE__*/ jsx("ul", {
|
|
36
|
+
className: NavMenu_module.list,
|
|
37
|
+
children: items.map((item)=>/*#__PURE__*/ jsx("li", {
|
|
38
|
+
children: renderLink(item, linkPropsFor(item, currentPath))
|
|
39
|
+
}, item.href))
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
return /*#__PURE__*/ jsx("nav", {
|
|
43
|
+
className: NavMenu_module.root,
|
|
44
|
+
"aria-label": label,
|
|
45
|
+
children: /*#__PURE__*/ jsxs(Menu.Root, {
|
|
46
|
+
open: open,
|
|
47
|
+
onOpenChange: setOpen,
|
|
48
|
+
modal: false,
|
|
49
|
+
children: [
|
|
50
|
+
/*#__PURE__*/ jsx(Menu.Trigger, {
|
|
51
|
+
className: NavMenu_module.trigger,
|
|
52
|
+
"aria-label": open ? 'Close menu' : 'Open menu',
|
|
53
|
+
children: /*#__PURE__*/ jsx("svg", {
|
|
54
|
+
className: NavMenu_module.triggerIcon,
|
|
55
|
+
viewBox: "0 0 18 18",
|
|
56
|
+
fill: "none",
|
|
57
|
+
stroke: "currentColor",
|
|
58
|
+
strokeWidth: "1.5",
|
|
59
|
+
strokeLinecap: "round",
|
|
60
|
+
"aria-hidden": "true",
|
|
61
|
+
focusable: "false",
|
|
62
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
63
|
+
d: open ? 'M4 4l10 10M14 4L4 14' : 'M2 5h14M2 9h14M2 13h14'
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
}),
|
|
67
|
+
/*#__PURE__*/ jsx(Menu.Portal, {
|
|
68
|
+
children: /*#__PURE__*/ jsx(Menu.Positioner, {
|
|
69
|
+
className: NavMenu_module.positioner,
|
|
70
|
+
side: "bottom",
|
|
71
|
+
align: "end",
|
|
72
|
+
sideOffset: 8,
|
|
73
|
+
children: /*#__PURE__*/ jsx(Menu.Popup, {
|
|
74
|
+
className: NavMenu_module.popup,
|
|
75
|
+
children: items.map((item)=>/*#__PURE__*/ jsx(Menu.Item, {
|
|
76
|
+
className: NavMenu_module.item,
|
|
77
|
+
render: renderLink(item, linkPropsFor(item, currentPath))
|
|
78
|
+
}, item.href))
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
]
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export { NavMenu };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "./NavMenu_module.css";
|
|
2
|
+
const NavMenu_module = {
|
|
3
|
+
root: "root-wsLiH",
|
|
4
|
+
list: "list-hzkjq",
|
|
5
|
+
link: "link-R0jaI",
|
|
6
|
+
trigger: "trigger-dj75u",
|
|
7
|
+
triggerIcon: "triggerIcon-gSo04",
|
|
8
|
+
positioner: "positioner-BPm1p",
|
|
9
|
+
popup: "popup-hUQFV",
|
|
10
|
+
item: "item-yXJoa"
|
|
11
|
+
};
|
|
12
|
+
export default NavMenu_module;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
.root-wsLiH {
|
|
2
|
+
align-items: center;
|
|
3
|
+
display: flex;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.list-hzkjq {
|
|
7
|
+
align-items: center;
|
|
8
|
+
gap: var(--space-5);
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
list-style: none;
|
|
12
|
+
display: flex;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.link-R0jaI {
|
|
16
|
+
min-height: var(--size-touch);
|
|
17
|
+
border-radius: var(--radius-sm);
|
|
18
|
+
color: var(--color-text-muted);
|
|
19
|
+
font-size: var(--text-sm);
|
|
20
|
+
white-space: nowrap;
|
|
21
|
+
transition-property: color;
|
|
22
|
+
transition-duration: var(--motion-duration-1);
|
|
23
|
+
transition-timing-function: var(--motion-ease);
|
|
24
|
+
align-items: center;
|
|
25
|
+
font-weight: 400;
|
|
26
|
+
text-decoration: none;
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.link-R0jaI[aria-current="page"] {
|
|
31
|
+
color: var(--color-text);
|
|
32
|
+
font-weight: 500;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media (hover: hover) {
|
|
36
|
+
.link-R0jaI:hover {
|
|
37
|
+
color: var(--color-text);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.trigger-dj75u {
|
|
42
|
+
width: var(--size-touch);
|
|
43
|
+
height: var(--size-touch);
|
|
44
|
+
border-radius: var(--radius-md);
|
|
45
|
+
color: var(--color-text);
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
background: none;
|
|
48
|
+
border: 0;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
align-items: center;
|
|
51
|
+
padding: 0;
|
|
52
|
+
display: inline-flex;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.triggerIcon-gSo04 {
|
|
56
|
+
width: 1.125rem;
|
|
57
|
+
height: 1.125rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.positioner-BPm1p {
|
|
61
|
+
z-index: 50;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.popup-hUQFV {
|
|
65
|
+
min-width: 12rem;
|
|
66
|
+
padding: var(--space-2);
|
|
67
|
+
border: 1px solid var(--color-border);
|
|
68
|
+
border-radius: var(--radius-lg);
|
|
69
|
+
background: var(--color-surface-raised);
|
|
70
|
+
box-shadow: var(--elevation-2);
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
display: flex;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.item-yXJoa {
|
|
76
|
+
padding-inline: var(--space-3);
|
|
77
|
+
border-radius: var(--radius-md);
|
|
78
|
+
font-size: var(--text-md);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.item-yXJoa[data-highlighted] {
|
|
82
|
+
background: var(--color-surface-2);
|
|
83
|
+
color: var(--color-text);
|
|
84
|
+
}
|
|
85
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export type PageHeadingSize = 'display' | 'page' | 'compact';
|
|
3
|
+
export interface PageHeadingProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
eyebrow?: ReactNode;
|
|
6
|
+
level?: 1 | 2;
|
|
7
|
+
/**
|
|
8
|
+
* The mock's three h1 sizes: `display` is the landing hero name, `page` (default) the
|
|
9
|
+
* Projects/About titles, `compact` the project and Under-the-hood titles. Ignored for
|
|
10
|
+
* `level={2}`, which always renders the section size.
|
|
11
|
+
*/
|
|
12
|
+
size?: PageHeadingSize;
|
|
13
|
+
/** Move focus to the heading when it mounts (used after client-side navigation). */
|
|
14
|
+
focusOnMount?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function PageHeading({ children, eyebrow, level, size, focusOnMount, }: PageHeadingProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import PageHeading_module from "./PageHeading.module.js";
|
|
4
|
+
function PageHeading({ children, eyebrow, level = 1, size = 'page', focusOnMount = false }) {
|
|
5
|
+
const headingRef = useRef(null);
|
|
6
|
+
const Heading = 1 === level ? 'h1' : 'h2';
|
|
7
|
+
useEffect(()=>{
|
|
8
|
+
if (focusOnMount) headingRef.current?.focus({
|
|
9
|
+
preventScroll: false
|
|
10
|
+
});
|
|
11
|
+
}, [
|
|
12
|
+
focusOnMount
|
|
13
|
+
]);
|
|
14
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
15
|
+
className: PageHeading_module.root,
|
|
16
|
+
children: [
|
|
17
|
+
eyebrow ? /*#__PURE__*/ jsx("p", {
|
|
18
|
+
className: PageHeading_module.eyebrow,
|
|
19
|
+
children: eyebrow
|
|
20
|
+
}) : null,
|
|
21
|
+
/*#__PURE__*/ jsx(Heading, {
|
|
22
|
+
ref: headingRef,
|
|
23
|
+
tabIndex: -1,
|
|
24
|
+
className: PageHeading_module.heading,
|
|
25
|
+
"data-level": level,
|
|
26
|
+
"data-size": size,
|
|
27
|
+
children: children
|
|
28
|
+
})
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export { PageHeading };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.root-G6df9 {
|
|
2
|
+
gap: var(--space-2);
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
display: flex;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.eyebrow-N5iEz {
|
|
8
|
+
font-family: var(--font-mono);
|
|
9
|
+
font-size: var(--text-sm);
|
|
10
|
+
color: var(--color-text-muted);
|
|
11
|
+
text-transform: uppercase;
|
|
12
|
+
letter-spacing: var(--tracking-wide);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.heading-C4QBU {
|
|
16
|
+
font-size: var(--text-h1);
|
|
17
|
+
letter-spacing: var(--tracking-h1);
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
line-height: var(--leading-display);
|
|
20
|
+
text-wrap: balance;
|
|
21
|
+
outline-offset: var(--space-2);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.heading-C4QBU[data-size="display"] {
|
|
25
|
+
max-width: 12ch;
|
|
26
|
+
font-size: var(--text-display);
|
|
27
|
+
letter-spacing: var(--tracking-display);
|
|
28
|
+
line-height: var(--leading-display);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@media (width >= 112.5rem) {
|
|
32
|
+
.heading-C4QBU[data-size="display"] {
|
|
33
|
+
font-size: var(--text-display-wide);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.heading-C4QBU[data-size="compact"] {
|
|
38
|
+
font-size: var(--text-h2);
|
|
39
|
+
letter-spacing: var(--tracking-heading);
|
|
40
|
+
line-height: var(--leading-tight);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.heading-C4QBU[data-level="2"] {
|
|
44
|
+
max-width: none;
|
|
45
|
+
font-size: var(--text-h2-feature);
|
|
46
|
+
letter-spacing: var(--tracking-heading);
|
|
47
|
+
line-height: var(--leading-tight);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.heading-C4QBU:focus:not(:focus-visible) {
|
|
51
|
+
outline: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import SkipLink_module from "./SkipLink.module.js";
|
|
3
|
+
function SkipLink({ targetId, children = 'Skip to content' }) {
|
|
4
|
+
return /*#__PURE__*/ jsx("a", {
|
|
5
|
+
className: SkipLink_module.root,
|
|
6
|
+
href: `#${targetId}`,
|
|
7
|
+
onClick: ()=>document.getElementById(targetId)?.focus(),
|
|
8
|
+
children: children
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export { SkipLink };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.root-arteG {
|
|
2
|
+
min-height: var(--size-touch);
|
|
3
|
+
top: var(--space-2);
|
|
4
|
+
left: var(--space-2);
|
|
5
|
+
z-index: 100;
|
|
6
|
+
padding: var(--space-2) var(--space-4);
|
|
7
|
+
border-radius: var(--radius-md);
|
|
8
|
+
background: var(--color-accent);
|
|
9
|
+
color: var(--color-accent-text);
|
|
10
|
+
transition-property: transform;
|
|
11
|
+
transition-duration: var(--motion-duration-1);
|
|
12
|
+
transition-timing-function: var(--motion-ease);
|
|
13
|
+
align-items: center;
|
|
14
|
+
font-weight: 600;
|
|
15
|
+
text-decoration: none;
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
position: absolute;
|
|
18
|
+
transform: translateY(-200%);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.root-arteG:focus-visible {
|
|
22
|
+
transform: translateY(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type RemoteStatus = 'registered' | 'loading' | 'loaded' | 'failed';
|
|
2
|
+
export interface StatusDotProps {
|
|
3
|
+
status: RemoteStatus;
|
|
4
|
+
/** The thing whose status this is, e.g. the remote name. */
|
|
5
|
+
label: string;
|
|
6
|
+
/**
|
|
7
|
+
* `mono` (default) sets the label in the monospace face, as in tables and badges; `text`
|
|
8
|
+
* sets it in the body face and text colour, as in the landing page's fleet list.
|
|
9
|
+
*/
|
|
10
|
+
labelStyle?: 'mono' | 'text';
|
|
11
|
+
/**
|
|
12
|
+
* `false` keeps the label for assistive tech but hides it visually — for a table whose
|
|
13
|
+
* first column already names the remote.
|
|
14
|
+
*/
|
|
15
|
+
showLabel?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function StatusDot({ status, label, labelStyle, showLabel, }: StatusDotProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import StatusDot_module from "./StatusDot.module.js";
|
|
3
|
+
function StatusDot({ status, label, labelStyle = 'mono', showLabel = true }) {
|
|
4
|
+
return /*#__PURE__*/ jsxs("span", {
|
|
5
|
+
className: StatusDot_module.root,
|
|
6
|
+
"data-status": status,
|
|
7
|
+
"data-label-style": labelStyle,
|
|
8
|
+
"data-testid": "status-dot",
|
|
9
|
+
children: [
|
|
10
|
+
/*#__PURE__*/ jsx("span", {
|
|
11
|
+
className: StatusDot_module.dot,
|
|
12
|
+
"aria-hidden": "true"
|
|
13
|
+
}),
|
|
14
|
+
/*#__PURE__*/ jsx("span", {
|
|
15
|
+
className: StatusDot_module.label,
|
|
16
|
+
"data-hidden": showLabel ? void 0 : '',
|
|
17
|
+
children: label
|
|
18
|
+
}),
|
|
19
|
+
/*#__PURE__*/ jsx("span", {
|
|
20
|
+
className: StatusDot_module.status,
|
|
21
|
+
children: status
|
|
22
|
+
})
|
|
23
|
+
]
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export { StatusDot };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
.root-tgMEi {
|
|
2
|
+
align-items: center;
|
|
3
|
+
gap: var(--space-2);
|
|
4
|
+
min-width: 0;
|
|
5
|
+
font-size: var(--text-sm);
|
|
6
|
+
display: inline-flex;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.dot-WDZDf {
|
|
10
|
+
border-radius: var(--radius-full);
|
|
11
|
+
background: var(--color-neutral);
|
|
12
|
+
flex: none;
|
|
13
|
+
width: .5rem;
|
|
14
|
+
height: .5rem;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.root-tgMEi[data-status="loading"] .dot-WDZDf {
|
|
18
|
+
background: var(--color-accent);
|
|
19
|
+
animation: pulse-RqPMg var(--motion-duration-4) var(--motion-ease) infinite alternate;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.root-tgMEi[data-status="loaded"] .dot-WDZDf {
|
|
23
|
+
background: var(--color-success);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.root-tgMEi[data-status="failed"] .dot-WDZDf {
|
|
27
|
+
background: var(--color-danger);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.label-m8svl {
|
|
31
|
+
text-overflow: ellipsis;
|
|
32
|
+
white-space: nowrap;
|
|
33
|
+
min-width: 0;
|
|
34
|
+
font-family: var(--font-mono);
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.label-m8svl[data-hidden] {
|
|
39
|
+
clip-path: inset(50%);
|
|
40
|
+
white-space: nowrap;
|
|
41
|
+
border: 0;
|
|
42
|
+
width: 1px;
|
|
43
|
+
height: 1px;
|
|
44
|
+
margin: -1px;
|
|
45
|
+
padding: 0;
|
|
46
|
+
position: absolute;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.root-tgMEi[data-label-style="text"] .label-m8svl {
|
|
51
|
+
font-family: var(--font-sans);
|
|
52
|
+
color: var(--color-text);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.status-t1WpV {
|
|
56
|
+
white-space: nowrap;
|
|
57
|
+
color: var(--color-text-muted);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.root-tgMEi[data-label-style="text"] .status-t1WpV {
|
|
61
|
+
color: var(--color-text-subtle);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@keyframes pulse-RqPMg {
|
|
65
|
+
from {
|
|
66
|
+
opacity: .35;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
to {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface TabItem {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
content: ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface TabsProps {
|
|
9
|
+
items: TabItem[];
|
|
10
|
+
/** Accessible name for the tab list. */
|
|
11
|
+
label: string;
|
|
12
|
+
value?: string;
|
|
13
|
+
defaultValue?: string;
|
|
14
|
+
onValueChange?: (value: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function Tabs({ items, label, value, defaultValue, onValueChange }: TabsProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Tabs } from "@base-ui/react/tabs";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import Tabs_module from "./Tabs.module.js";
|
|
5
|
+
function Tabs_Tabs({ items, label, value, defaultValue, onValueChange }) {
|
|
6
|
+
const [initialValue] = useState(()=>defaultValue ?? items[0]?.value);
|
|
7
|
+
return /*#__PURE__*/ jsxs(Tabs.Root, {
|
|
8
|
+
className: Tabs_module.root,
|
|
9
|
+
value: value,
|
|
10
|
+
defaultValue: initialValue,
|
|
11
|
+
onValueChange: (next)=>onValueChange?.(String(next)),
|
|
12
|
+
children: [
|
|
13
|
+
/*#__PURE__*/ jsxs(Tabs.List, {
|
|
14
|
+
className: Tabs_module.list,
|
|
15
|
+
"aria-label": label,
|
|
16
|
+
children: [
|
|
17
|
+
items.map((item)=>/*#__PURE__*/ jsx(Tabs.Tab, {
|
|
18
|
+
className: Tabs_module.tab,
|
|
19
|
+
value: item.value,
|
|
20
|
+
disabled: item.disabled,
|
|
21
|
+
children: item.label
|
|
22
|
+
}, item.value)),
|
|
23
|
+
/*#__PURE__*/ jsx(Tabs.Indicator, {
|
|
24
|
+
className: Tabs_module.indicator
|
|
25
|
+
})
|
|
26
|
+
]
|
|
27
|
+
}),
|
|
28
|
+
items.map((item)=>/*#__PURE__*/ jsx(Tabs.Panel, {
|
|
29
|
+
value: item.value,
|
|
30
|
+
children: item.content
|
|
31
|
+
}, item.value))
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export { Tabs_Tabs as Tabs };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.root-Hb4Mh {
|
|
2
|
+
gap: var(--space-4);
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
display: flex;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.list-ZEK7A {
|
|
8
|
+
gap: var(--space-1);
|
|
9
|
+
padding-block: var(--space-1);
|
|
10
|
+
border-bottom: 1px solid var(--color-border);
|
|
11
|
+
scrollbar-width: thin;
|
|
12
|
+
display: flex;
|
|
13
|
+
position: relative;
|
|
14
|
+
overflow-x: auto;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.tab-FRO0R {
|
|
18
|
+
min-height: var(--size-touch);
|
|
19
|
+
padding: var(--space-2) var(--space-3);
|
|
20
|
+
color: var(--color-text-muted);
|
|
21
|
+
font-size: var(--text-sm);
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
transition-property: color;
|
|
24
|
+
transition-duration: var(--motion-duration-1);
|
|
25
|
+
transition-timing-function: var(--motion-ease);
|
|
26
|
+
background: none;
|
|
27
|
+
border: 0;
|
|
28
|
+
flex: none;
|
|
29
|
+
font-weight: 600;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.tab-FRO0R[data-active] {
|
|
33
|
+
color: var(--color-text);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.tab-FRO0R[data-disabled] {
|
|
37
|
+
color: var(--color-text-muted);
|
|
38
|
+
opacity: .5;
|
|
39
|
+
cursor: not-allowed;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.indicator-a2Roe {
|
|
43
|
+
bottom: -1px;
|
|
44
|
+
left: var(--active-tab-left);
|
|
45
|
+
width: var(--active-tab-width);
|
|
46
|
+
background: var(--color-accent);
|
|
47
|
+
height: 2px;
|
|
48
|
+
transition-property: left, width;
|
|
49
|
+
transition-duration: var(--motion-duration-2);
|
|
50
|
+
transition-timing-function: var(--motion-ease);
|
|
51
|
+
position: absolute;
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import Tag_module from "./Tag.module.js";
|
|
3
|
+
function Tag({ children, tone = 'neutral' }) {
|
|
4
|
+
return /*#__PURE__*/ jsx("span", {
|
|
5
|
+
className: Tag_module.root,
|
|
6
|
+
"data-tone": tone,
|
|
7
|
+
children: children
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export { Tag };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.root-lzcr8 {
|
|
2
|
+
min-height: 1.5rem;
|
|
3
|
+
padding: 0 var(--space-2);
|
|
4
|
+
border: 1px solid var(--color-border);
|
|
5
|
+
border-radius: var(--radius-full);
|
|
6
|
+
font-size: var(--text-xs);
|
|
7
|
+
line-height: var(--leading-snug);
|
|
8
|
+
white-space: nowrap;
|
|
9
|
+
background: var(--color-surface-2);
|
|
10
|
+
color: var(--color-text-muted);
|
|
11
|
+
align-items: center;
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.root-lzcr8[data-tone="accent"] {
|
|
16
|
+
background: var(--color-accent-soft);
|
|
17
|
+
border-color: var(--color-border-accent);
|
|
18
|
+
color: var(--color-text);
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
2
|
+
import { breakpoint } from "@calcifer-design/tokens";
|
|
3
|
+
function minWidth(name) {
|
|
4
|
+
return `(min-width: ${breakpoint[name]})`;
|
|
5
|
+
}
|
|
6
|
+
function getServerSnapshot() {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
function useMediaQuery(query) {
|
|
10
|
+
const subscribe = useCallback((onChange)=>{
|
|
11
|
+
const mediaQueryList = window.matchMedia(query);
|
|
12
|
+
mediaQueryList.addEventListener('change', onChange);
|
|
13
|
+
return ()=>mediaQueryList.removeEventListener('change', onChange);
|
|
14
|
+
}, [
|
|
15
|
+
query
|
|
16
|
+
]);
|
|
17
|
+
const getSnapshot = useCallback(()=>window.matchMedia(query).matches, [
|
|
18
|
+
query
|
|
19
|
+
]);
|
|
20
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
21
|
+
}
|
|
22
|
+
export { minWidth, useMediaQuery };
|