@flanksource/clicky-ui 0.2.5 → 0.2.7
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/data/StatusBreakdown.cjs +80 -0
- package/dist/data/StatusBreakdown.cjs.map +1 -0
- package/dist/data/StatusBreakdown.d.ts +36 -0
- package/dist/data/StatusBreakdown.d.ts.map +1 -0
- package/dist/data/StatusBreakdown.js +80 -0
- package/dist/data/StatusBreakdown.js.map +1 -0
- package/dist/data/TimeseriesCoreBars.cjs +125 -0
- package/dist/data/TimeseriesCoreBars.cjs.map +1 -0
- package/dist/data/TimeseriesCoreBars.d.ts +61 -0
- package/dist/data/TimeseriesCoreBars.d.ts.map +1 -0
- package/dist/data/TimeseriesCoreBars.js +125 -0
- package/dist/data/TimeseriesCoreBars.js.map +1 -0
- package/dist/data/TimeseriesGauge.cjs +144 -0
- package/dist/data/TimeseriesGauge.cjs.map +1 -0
- package/dist/data/TimeseriesGauge.d.ts +56 -0
- package/dist/data/TimeseriesGauge.d.ts.map +1 -0
- package/dist/data/TimeseriesGauge.js +144 -0
- package/dist/data/TimeseriesGauge.js.map +1 -0
- package/dist/data.cjs +9 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +3 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +9 -0
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/Section.cjs +23 -14
- package/dist/layout/Section.cjs.map +1 -1
- package/dist/layout/Section.d.ts +14 -2
- package/dist/layout/Section.d.ts.map +1 -1
- package/dist/layout/Section.js +24 -15
- package/dist/layout/Section.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/layout/Section.cjs
CHANGED
|
@@ -16,6 +16,7 @@ const toneRing = {
|
|
|
16
16
|
function Section({
|
|
17
17
|
title,
|
|
18
18
|
summary,
|
|
19
|
+
collapsible = true,
|
|
19
20
|
defaultOpen = false,
|
|
20
21
|
open: openProp,
|
|
21
22
|
onToggle,
|
|
@@ -28,35 +29,43 @@ function Section({
|
|
|
28
29
|
}) {
|
|
29
30
|
const isControlled = openProp !== void 0;
|
|
30
31
|
const [innerOpen, setInnerOpen] = react.useState(defaultOpen);
|
|
31
|
-
const open = isControlled ? openProp : innerOpen;
|
|
32
|
+
const open = !collapsible || (isControlled ? openProp : innerOpen);
|
|
32
33
|
function toggle() {
|
|
33
34
|
const next = !open;
|
|
34
35
|
if (!isControlled) setInnerOpen(next);
|
|
35
36
|
onToggle == null ? void 0 : onToggle(next);
|
|
36
37
|
}
|
|
38
|
+
const leading = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
39
|
+
collapsible && /* @__PURE__ */ jsxRuntime.jsx(
|
|
40
|
+
Icon.Icon,
|
|
41
|
+
{
|
|
42
|
+
icon: open ? UiChevronDown.UiChevronDown : UiChevronRight.UiChevronRight,
|
|
43
|
+
className: "text-muted-foreground text-xs"
|
|
44
|
+
}
|
|
45
|
+
),
|
|
46
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { ...typeof icon === "string" ? { name: icon } : { icon }, className: "text-base" }),
|
|
47
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-sm flex-1 truncate", children: title })
|
|
48
|
+
] });
|
|
37
49
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("rounded-md border border-border bg-background", toneRing[tone], className), children: [
|
|
38
50
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
39
|
-
"
|
|
51
|
+
"div",
|
|
40
52
|
{
|
|
41
|
-
type: "button",
|
|
42
|
-
onClick: toggle,
|
|
43
|
-
"aria-expanded": open,
|
|
44
53
|
className: utils.cn(
|
|
45
54
|
"w-full flex items-center gap-2 px-density-3 py-density-2 text-left",
|
|
46
|
-
"hover:bg-accent/50 transition-colors",
|
|
47
55
|
headerClassName
|
|
48
56
|
),
|
|
49
57
|
children: [
|
|
50
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51
|
-
|
|
58
|
+
collapsible ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
59
|
+
"button",
|
|
52
60
|
{
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
type: "button",
|
|
62
|
+
onClick: toggle,
|
|
63
|
+
"aria-expanded": open,
|
|
64
|
+
className: "flex flex-1 min-w-0 items-center gap-2 text-left rounded-sm hover:bg-accent/50 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
65
|
+
children: leading
|
|
55
66
|
}
|
|
56
|
-
),
|
|
57
|
-
|
|
58
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-sm flex-1 truncate", children: title }),
|
|
59
|
-
summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: summary })
|
|
67
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 min-w-0 items-center gap-2", children: leading }),
|
|
68
|
+
summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground shrink-0", children: summary })
|
|
60
69
|
]
|
|
61
70
|
}
|
|
62
71
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Section.cjs","sources":["../../src/layout/Section.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type SectionProps = {\n /** Header title. */\n title: ReactNode;\n
|
|
1
|
+
{"version":3,"file":"Section.cjs","sources":["../../src/layout/Section.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type SectionProps = {\n /** Header title. */\n title: ReactNode;\n /**\n * Optional right-aligned header summary. The summary is a sibling of the\n * collapse toggle, never a descendant of it — so a summary containing\n * interactive content (filter tabs, links) is valid DOM and its clicks do\n * not toggle the section. When a summary is present, only the chevron + the\n * title toggle; the summary region does not.\n */\n summary?: ReactNode;\n /**\n * Whether the section can collapse. Defaults to true. When false the body is\n * always rendered, the header has no chevron and is not clickable — use for a\n * fixed panel that should never hide its content.\n */\n collapsible?: boolean;\n /** Initial open state for uncontrolled usage. */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when the section is toggled. */\n onToggle?: (open: boolean) => void;\n /** Optional leading icon. */\n icon?: string | StaticIconComponent;\n /** Semantic border accent. */\n tone?: \"default\" | \"danger\" | \"warning\" | \"success\" | \"info\";\n /** Classes applied to the section root. */\n className?: string;\n /** Classes applied to the clickable header. */\n headerClassName?: string;\n /** Classes applied to the content body. */\n bodyClassName?: string;\n /** Collapsible content. */\n children: ReactNode;\n};\n\nconst toneRing: Record<NonNullable<SectionProps[\"tone\"]>, string> = {\n default: \"\",\n danger: \"border-l-2 border-red-500\",\n warning: \"border-l-2 border-yellow-500\",\n success: \"border-l-2 border-green-500\",\n info: \"border-l-2 border-blue-500\",\n};\n\nexport function Section({\n title,\n summary,\n collapsible = true,\n defaultOpen = false,\n open: openProp,\n onToggle,\n icon,\n tone = \"default\",\n className,\n headerClassName,\n bodyClassName,\n children,\n}: SectionProps) {\n const isControlled = openProp !== undefined;\n const [innerOpen, setInnerOpen] = useState(defaultOpen);\n // A non-collapsible section is permanently open — there is no toggle to track.\n const open = !collapsible || (isControlled ? openProp : innerOpen);\n\n function toggle() {\n const next = !open;\n if (!isControlled) setInnerOpen(next);\n onToggle?.(next);\n }\n\n const leading = (\n <>\n {collapsible && (\n <Icon\n icon={open ? UiChevronDown : UiChevronRight}\n className=\"text-muted-foreground text-xs\"\n />\n )}\n {icon && (\n <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} className=\"text-base\" />\n )}\n <span className=\"font-medium text-sm flex-1 truncate\">{title}</span>\n </>\n );\n\n return (\n <div className={cn(\"rounded-md border border-border bg-background\", toneRing[tone], className)}>\n {/* The header is always a plain row so the summary (which may hold its own\n interactive content) is never nested inside the toggle button. Only the\n chevron + title region toggles; the summary is a sibling of it. */}\n <div\n className={cn(\n \"w-full flex items-center gap-2 px-density-3 py-density-2 text-left\",\n headerClassName,\n )}\n >\n {collapsible ? (\n <button\n type=\"button\"\n onClick={toggle}\n aria-expanded={open}\n className=\"flex flex-1 min-w-0 items-center gap-2 text-left rounded-sm hover:bg-accent/50 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n >\n {leading}\n </button>\n ) : (\n <div className=\"flex flex-1 min-w-0 items-center gap-2\">{leading}</div>\n )}\n {summary && <span className=\"text-xs text-muted-foreground shrink-0\">{summary}</span>}\n </div>\n {open && (\n <div className={cn(\"px-density-3 py-density-2 border-t border-border\", bodyClassName)}>\n {children}\n </div>\n )}\n </div>\n );\n}\n\nexport type DetailEmptyStateProps = {\n /** Optional icon shown above the label. */\n icon?: string | StaticIconComponent;\n /** Primary empty-state text. */\n label: ReactNode;\n /** Secondary empty-state text. */\n description?: ReactNode;\n /** Classes applied to the empty-state root. */\n className?: string;\n};\n\nexport function DetailEmptyState({ icon, label, description, className }: DetailEmptyStateProps) {\n return (\n <div className={cn(\"p-density-6 text-center text-muted-foreground\", className)}>\n {icon && (\n <Icon\n {...(typeof icon === \"string\" ? { name: icon } : { icon })}\n className=\"text-3xl mb-density-2\"\n />\n )}\n <p className=\"text-sm\">{label}</p>\n {description && <p className=\"text-xs mt-density-1 opacity-70\">{description}</p>}\n </div>\n );\n}\n"],"names":["useState","jsxs","Fragment","jsx","Icon","UiChevronDown","UiChevronRight","cn"],"mappings":";;;;;;;;AA0CA,MAAM,WAA8D;AAAA,EAClE,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AACR;AAEO,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,cAAc;AAAA,EACd,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiB;AACf,QAAM,eAAe,aAAa;AAClC,QAAM,CAAC,WAAW,YAAY,IAAIA,MAAAA,SAAS,WAAW;AAEtD,QAAM,OAAO,CAAC,gBAAgB,eAAe,WAAW;AAExD,WAAS,SAAS;AAChB,UAAM,OAAO,CAAC;AACd,QAAI,CAAC,aAAc,cAAa,IAAI;AACpC,yCAAW;AAAA,EACb;AAEA,QAAM,UACJC,2BAAAA,KAAAC,WAAAA,UAAA,EACG,UAAA;AAAA,IAAA,eACCC,2BAAAA;AAAAA,MAACC,KAAAA;AAAAA,MAAA;AAAA,QACC,MAAM,OAAOC,cAAAA,gBAAgBC,eAAAA;AAAAA,QAC7B,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAGb,QACCH,2BAAAA,IAACC,KAAAA,MAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,SAAS,EAAE,KAAA,GAAS,WAAU,YAAA,CAAY;AAAA,IAE1FD,2BAAAA,IAAC,QAAA,EAAK,WAAU,uCAAuC,UAAA,MAAA,CAAM;AAAA,EAAA,GAC/D;AAGF,SACEF,gCAAC,SAAI,WAAWM,MAAAA,GAAG,iDAAiD,SAAS,IAAI,GAAG,SAAS,GAI3F,UAAA;AAAA,IAAAN,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWM,MAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAGD,UAAA;AAAA,UAAA,cACCJ,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,iBAAe;AAAA,cACf,WAAU;AAAA,cAET,UAAA;AAAA,YAAA;AAAA,UAAA,IAGHA,2BAAAA,IAAC,OAAA,EAAI,WAAU,0CAA0C,UAAA,SAAQ;AAAA,UAElE,WAAWA,2BAAAA,IAAC,QAAA,EAAK,WAAU,0CAA0C,UAAA,QAAA,CAAQ;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAE/E,uCACE,OAAA,EAAI,WAAWI,MAAAA,GAAG,oDAAoD,aAAa,GACjF,SAAA,CACH;AAAA,EAAA,GAEJ;AAEJ;AAaO,SAAS,iBAAiB,EAAE,MAAM,OAAO,aAAa,aAAoC;AAC/F,yCACG,OAAA,EAAI,WAAWA,MAAAA,GAAG,iDAAiD,SAAS,GAC1E,UAAA;AAAA,IAAA,QACCJ,2BAAAA;AAAAA,MAACC,KAAAA;AAAAA,MAAA;AAAA,QACE,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA;AAAA,QACnD,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAGdD,2BAAAA,IAAC,KAAA,EAAE,WAAU,WAAW,UAAA,OAAM;AAAA,IAC7B,eAAeA,2BAAAA,IAAC,KAAA,EAAE,WAAU,mCAAmC,UAAA,YAAA,CAAY;AAAA,EAAA,GAC9E;AAEJ;;;"}
|
package/dist/layout/Section.d.ts
CHANGED
|
@@ -3,8 +3,20 @@ import { StaticIconComponent } from '../data/Icon';
|
|
|
3
3
|
export type SectionProps = {
|
|
4
4
|
/** Header title. */
|
|
5
5
|
title: ReactNode;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Optional right-aligned header summary. The summary is a sibling of the
|
|
8
|
+
* collapse toggle, never a descendant of it — so a summary containing
|
|
9
|
+
* interactive content (filter tabs, links) is valid DOM and its clicks do
|
|
10
|
+
* not toggle the section. When a summary is present, only the chevron + the
|
|
11
|
+
* title toggle; the summary region does not.
|
|
12
|
+
*/
|
|
7
13
|
summary?: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the section can collapse. Defaults to true. When false the body is
|
|
16
|
+
* always rendered, the header has no chevron and is not clickable — use for a
|
|
17
|
+
* fixed panel that should never hide its content.
|
|
18
|
+
*/
|
|
19
|
+
collapsible?: boolean;
|
|
8
20
|
/** Initial open state for uncontrolled usage. */
|
|
9
21
|
defaultOpen?: boolean;
|
|
10
22
|
/** Controlled open state. */
|
|
@@ -24,7 +36,7 @@ export type SectionProps = {
|
|
|
24
36
|
/** Collapsible content. */
|
|
25
37
|
children: ReactNode;
|
|
26
38
|
};
|
|
27
|
-
export declare function Section({ title, summary, defaultOpen, open: openProp, onToggle, icon, tone, className, headerClassName, bodyClassName, children, }: SectionProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export declare function Section({ title, summary, collapsible, defaultOpen, open: openProp, onToggle, icon, tone, className, headerClassName, bodyClassName, children, }: SectionProps): import("react/jsx-runtime").JSX.Element;
|
|
28
40
|
export type DetailEmptyStateProps = {
|
|
29
41
|
/** Optional icon shown above the label. */
|
|
30
42
|
icon?: string | StaticIconComponent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Section.d.ts","sourceRoot":"","sources":["../../src/layout/Section.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,YAAY,GAAG;IACzB,oBAAoB;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB
|
|
1
|
+
{"version":3,"file":"Section.d.ts","sourceRoot":"","sources":["../../src/layout/Section.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAQ,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAG9D,MAAM,MAAM,YAAY,GAAG;IACzB,oBAAoB;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,8BAA8B;IAC9B,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7D,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAUF,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,OAAO,EACP,WAAkB,EAClB,WAAmB,EACnB,IAAI,EAAE,QAAQ,EACd,QAAQ,EACR,IAAI,EACJ,IAAgB,EAChB,SAAS,EACT,eAAe,EACf,aAAa,EACb,QAAQ,GACT,EAAE,YAAY,2CA2Dd;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACpC,gCAAgC;IAChC,KAAK,EAAE,SAAS,CAAC;IACjB,kCAAkC;IAClC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CAa9F"}
|
package/dist/layout/Section.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { Icon } from "../data/Icon.js";
|
|
@@ -14,6 +14,7 @@ const toneRing = {
|
|
|
14
14
|
function Section({
|
|
15
15
|
title,
|
|
16
16
|
summary,
|
|
17
|
+
collapsible = true,
|
|
17
18
|
defaultOpen = false,
|
|
18
19
|
open: openProp,
|
|
19
20
|
onToggle,
|
|
@@ -26,35 +27,43 @@ function Section({
|
|
|
26
27
|
}) {
|
|
27
28
|
const isControlled = openProp !== void 0;
|
|
28
29
|
const [innerOpen, setInnerOpen] = useState(defaultOpen);
|
|
29
|
-
const open = isControlled ? openProp : innerOpen;
|
|
30
|
+
const open = !collapsible || (isControlled ? openProp : innerOpen);
|
|
30
31
|
function toggle() {
|
|
31
32
|
const next = !open;
|
|
32
33
|
if (!isControlled) setInnerOpen(next);
|
|
33
34
|
onToggle == null ? void 0 : onToggle(next);
|
|
34
35
|
}
|
|
36
|
+
const leading = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
37
|
+
collapsible && /* @__PURE__ */ jsx(
|
|
38
|
+
Icon,
|
|
39
|
+
{
|
|
40
|
+
icon: open ? UiChevronDown : UiChevronRight,
|
|
41
|
+
className: "text-muted-foreground text-xs"
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
icon && /* @__PURE__ */ jsx(Icon, { ...typeof icon === "string" ? { name: icon } : { icon }, className: "text-base" }),
|
|
45
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-sm flex-1 truncate", children: title })
|
|
46
|
+
] });
|
|
35
47
|
return /* @__PURE__ */ jsxs("div", { className: cn("rounded-md border border-border bg-background", toneRing[tone], className), children: [
|
|
36
48
|
/* @__PURE__ */ jsxs(
|
|
37
|
-
"
|
|
49
|
+
"div",
|
|
38
50
|
{
|
|
39
|
-
type: "button",
|
|
40
|
-
onClick: toggle,
|
|
41
|
-
"aria-expanded": open,
|
|
42
51
|
className: cn(
|
|
43
52
|
"w-full flex items-center gap-2 px-density-3 py-density-2 text-left",
|
|
44
|
-
"hover:bg-accent/50 transition-colors",
|
|
45
53
|
headerClassName
|
|
46
54
|
),
|
|
47
55
|
children: [
|
|
48
|
-
/* @__PURE__ */ jsx(
|
|
49
|
-
|
|
56
|
+
collapsible ? /* @__PURE__ */ jsx(
|
|
57
|
+
"button",
|
|
50
58
|
{
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
type: "button",
|
|
60
|
+
onClick: toggle,
|
|
61
|
+
"aria-expanded": open,
|
|
62
|
+
className: "flex flex-1 min-w-0 items-center gap-2 text-left rounded-sm hover:bg-accent/50 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
63
|
+
children: leading
|
|
53
64
|
}
|
|
54
|
-
),
|
|
55
|
-
|
|
56
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-sm flex-1 truncate", children: title }),
|
|
57
|
-
summary && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: summary })
|
|
65
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 min-w-0 items-center gap-2", children: leading }),
|
|
66
|
+
summary && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground shrink-0", children: summary })
|
|
58
67
|
]
|
|
59
68
|
}
|
|
60
69
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Section.js","sources":["../../src/layout/Section.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type SectionProps = {\n /** Header title. */\n title: ReactNode;\n
|
|
1
|
+
{"version":3,"file":"Section.js","sources":["../../src/layout/Section.tsx"],"sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"../data/Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type SectionProps = {\n /** Header title. */\n title: ReactNode;\n /**\n * Optional right-aligned header summary. The summary is a sibling of the\n * collapse toggle, never a descendant of it — so a summary containing\n * interactive content (filter tabs, links) is valid DOM and its clicks do\n * not toggle the section. When a summary is present, only the chevron + the\n * title toggle; the summary region does not.\n */\n summary?: ReactNode;\n /**\n * Whether the section can collapse. Defaults to true. When false the body is\n * always rendered, the header has no chevron and is not clickable — use for a\n * fixed panel that should never hide its content.\n */\n collapsible?: boolean;\n /** Initial open state for uncontrolled usage. */\n defaultOpen?: boolean;\n /** Controlled open state. */\n open?: boolean;\n /** Called when the section is toggled. */\n onToggle?: (open: boolean) => void;\n /** Optional leading icon. */\n icon?: string | StaticIconComponent;\n /** Semantic border accent. */\n tone?: \"default\" | \"danger\" | \"warning\" | \"success\" | \"info\";\n /** Classes applied to the section root. */\n className?: string;\n /** Classes applied to the clickable header. */\n headerClassName?: string;\n /** Classes applied to the content body. */\n bodyClassName?: string;\n /** Collapsible content. */\n children: ReactNode;\n};\n\nconst toneRing: Record<NonNullable<SectionProps[\"tone\"]>, string> = {\n default: \"\",\n danger: \"border-l-2 border-red-500\",\n warning: \"border-l-2 border-yellow-500\",\n success: \"border-l-2 border-green-500\",\n info: \"border-l-2 border-blue-500\",\n};\n\nexport function Section({\n title,\n summary,\n collapsible = true,\n defaultOpen = false,\n open: openProp,\n onToggle,\n icon,\n tone = \"default\",\n className,\n headerClassName,\n bodyClassName,\n children,\n}: SectionProps) {\n const isControlled = openProp !== undefined;\n const [innerOpen, setInnerOpen] = useState(defaultOpen);\n // A non-collapsible section is permanently open — there is no toggle to track.\n const open = !collapsible || (isControlled ? openProp : innerOpen);\n\n function toggle() {\n const next = !open;\n if (!isControlled) setInnerOpen(next);\n onToggle?.(next);\n }\n\n const leading = (\n <>\n {collapsible && (\n <Icon\n icon={open ? UiChevronDown : UiChevronRight}\n className=\"text-muted-foreground text-xs\"\n />\n )}\n {icon && (\n <Icon {...(typeof icon === \"string\" ? { name: icon } : { icon })} className=\"text-base\" />\n )}\n <span className=\"font-medium text-sm flex-1 truncate\">{title}</span>\n </>\n );\n\n return (\n <div className={cn(\"rounded-md border border-border bg-background\", toneRing[tone], className)}>\n {/* The header is always a plain row so the summary (which may hold its own\n interactive content) is never nested inside the toggle button. Only the\n chevron + title region toggles; the summary is a sibling of it. */}\n <div\n className={cn(\n \"w-full flex items-center gap-2 px-density-3 py-density-2 text-left\",\n headerClassName,\n )}\n >\n {collapsible ? (\n <button\n type=\"button\"\n onClick={toggle}\n aria-expanded={open}\n className=\"flex flex-1 min-w-0 items-center gap-2 text-left rounded-sm hover:bg-accent/50 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n >\n {leading}\n </button>\n ) : (\n <div className=\"flex flex-1 min-w-0 items-center gap-2\">{leading}</div>\n )}\n {summary && <span className=\"text-xs text-muted-foreground shrink-0\">{summary}</span>}\n </div>\n {open && (\n <div className={cn(\"px-density-3 py-density-2 border-t border-border\", bodyClassName)}>\n {children}\n </div>\n )}\n </div>\n );\n}\n\nexport type DetailEmptyStateProps = {\n /** Optional icon shown above the label. */\n icon?: string | StaticIconComponent;\n /** Primary empty-state text. */\n label: ReactNode;\n /** Secondary empty-state text. */\n description?: ReactNode;\n /** Classes applied to the empty-state root. */\n className?: string;\n};\n\nexport function DetailEmptyState({ icon, label, description, className }: DetailEmptyStateProps) {\n return (\n <div className={cn(\"p-density-6 text-center text-muted-foreground\", className)}>\n {icon && (\n <Icon\n {...(typeof icon === \"string\" ? { name: icon } : { icon })}\n className=\"text-3xl mb-density-2\"\n />\n )}\n <p className=\"text-sm\">{label}</p>\n {description && <p className=\"text-xs mt-density-1 opacity-70\">{description}</p>}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;AA0CA,MAAM,WAA8D;AAAA,EAClE,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AACR;AAEO,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,cAAc;AAAA,EACd,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiB;AACf,QAAM,eAAe,aAAa;AAClC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,WAAW;AAEtD,QAAM,OAAO,CAAC,gBAAgB,eAAe,WAAW;AAExD,WAAS,SAAS;AAChB,UAAM,OAAO,CAAC;AACd,QAAI,CAAC,aAAc,cAAa,IAAI;AACpC,yCAAW;AAAA,EACb;AAEA,QAAM,UACJ,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA,eACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAM,OAAO,gBAAgB;AAAA,QAC7B,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAGb,QACC,oBAAC,MAAA,EAAM,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,SAAS,EAAE,KAAA,GAAS,WAAU,YAAA,CAAY;AAAA,IAE1F,oBAAC,QAAA,EAAK,WAAU,uCAAuC,UAAA,MAAA,CAAM;AAAA,EAAA,GAC/D;AAGF,SACE,qBAAC,SAAI,WAAW,GAAG,iDAAiD,SAAS,IAAI,GAAG,SAAS,GAI3F,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAGD,UAAA;AAAA,UAAA,cACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,iBAAe;AAAA,cACf,WAAU;AAAA,cAET,UAAA;AAAA,YAAA;AAAA,UAAA,IAGH,oBAAC,OAAA,EAAI,WAAU,0CAA0C,UAAA,SAAQ;AAAA,UAElE,WAAW,oBAAC,QAAA,EAAK,WAAU,0CAA0C,UAAA,QAAA,CAAQ;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAE/E,4BACE,OAAA,EAAI,WAAW,GAAG,oDAAoD,aAAa,GACjF,SAAA,CACH;AAAA,EAAA,GAEJ;AAEJ;AAaO,SAAS,iBAAiB,EAAE,MAAM,OAAO,aAAa,aAAoC;AAC/F,8BACG,OAAA,EAAI,WAAW,GAAG,iDAAiD,SAAS,GAC1E,UAAA;AAAA,IAAA,QACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACE,GAAI,OAAO,SAAS,WAAW,EAAE,MAAM,KAAA,IAAS,EAAE,KAAA;AAAA,QACnD,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAGd,oBAAC,KAAA,EAAE,WAAU,WAAW,UAAA,OAAM;AAAA,IAC7B,eAAe,oBAAC,KAAA,EAAE,WAAU,mCAAmC,UAAA,YAAA,CAAY;AAAA,EAAA,GAC9E;AAEJ;"}
|