@conductor-by-89soone/react 0.3.1 → 0.4.1

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.
@@ -0,0 +1,215 @@
1
+ "use client";
2
+ "use client";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { forwardRef, useId, useMemo, useState } from "react";
5
+ import { Button } from "./action.js";
6
+ import { Table } from "./data.js";
7
+ import { cx } from "./cx.js";
8
+ function layoutRelationNodes(nodes) {
9
+ const ordered = [...nodes].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
10
+ const columns = Math.max(1, Math.ceil(Math.sqrt(ordered.length)));
11
+ return ordered.map((node, index) => ({ ...node, x: 130 + index % columns * 280, y: 90 + Math.floor(index / columns) * 150 }));
12
+ }
13
+ function edgeState(edge) {
14
+ return [edge.retracted && "\uD574\uC81C\uB428", edge.unresolved && "\uBBF8\uD574\uACB0", edge.ambiguous && "\uBAA8\uD638\uD568"].filter(Boolean).join(" \xB7 ") || "\uD655\uC778\uB428";
15
+ }
16
+ const RelationGraph = forwardRef(function RelationGraph2({
17
+ nodes,
18
+ edges,
19
+ label,
20
+ selection: controlledSelection,
21
+ onSelectionChange,
22
+ viewport: controlledViewport,
23
+ onViewportChange,
24
+ state = "ready",
25
+ stateMessage,
26
+ onRetry,
27
+ className,
28
+ ...props
29
+ }, ref) {
30
+ const uid = useId().replace(/:/g, "");
31
+ const [localSelection, setSelection] = useState(null);
32
+ const [localViewport, setViewport] = useState({ x: 0, y: 0, zoom: 1 });
33
+ const [query, setQuery] = useState("");
34
+ const [type, setType] = useState("");
35
+ const selection = controlledSelection === void 0 ? localSelection : controlledSelection;
36
+ const viewport = controlledViewport ?? localViewport;
37
+ const positioned = useMemo(() => layoutRelationNodes(nodes), [nodes]);
38
+ const byId = useMemo(() => new Map(positioned.map((node) => [node.id, node])), [positioned]);
39
+ const types = useMemo(() => [...new Set(edges.map((edge) => edge.type))].sort(), [edges]);
40
+ const visibleEdges = useMemo(() => [...edges].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0).filter((edge) => !type || edge.type === type), [edges, type]);
41
+ const found = positioned.filter((node) => `${node.label} ${node.id}`.toLocaleLowerCase().includes(query.toLocaleLowerCase()));
42
+ const neighbors = useMemo(() => {
43
+ const result = /* @__PURE__ */ new Set();
44
+ if (selection?.kind === "node") {
45
+ result.add(selection.id);
46
+ for (const edge of visibleEdges) if (edge.source === selection.id || edge.target === selection.id) {
47
+ result.add(edge.source);
48
+ result.add(edge.target);
49
+ }
50
+ }
51
+ return result;
52
+ }, [selection, visibleEdges]);
53
+ const width = Math.max(560, ...positioned.map((node) => node.x + 150));
54
+ const height = Math.max(300, ...positioned.map((node) => node.y + 90));
55
+ const zoom = Math.min(8, Math.max(0.5, Number.isFinite(viewport.zoom) ? viewport.zoom : 1));
56
+ const changeSelection = (next) => {
57
+ if (controlledSelection === void 0) setSelection(next);
58
+ onSelectionChange?.(next);
59
+ };
60
+ const changeViewport = (next) => {
61
+ if (!controlledViewport) setViewport(next);
62
+ onViewportChange?.(next);
63
+ };
64
+ const pan = (x, y) => changeViewport({ x: viewport.x + x * width / zoom / 4, y: viewport.y + y * height / zoom / 4, zoom });
65
+ const selectedNode = selection?.kind === "node" ? byId.get(selection.id) : void 0;
66
+ const selectedEdge = selection?.kind === "edge" ? edges.find((edge) => edge.id === selection.id) : void 0;
67
+ const endpointLabel = (id) => byId.get(id)?.label ?? "\uB300\uC0C1 \uC815\uBCF4 \uC5C6\uC74C";
68
+ const selectedHidden = selectedEdge && !visibleEdges.some((edge) => edge.id === selectedEdge.id);
69
+ const blocked = state === "loading" || state === "error" || state === "empty";
70
+ return /* @__PURE__ */ jsxs("div", { ...props, ref, className: cx("cdt-relation", className), "aria-label": props["aria-label"] ?? label, "aria-busy": props["aria-busy"] ?? (state === "loading" || void 0), children: [
71
+ /* @__PURE__ */ jsxs("div", { className: "cdt-relation__toolbar", children: [
72
+ /* @__PURE__ */ jsxs("label", { htmlFor: `${uid}-find`, children: [
73
+ "\uB178\uB4DC \uCC3E\uAE30",
74
+ /* @__PURE__ */ jsx("input", { id: `${uid}-find`, className: "cdt-input", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\uC774\uB984 \uB610\uB294 \uC548\uC815 ID" })
75
+ ] }),
76
+ /* @__PURE__ */ jsxs("label", { htmlFor: `${uid}-type`, children: [
77
+ "\uAD00\uACC4 \uC720\uD615",
78
+ /* @__PURE__ */ jsxs("select", { id: `${uid}-type`, className: "cdt-input", value: type, onChange: (event) => setType(event.target.value), children: [
79
+ /* @__PURE__ */ jsx("option", { value: "", children: "\uBAA8\uB4E0 \uC720\uD615" }),
80
+ types.map((value) => /* @__PURE__ */ jsx("option", { value, children: value }, value))
81
+ ] })
82
+ ] }),
83
+ /* @__PURE__ */ jsx("a", { href: `#${uid}-list`, onClick: (event) => {
84
+ event.preventDefault();
85
+ const list = event.currentTarget.ownerDocument.getElementById(`${uid}-list`);
86
+ list?.focus({ preventScroll: true });
87
+ list?.scrollIntoView({ block: "start" });
88
+ }, children: "\uB3D9\uC77C \uC815\uBCF4 \uBAA9\uB85D\uC73C\uB85C \uC774\uB3D9" })
89
+ ] }),
90
+ /* @__PURE__ */ jsx("p", { className: "cdt-relation__legend", children: "\uD654\uC0B4\uD45C: \uCD9C\uBC1C \u2192 \uAD00\uACC4 \u2192 \uB3C4\uCC29. \uC2E4\uC120: \uD655\uC778\uB428 \xB7 \uC810\uC120: \uBBF8\uD574\uACB0/\uBAA8\uD638\uD568 \xB7 \uD30C\uC120: \uD574\uC81C\uB428. \uC704\uCE58\uB294 ID \uC21C\uC11C\uC774\uBA70 \uC2DC\uAC04\xB7\uBCD1\uD569\xB7\uC758\uC874 \uC21C\uC11C\uB97C \uB73B\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." }),
91
+ /* @__PURE__ */ jsx("p", { role: state === "error" ? "alert" : "status", children: stateMessage ?? { ready: `${nodes.length}\uAC1C \uB178\uB4DC \xB7 ${visibleEdges.length}\uAC1C \uAD00\uACC4`, loading: "\uAD00\uACC4 \uBD88\uB7EC\uC624\uB294 \uC911", empty: "\uD655\uC778\uB41C \uAD00\uACC4 \uC5C6\uC74C", error: "\uAD00\uACC4\uB97C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4", partial: "\uBD80\uBD84 \uACB0\uACFC: \uD45C\uC2DC\uB418\uC9C0 \uC54A\uC740 \uAD00\uACC4\uAC00 \uC788\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4" }[state] }),
92
+ state === "error" && onRetry && /* @__PURE__ */ jsx(Button, { type: "button", onClick: onRetry, children: "\uAD00\uACC4 \uB2E4\uC2DC \uC2DC\uB3C4" }),
93
+ !blocked && /* @__PURE__ */ jsxs(Fragment, { children: [
94
+ /* @__PURE__ */ jsxs("div", { className: "cdt-relation__toolbar", "aria-label": "\uADF8\uB798\uD504 \uBCF4\uAE30 \uC870\uC808", children: [
95
+ /* @__PURE__ */ jsx(Button, { type: "button", disabled: zoom >= 8, onClick: () => changeViewport({ ...viewport, zoom: Math.min(8, zoom * 1.5) }), children: "\uD655\uB300" }),
96
+ /* @__PURE__ */ jsx(Button, { type: "button", disabled: zoom <= 0.5, onClick: () => changeViewport({ ...viewport, zoom: Math.max(0.5, zoom / 1.5) }), children: "\uCD95\uC18C" }),
97
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: () => changeViewport({ x: 0, y: 0, zoom: 1 }), children: "\uC804\uCCB4 \uBCF4\uAE30" }),
98
+ /* @__PURE__ */ jsx(Button, { type: "button", "aria-label": "\uADF8\uB798\uD504 \uC67C\uCABD\uC73C\uB85C \uC774\uB3D9", onClick: () => pan(-1, 0), children: "\u2190" }),
99
+ /* @__PURE__ */ jsx(Button, { type: "button", "aria-label": "\uADF8\uB798\uD504 \uC624\uB978\uCABD\uC73C\uB85C \uC774\uB3D9", onClick: () => pan(1, 0), children: "\u2192" }),
100
+ /* @__PURE__ */ jsx(Button, { type: "button", "aria-label": "\uADF8\uB798\uD504 \uC704\uB85C \uC774\uB3D9", onClick: () => pan(0, -1), children: "\u2191" }),
101
+ /* @__PURE__ */ jsx(Button, { type: "button", "aria-label": "\uADF8\uB798\uD504 \uC544\uB798\uB85C \uC774\uB3D9", onClick: () => pan(0, 1), children: "\u2193" }),
102
+ /* @__PURE__ */ jsxs("output", { "aria-label": "\uD655\uB300 \uBC30\uC728", children: [
103
+ Math.round(zoom * 100),
104
+ "%"
105
+ ] })
106
+ ] }),
107
+ /* @__PURE__ */ jsxs("svg", { className: "cdt-relation__canvas", "aria-hidden": "true", focusable: "false", viewBox: `${viewport.x} ${viewport.y} ${width / zoom} ${height / zoom}`, children: [
108
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("marker", { id: `${uid}-arrow`, viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse", children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", className: "cdt-relation__arrow" }) }) }),
109
+ visibleEdges.map((edge, index) => {
110
+ const source = byId.get(edge.source);
111
+ const target = byId.get(edge.target);
112
+ if (!source || !target) return null;
113
+ const dx = target.x - source.x;
114
+ const dy = target.y - source.y;
115
+ const distance = Math.hypot(dx, dy) || 1;
116
+ const offset = dy === 0 ? -Math.sign(dx) * (100 + index % 3 * 20) : 28 + index % 3 * 12;
117
+ const inset = Math.min(Math.abs(dx) > 0 ? 118 / Math.abs(dx) : Infinity, Math.abs(dy) > 0 ? 34 / Math.abs(dy) : Infinity);
118
+ const path = source.id === target.id ? `M ${source.x} ${source.y - 26} C ${source.x - 90} ${source.y - 105}, ${source.x + 90} ${source.y - 105}, ${source.x + 30} ${source.y - 26}` : `M ${source.x} ${source.y} Q ${(source.x + target.x) / 2 - dy / distance * offset} ${(source.y + target.y) / 2 + dx / distance * offset} ${target.x - dx * inset} ${target.y - dy * inset}`;
119
+ return /* @__PURE__ */ jsxs("g", { "data-edge-id": edge.id, "data-selected": selectedEdge?.id === edge.id || void 0, "data-dimmed": neighbors.size > 0 && !(neighbors.has(edge.source) && neighbors.has(edge.target)) || void 0, className: "cdt-relation__edge", onClick: () => changeSelection({ kind: "edge", id: edge.id }), children: [
120
+ /* @__PURE__ */ jsx("path", { d: path, className: "cdt-relation__edge-hit" }),
121
+ /* @__PURE__ */ jsx("path", { d: path, markerEnd: `url(#${uid}-arrow)`, "data-state": edge.retracted ? "retracted" : edge.unresolved || edge.ambiguous ? "uncertain" : "confirmed" }),
122
+ (nodes.length <= 50 || selectedEdge?.id === edge.id) && /* @__PURE__ */ jsx("text", { x: (source.x + target.x) / 2 - dy / distance * offset / 2, y: (source.y + target.y) / 2 + dx / distance * offset / 2 - 8, textAnchor: "middle", className: "cdt-relation__edge-label", children: edge.label }),
123
+ /* @__PURE__ */ jsx("title", { children: `${endpointLabel(edge.source)} \u2192 ${edge.label} \u2192 ${endpointLabel(edge.target)} \xB7 ${edgeState(edge)}` })
124
+ ] }, edge.id);
125
+ }),
126
+ positioned.map((node) => /* @__PURE__ */ jsxs("g", { "data-node-id": node.id, className: "cdt-relation__node", "data-selected": selectedNode?.id === node.id || void 0, "data-dimmed": neighbors.size > 0 && !neighbors.has(node.id) || void 0, onClick: () => changeSelection({ kind: "node", id: node.id }), children: [
127
+ /* @__PURE__ */ jsx("rect", { x: node.x - 110, y: node.y - 26, width: 220, height: 52, rx: 8 }),
128
+ /* @__PURE__ */ jsx("text", { x: node.x, y: node.y + 5, textAnchor: "middle", children: node.label.length > 24 ? `${node.label.slice(0, 23)}\u2026` : node.label }),
129
+ /* @__PURE__ */ jsx("title", { children: node.label })
130
+ ] }, node.id))
131
+ ] }),
132
+ /* @__PURE__ */ jsxs("section", { "aria-label": "\uC120\uD0DD\uD55C \uAD00\uACC4 \uC0C1\uC138", className: "cdt-relation__detail", "aria-live": "polite", children: [
133
+ selectedNode ? /* @__PURE__ */ jsxs(Fragment, { children: [
134
+ /* @__PURE__ */ jsx("strong", { children: selectedNode.label }),
135
+ /* @__PURE__ */ jsx("p", { children: selectedNode.description ?? "\uCD94\uAC00 \uC124\uBA85 \uC5C6\uC74C" }),
136
+ /* @__PURE__ */ jsx("p", { children: selectedNode.unavailable ? "\uB300\uC0C1 \uB0B4\uC6A9\uC744 \uD45C\uC2DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." : "\uC5F0\uACB0\uB41C \uC774\uC6C3\uC774 \uAC15\uC870\uB418\uC5C8\uC2B5\uB2C8\uB2E4." })
137
+ ] }) : selectedEdge ? /* @__PURE__ */ jsxs(Fragment, { children: [
138
+ /* @__PURE__ */ jsxs("strong", { children: [
139
+ endpointLabel(selectedEdge.source),
140
+ " \u2192 ",
141
+ selectedEdge.label,
142
+ " \u2192 ",
143
+ endpointLabel(selectedEdge.target)
144
+ ] }),
145
+ /* @__PURE__ */ jsxs("p", { children: [
146
+ "\uC720\uD615: ",
147
+ selectedEdge.type,
148
+ " \xB7 \uC0C1\uD0DC: ",
149
+ edgeState(selectedEdge),
150
+ " \xB7 \uC2E0\uB8B0\uB3C4: ",
151
+ selectedEdge.confidence ?? "\uC54C \uC218 \uC5C6\uC74C"
152
+ ] }),
153
+ /* @__PURE__ */ jsxs("p", { children: [
154
+ "\uADFC\uAC70: ",
155
+ selectedEdge.evidence || "\uC81C\uACF5\uB418\uC9C0 \uC54A\uC74C"
156
+ ] }),
157
+ selectedHidden && /* @__PURE__ */ jsx("p", { children: "\uC120\uD0DD\uD55C \uAD00\uACC4\uB294 \uD604\uC7AC \uC720\uD615 \uD544\uD130\uC5D0\uC11C \uC228\uACA8\uC838 \uC788\uC2B5\uB2C8\uB2E4." })
158
+ ] }) : /* @__PURE__ */ jsx("p", { children: selection ? "\uC120\uD0DD\uD55C \uD56D\uBAA9\uC774 \uD604\uC7AC \uB370\uC774\uD130\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4." : "\uB178\uB4DC \uB610\uB294 \uAD00\uACC4\uB97C \uC120\uD0DD\uD558\uBA74 \uBC29\uD5A5, \uC0C1\uD0DC, \uADFC\uAC70\uB97C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4." }),
159
+ selection && /* @__PURE__ */ jsx(Button, { type: "button", onClick: () => changeSelection(null), children: "\uC120\uD0DD \uD574\uC81C" })
160
+ ] }),
161
+ /* @__PURE__ */ jsxs("section", { id: `${uid}-list`, tabIndex: -1, "aria-label": "\uAD00\uACC4 \uB300\uCCB4 \uBAA9\uB85D", children: [
162
+ /* @__PURE__ */ jsxs("details", { open: query.length > 0 || nodes.length <= 12, children: [
163
+ /* @__PURE__ */ jsxs("summary", { children: [
164
+ "\uB178\uB4DC \uBAA9\uB85D \xB7 ",
165
+ found.length,
166
+ "\uAC1C (\uACE0\uB9BD\uB41C \uB178\uB4DC \uD3EC\uD568)"
167
+ ] }),
168
+ /* @__PURE__ */ jsx("ul", { className: "cdt-relation__nodes", children: found.map((node) => /* @__PURE__ */ jsxs("li", { children: [
169
+ /* @__PURE__ */ jsx(Button, { type: "button", "aria-pressed": selectedNode?.id === node.id, onClick: () => {
170
+ changeSelection({ kind: "node", id: node.id });
171
+ if (query) changeViewport({ x: node.x - width / zoom / 2, y: node.y - height / zoom / 2, zoom });
172
+ }, children: node.label }),
173
+ node.unavailable && /* @__PURE__ */ jsx("span", { children: " \xB7 \uB0B4\uC6A9 \uD45C\uC2DC \uBD88\uAC00" })
174
+ ] }, node.id)) }),
175
+ found.length === 0 && /* @__PURE__ */ jsx("p", { children: "\uC77C\uCE58\uD558\uB294 \uB178\uB4DC \uC5C6\uC74C" })
176
+ ] }),
177
+ /* @__PURE__ */ jsxs(Table, { caption: `${label} \u2014 \uBC29\uD5A5\uACFC \uADFC\uAC70 \uBAA9\uB85D`, children: [
178
+ /* @__PURE__ */ jsx(Table.Head, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
179
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "\uCD9C\uBC1C \u2192 \uAD00\uACC4 \u2192 \uB3C4\uCC29" }),
180
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "\uC720\uD615 \xB7 \uC0C1\uD0DC" }),
181
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "\uC2E0\uB8B0\uB3C4 \xB7 \uADFC\uAC70" }),
182
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "\uC120\uD0DD" })
183
+ ] }) }),
184
+ /* @__PURE__ */ jsx(Table.Body, { children: visibleEdges.map((edge) => /* @__PURE__ */ jsxs(Table.Row, { "data-selected": selectedEdge?.id === edge.id || void 0, children: [
185
+ /* @__PURE__ */ jsxs(Table.Cell, { children: [
186
+ endpointLabel(edge.source),
187
+ " \u2192 ",
188
+ edge.label,
189
+ " \u2192 ",
190
+ endpointLabel(edge.target)
191
+ ] }),
192
+ /* @__PURE__ */ jsxs(Table.Cell, { children: [
193
+ edge.type,
194
+ " \xB7 ",
195
+ edgeState(edge),
196
+ (!byId.has(edge.source) || !byId.has(edge.target)) && " \xB7 \uB05D\uC810 \uC815\uBCF4 \uC5C6\uC74C"
197
+ ] }),
198
+ /* @__PURE__ */ jsxs(Table.Cell, { children: [
199
+ edge.confidence ?? "\uC54C \uC218 \uC5C6\uC74C",
200
+ " \xB7 ",
201
+ edge.evidence || "\uADFC\uAC70 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC74C"
202
+ ] }),
203
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Button, { type: "button", "aria-pressed": selectedEdge?.id === edge.id, "aria-label": `\uAD00\uACC4 \uC120\uD0DD: ${edge.id}`, onClick: () => changeSelection({ kind: "edge", id: edge.id }), children: "\uADFC\uAC70 \uBCF4\uAE30" }) })
204
+ ] }, edge.id)) })
205
+ ] }),
206
+ visibleEdges.length === 0 && /* @__PURE__ */ jsx("p", { children: "\uD604\uC7AC \uD544\uD130\uC5D0\uC11C \uD45C\uC2DC\uD560 \uAD00\uACC4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." })
207
+ ] })
208
+ ] })
209
+ ] });
210
+ });
211
+ RelationGraph.displayName = "RelationGraph";
212
+ export {
213
+ RelationGraph,
214
+ layoutRelationNodes
215
+ };
package/dist/shell.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
+ import * as RadixDialog from '@radix-ui/react-dialog';
3
4
 
4
5
  interface AppShellProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
5
6
  readonly nav: ReactNode;
@@ -10,9 +11,14 @@ interface AppShellProps extends Omit<HTMLAttributes<HTMLDivElement>, "children">
10
11
  /** Accessible name of the mobile navigation scrim, which closes the drawer. */
11
12
  readonly navCloseLabel?: string;
12
13
  readonly mainId?: string;
14
+ /** Changing this consumer-owned route identity closes navigation and focuses main. */
15
+ readonly routeKey?: string;
16
+ readonly navLabel?: string;
13
17
  readonly children: ReactNode;
14
18
  }
15
19
  declare const AppShell: react.ForwardRefExoticComponent<AppShellProps & react.RefAttributes<HTMLDivElement>>;
20
+ /** Place inside AppShell, normally in TopBar.menuButton. Use asChild for IconButton. */
21
+ declare const AppShellNavTrigger: react.ForwardRefExoticComponent<Omit<RadixDialog.DialogTriggerProps & react.RefAttributes<HTMLButtonElement>, "ref"> & react.RefAttributes<HTMLButtonElement>>;
16
22
  interface NavItem {
17
23
  readonly id: string;
18
24
  readonly label: string;
@@ -40,4 +46,4 @@ interface TopBarProps extends Omit<HTMLAttributes<HTMLElement>, "children" | "ti
40
46
  }
41
47
  declare const TopBar: react.ForwardRefExoticComponent<TopBarProps & react.RefAttributes<HTMLElement>>;
42
48
 
43
- export { AppShell, type AppShellProps, type NavItem, type NavLinkRenderProps, NavList, type NavListProps, TopBar, type TopBarProps };
49
+ export { AppShell, AppShellNavTrigger, type AppShellProps, type NavItem, type NavLinkRenderProps, NavList, type NavListProps, TopBar, type TopBarProps };
package/dist/shell.js CHANGED
@@ -1,2 +1,158 @@
1
- export { AppShell, NavList, TopBar } from './chunk-R5BLKHR2.js';
2
- import './chunk-WEIMKRZV.js';
1
+ "use client";
2
+ "use client";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import * as RadixDialog from "@radix-ui/react-dialog";
5
+ import {
6
+ Fragment as Fragment2,
7
+ forwardRef,
8
+ useRef,
9
+ useId,
10
+ useEffect,
11
+ useState
12
+ } from "react";
13
+ import { breakpoints } from "@conductor-by-89soone/tokens";
14
+ import { cx } from "./cx.js";
15
+ const AppShell = forwardRef(function AppShell2({
16
+ children,
17
+ className,
18
+ mainId: suppliedMainId,
19
+ routeKey,
20
+ navLabel = "Navigation",
21
+ nav,
22
+ navCloseLabel = "Close navigation",
23
+ navOpen,
24
+ onNavOpenChange,
25
+ skipLinkLabel,
26
+ topBar,
27
+ ...props
28
+ }, ref) {
29
+ const mainRef = useRef(null);
30
+ const generatedId = useId();
31
+ const mainId = suppliedMainId ?? `cdt-main-${generatedId}`;
32
+ const [internalOpen, setInternalOpen] = useState(false);
33
+ const open = navOpen ?? internalOpen;
34
+ const returnFocus = useRef(null);
35
+ const outsideInteraction = useRef(false);
36
+ const routeChanged = useRef(false);
37
+ const previousRoute = useRef(routeKey);
38
+ const changeOpen = (next) => {
39
+ setInternalOpen(next);
40
+ onNavOpenChange?.(next);
41
+ };
42
+ const changeOpenRef = useRef(changeOpen);
43
+ changeOpenRef.current = changeOpen;
44
+ useEffect(() => {
45
+ if (previousRoute.current === routeKey) return;
46
+ previousRoute.current = routeKey;
47
+ routeChanged.current = true;
48
+ changeOpenRef.current(false);
49
+ mainRef.current?.focus({ preventScroll: true });
50
+ }, [routeKey]);
51
+ useEffect(() => {
52
+ if (typeof window.matchMedia !== "function") return;
53
+ const media = window.matchMedia(`(max-width: ${breakpoints.md}px)`);
54
+ const onChange = () => {
55
+ if (!media.matches) {
56
+ routeChanged.current = true;
57
+ changeOpenRef.current(false);
58
+ mainRef.current?.focus({ preventScroll: true });
59
+ }
60
+ };
61
+ media.addEventListener("change", onChange);
62
+ return () => media.removeEventListener("change", onChange);
63
+ }, []);
64
+ return /* @__PURE__ */ jsxs(RadixDialog.Root, { modal: false, open, onOpenChange: changeOpen, children: [
65
+ /* @__PURE__ */ jsxs("div", { ...props, ref, className: cx("cdt-app-shell", className), children: [
66
+ /* @__PURE__ */ jsx(
67
+ "a",
68
+ {
69
+ className: "cdt-skip-link",
70
+ href: `#${mainId}`,
71
+ onClick: (event) => {
72
+ event.preventDefault();
73
+ mainRef.current?.focus();
74
+ },
75
+ children: skipLinkLabel
76
+ }
77
+ ),
78
+ /* @__PURE__ */ jsx("aside", { className: "cdt-app-shell__nav", "aria-hidden": open || void 0, children: open ? null : nav }),
79
+ /* @__PURE__ */ jsxs("div", { className: "cdt-app-shell__content", children: [
80
+ topBar,
81
+ /* @__PURE__ */ jsx("main", { ref: mainRef, id: mainId, className: "cdt-app-shell__main", tabIndex: -1, children })
82
+ ] })
83
+ ] }),
84
+ /* @__PURE__ */ jsxs(RadixDialog.Portal, { children: [
85
+ /* @__PURE__ */ jsx(RadixDialog.Close, { asChild: true, children: /* @__PURE__ */ jsx("button", { type: "button", className: "cdt-app-shell__overlay", "aria-label": navCloseLabel }) }),
86
+ /* @__PURE__ */ jsxs(
87
+ RadixDialog.Content,
88
+ {
89
+ className: "cdt-app-shell__nav",
90
+ "data-mobile": "",
91
+ "aria-describedby": void 0,
92
+ onOpenAutoFocus: () => {
93
+ returnFocus.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
94
+ outsideInteraction.current = false;
95
+ routeChanged.current = false;
96
+ },
97
+ onInteractOutside: (event) => {
98
+ const target = event.detail.originalEvent.target;
99
+ outsideInteraction.current = !(target instanceof Element && target.closest(".cdt-app-shell__overlay"));
100
+ },
101
+ onCloseAutoFocus: (event) => {
102
+ event.preventDefault();
103
+ if (routeChanged.current) mainRef.current?.focus({ preventScroll: true });
104
+ else if (!outsideInteraction.current && returnFocus.current?.isConnected) returnFocus.current.focus({ preventScroll: true });
105
+ },
106
+ children: [
107
+ /* @__PURE__ */ jsx(RadixDialog.Title, { className: "cdt-sr-only", children: navLabel }),
108
+ nav
109
+ ]
110
+ }
111
+ )
112
+ ] })
113
+ ] });
114
+ });
115
+ AppShell.displayName = "AppShell";
116
+ const AppShellNavTrigger = forwardRef(
117
+ function AppShellNavTrigger2({ className, asChild = false, ...props }, ref) {
118
+ return /* @__PURE__ */ jsx(RadixDialog.Trigger, { ...props, asChild, ref, className: cx(!asChild && "cdt-btn cdt-btn--ghost", className) });
119
+ }
120
+ );
121
+ AppShellNavTrigger.displayName = "AppShellNavTrigger";
122
+ const NavList = forwardRef(function NavList2({ className, items, renderLink, ...props }, ref) {
123
+ let previousSection;
124
+ return /* @__PURE__ */ jsx("nav", { ...props, ref, className: cx("cdt-nav-list", className), children: /* @__PURE__ */ jsx("ul", { className: "cdt-nav-list__items", children: items.map((item) => {
125
+ const showSection = item.section !== void 0 && item.section !== previousSection;
126
+ previousSection = item.section;
127
+ const linkProps = {
128
+ className: cx("cdt-nav-list__item", item.active && "cdt-nav-list__item--active"),
129
+ "aria-current": item.active ? "page" : void 0,
130
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
131
+ item.icon === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "cdt-nav-list__icon", "aria-hidden": "true", children: item.icon }),
132
+ /* @__PURE__ */ jsx("span", { children: item.label })
133
+ ] })
134
+ };
135
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
136
+ showSection ? /* @__PURE__ */ jsx("li", { className: "cdt-nav-list__section-label", children: item.section }) : null,
137
+ /* @__PURE__ */ jsx("li", { children: renderLink(item, linkProps) })
138
+ ] }, item.id);
139
+ }) }) });
140
+ });
141
+ NavList.displayName = "NavList";
142
+ const TopBar = forwardRef(function TopBar2({ actions, className, eyebrow, menuButton, title, ...props }, ref) {
143
+ return /* @__PURE__ */ jsxs("header", { ...props, ref, className: cx("cdt-topbar", className), title: typeof title === "string" ? title : void 0, children: [
144
+ menuButton === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-topbar__menu-button", children: menuButton }),
145
+ eyebrow === void 0 && title === void 0 ? null : /* @__PURE__ */ jsxs("div", { className: "cdt-topbar__context", children: [
146
+ eyebrow === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-topbar__eyebrow", children: eyebrow }),
147
+ title === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-topbar__title", children: title })
148
+ ] }),
149
+ actions === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-topbar__actions", children: actions })
150
+ ] });
151
+ });
152
+ TopBar.displayName = "TopBar";
153
+ export {
154
+ AppShell,
155
+ AppShellNavTrigger,
156
+ NavList,
157
+ TopBar
158
+ };
package/dist/status.js CHANGED
@@ -1,2 +1,35 @@
1
- export { Badge, SeverityTag, StatusBadge } from './chunk-535YGQAR.js';
2
- import './chunk-WEIMKRZV.js';
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { SEVERITY_ICONS, STATUS_ICONS } from "@conductor-by-89soone/tokens";
4
+ import { forwardRef } from "react";
5
+ import { cx } from "./cx.js";
6
+ const Badge = forwardRef(function Badge2({ children, className, icon, tone = "neutral", ...props }, ref) {
7
+ return /* @__PURE__ */ jsxs("span", { ...props, ref, className: cx("cdt-badge", `cdt-badge--${tone}`, className), children: [
8
+ icon === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "cdt-badge__icon", "aria-hidden": "true", children: icon }),
9
+ children
10
+ ] });
11
+ });
12
+ Badge.displayName = "Badge";
13
+ const markerStatuses = ["queued", "neutralEnd"];
14
+ const StatusBadge = forwardRef(function StatusBadge2({ className, icon, label, status, ...props }, ref) {
15
+ const isMarker = markerStatuses.includes(status);
16
+ const statusClass = status === "neutralEnd" ? "neutral-end" : status;
17
+ return /* @__PURE__ */ jsxs("span", { ...props, ref, className: cx("cdt-badge", "cdt-status-badge", `cdt-status-badge--${statusClass}`, className), children: [
18
+ isMarker ? /* @__PURE__ */ jsx("span", { className: cx("cdt-status-badge__marker", status === "neutralEnd" && "cdt-status-badge__marker--neutral-end"), "aria-hidden": "true" }) : null,
19
+ /* @__PURE__ */ jsx("span", { className: "cdt-badge__icon", "aria-hidden": "true", "data-cdt-icon": STATUS_ICONS[status], children: icon }),
20
+ label
21
+ ] });
22
+ });
23
+ StatusBadge.displayName = "StatusBadge";
24
+ const SeverityTag = forwardRef(function SeverityTag2({ className, icon, label, severity, ...props }, ref) {
25
+ return /* @__PURE__ */ jsxs("span", { ...props, ref, className: cx("cdt-badge", "cdt-severity-tag", `cdt-severity-tag--${severity}`, className), children: [
26
+ /* @__PURE__ */ jsx("span", { className: "cdt-badge__icon", "aria-hidden": "true", "data-cdt-icon": SEVERITY_ICONS[severity], children: icon }),
27
+ label
28
+ ] });
29
+ });
30
+ SeverityTag.displayName = "SeverityTag";
31
+ export {
32
+ Badge,
33
+ SeverityTag,
34
+ StatusBadge
35
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Warn once when a consumer renders a Conductor class without loading the CSS
3
+ * package. The deferred check gives bundlers time to apply extracted styles and
4
+ * never runs during SSR.
5
+ */
6
+ declare function scheduleStylesheetWarning(): void;
7
+
8
+ export { scheduleStylesheetWarning };
@@ -1,6 +1,5 @@
1
- // src/stylesheet-warning.ts
2
- var STYLE_MARKER = "--cdt-font-sans";
3
- var warningScheduled = false;
1
+ const STYLE_MARKER = "--cdt-font-sans";
2
+ let warningScheduled = false;
4
3
  function scheduleStylesheetWarning() {
5
4
  if (process.env.NODE_ENV !== "development" || typeof window === "undefined" || warningScheduled) return;
6
5
  warningScheduled = true;
@@ -11,13 +10,6 @@ function scheduleStylesheetWarning() {
11
10
  }
12
11
  }, 0);
13
12
  }
14
-
15
- // src/cx.ts
16
- function cx(...classNames) {
17
- if (classNames.some((className) => typeof className === "string" && /(?:^|\s)cdt-/.test(className))) {
18
- scheduleStylesheetWarning();
19
- }
20
- return classNames.filter((className) => typeof className === "string" && className !== "").join(" ");
21
- }
22
-
23
- export { cx };
13
+ export {
14
+ scheduleStylesheetWarning
15
+ };
package/dist/surface.js CHANGED
@@ -1,2 +1,45 @@
1
- export { Card, CardGrid, Panel } from './chunk-TUHWL5JC.js';
2
- import './chunk-WEIMKRZV.js';
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { forwardRef, useEffect, useRef } from "react";
4
+ import { cx } from "./cx.js";
5
+ const nestedInteractive = "a,button,input,select,textarea,[role=button]";
6
+ const Card = forwardRef(function Card2({ as, children, className, href, onClick, size = "md", ...props }, forwardedRef) {
7
+ const rootRef = useRef(null);
8
+ const interactive = as === "a" || as === "button" || href !== void 0 || onClick !== void 0;
9
+ const element = as ?? (href === void 0 ? onClick === void 0 ? "div" : "button" : "a");
10
+ useEffect(() => {
11
+ if (process.env.NODE_ENV !== "production" && interactive && rootRef.current?.querySelector(nestedInteractive) !== null) {
12
+ console.warn("[conductor] Interactive Card must not contain nested interactive elements.");
13
+ }
14
+ }, [interactive]);
15
+ const setRef = (node) => {
16
+ rootRef.current = node;
17
+ if (typeof forwardedRef === "function") forwardedRef(node);
18
+ else if (forwardedRef !== null) forwardedRef.current = node;
19
+ };
20
+ const classes = cx("cdt-card", interactive && "cdt-card--interactive", size === "sm" && "cdt-card--sm", className);
21
+ if (element === "a") {
22
+ return /* @__PURE__ */ jsx("a", { ...props, ref: setRef, href, className: classes, children });
23
+ }
24
+ if (element === "button") {
25
+ return /* @__PURE__ */ jsx("button", { ...props, ref: setRef, type: "button", onClick, className: classes, children });
26
+ }
27
+ return /* @__PURE__ */ jsx("div", { ...props, ref: setRef, className: classes, children });
28
+ });
29
+ Card.displayName = "Card";
30
+ const CardGrid = forwardRef(function CardGrid2({ className, ...props }, ref) {
31
+ return /* @__PURE__ */ jsx("div", { ...props, ref, className: cx("cdt-card-grid", className) });
32
+ });
33
+ CardGrid.displayName = "CardGrid";
34
+ const Panel = forwardRef(function Panel2({ as = "div", className, size = "md", ...props }, ref) {
35
+ const classes = cx("cdt-panel", size === "sm" && "cdt-panel--sm", className);
36
+ if (as === "section") return /* @__PURE__ */ jsx("section", { ...props, ref, className: classes });
37
+ if (as === "aside") return /* @__PURE__ */ jsx("aside", { ...props, ref, className: classes });
38
+ return /* @__PURE__ */ jsx("div", { ...props, ref, className: classes });
39
+ });
40
+ Panel.displayName = "Panel";
41
+ export {
42
+ Card,
43
+ CardGrid,
44
+ Panel
45
+ };
package/dist/types.js CHANGED
@@ -1 +0,0 @@
1
-