@conductor-by-89soone/react 0.3.0 → 0.4.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 CHANGED
@@ -36,3 +36,234 @@ Node.js 20 or newer, React 18 or 19, React DOM 18 or 19, and `lucide-react` 0.40
36
36
  ## License
37
37
 
38
38
  MIT
39
+
40
+ ## Workbench APIs (CR-041, review extension)
41
+
42
+ The additions below are a local review extension; installing a previously published
43
+ 0.3.1 package does not provide them. Build and install the matching tokens, CSS,
44
+ and React tarballs together when evaluating this change. No component fetches,
45
+ authenticates, parses a search language, infers relationships, or retries work
46
+ without a consumer callback.
47
+
48
+ Load `@conductor-by-89soone/css` once in the application root. Set
49
+ `data-cdt-theme="dark"` or `data-cdt-theme="light"` on `html`; both palettes use the
50
+ same semantic keys. There is no new provider or remote font requirement. For a
51
+ locally themed island, pass that island's element as `Popover.Content container`
52
+ so portalled content inherits its theme. A body portal inherits the document
53
+ theme, not the trigger's nested theme.
54
+
55
+ In Next App Router, import CSS from the root layout and put state/callback code in
56
+ a small `"use client"` component. The package preserves client directives on
57
+ component modules and keeps its re-export barrel unmarked. A Server Component
58
+ can render a client component with serializable data; callback functions must be
59
+ created inside a client boundary. SSR output alone does not demonstrate hydration
60
+ or RSC compatibility; see the local verification record for executed consumers.
61
+
62
+ ### Disclosure and overlay
63
+
64
+ ```tsx
65
+ "use client";
66
+ import { Tabs, Popover, Collapsible, Button } from "@conductor-by-89soone/react";
67
+
68
+ export function Filters() {
69
+ return <Tabs.Root defaultValue="results" activationMode="manual">
70
+ <Tabs.List aria-label="Investigation view">
71
+ <Tabs.Trigger value="results">Results</Tabs.Trigger>
72
+ <Tabs.Trigger value="history">History</Tabs.Trigger>
73
+ <Tabs.Trigger value="restricted" disabled>Unavailable</Tabs.Trigger>
74
+ </Tabs.List>
75
+ <Tabs.Content value="results" forceMount>
76
+ <Popover.Root>
77
+ <Popover.Trigger asChild><Button>More filters</Button></Popover.Trigger>
78
+ <Popover.Content aria-label="Additional filters">
79
+ <Collapsible.Root>
80
+ <Collapsible.Trigger>Advanced conditions</Collapsible.Trigger>
81
+ <Collapsible.Content>Consumer-owned filter fields</Collapsible.Content>
82
+ </Collapsible.Root>
83
+ <Popover.Close asChild><Button>Close</Button></Popover.Close>
84
+ </Popover.Content>
85
+ </Popover.Root>
86
+ </Tabs.Content>
87
+ <Tabs.Content value="history">History supplied by the consumer</Tabs.Content>
88
+ </Tabs.Root>;
89
+ }
90
+ ```
91
+
92
+ - `Tabs.Root` accepts Radix `value/onValueChange`, `defaultValue`, `orientation`
93
+ and `activationMode`. Conductor defaults to **manual**: arrows/Home/End move
94
+ focus, Enter/Space activate. `automatic` is opt-in for inexpensive content.
95
+ Disabled triggers are skipped. Provide an initial value. Inactive contents
96
+ unmount by default; `forceMount` preserves child state but keeps inactive
97
+ panels hidden. Maintaining a panel can keep its consumer effects running.
98
+ - `Popover` provides `Root/Trigger/Anchor/Content/Close/Arrow`, with Radix open
99
+ state, Escape/outside dismissal, and focus restoration. Give its content an
100
+ accessible name; use `asChild` to avoid nesting buttons.
101
+ - `Collapsible` provides `Root/Trigger/Content`, Radix controlled or uncontrolled
102
+ expansion and disabled props. Enter/Space toggle its trigger. The component
103
+ does not request data on expansion: keep first-load, cached reopen, and explicit
104
+ retry policy in the consumer. It is a disclosure, not a complete Accordion API.
105
+
106
+ ### Search, filtering, and results
107
+
108
+ | API | Data and interaction contract | Consumer responsibility / incorrect use |
109
+ | --- | --- | --- |
110
+ | `Combobox` | Required `label`, `options: {id,label,disabled?}[]`, `value`, `query`, `onValueChange`, `onQueryChange`. `state` is ready/loading/error; `resultsQuery` hides stale option batches. Downshift owns single-selection keyboard behavior. IME composition does not commit Enter. `inputRef` targets input; forwarded ref targets wrapper. | Associate responses with their original query, supply error/retry UI as needed, and keep selected value separate from typed text. Do not label the single-selection popup as multi-select or publish stale results with the current query key. |
111
+ | `MultiSelect` | `label/options/value/onValueChange`, optional disabled; a fieldset/legend with existing Radix Checkbox controls, local option search, and removable selected chips. Tab reaches controls; Space toggles a checkbox. | Supply stable unique IDs and your approved options. Fetch/loading/error state is external. Do not treat this checkbox group as a single Combobox or infer authorization from disabled controls. |
112
+ | `FilterChip` | Text children plus required `removeLabel/onRemove`; optional disabled. Removal is a separate named button. | Use a specific label such as “Remove repository filter”; removing a chip does not parse or rewrite a query automatically. |
113
+ | `FilterToolbar` | A styled div forwarding HTML attributes/ref. It groups normal tabbable controls. | Give a group name if needed; this is not an ARIA toolbar with a roving arrow-key model. |
114
+ | `DataTable<Row>` | Required `label/rows/columns/rowId/rowLabel`. Columns supply `id/header/cell`, optional sortable/numeric. `sort/onSortChange` is external; `visibleColumns` and density control display. Semantic table, separately named preview buttons, no row click handler. | Sort the full local source or request server sorting before passing rows. Do not sort one cursor page and claim global order. Stable IDs must survive sorting/appending. Links in cells remain independent of selection. |
115
+ | DataTable selection/paging | `selectedId/onSelect(id, trigger)` selects one loaded row for preview. `hasMore/onLoadMore/loadingMore` exposes cursor continuation. No page counts or cursor storage. | Preserve opaque cursors in session state; share conditions in the URL without cursors. This is not “select all results”. Own URL replace/push and permission-bound resets. |
116
+ | DataTable states | `state`: ready/loading/error/partial, optional `message`; an empty rows array yields empty state. Loading is busy, partial/error retain provided rows. Density defaults comfortable; compact is explicit. | Announce whether rows are stale/partial using message. The component has no sorting, resizing, pinning, or virtualization engine. |
117
+
118
+ ```tsx
119
+ "use client";
120
+ import { useRef, useState } from "react";
121
+ import { DataTable, DetailInspector, WorkbenchLayout, CopyButton,
122
+ type DataColumn } from "@conductor-by-89soone/react";
123
+
124
+ type Row = { id: string; title: string };
125
+ const rows: Row[] = [{ id: "sample:change:1", title: "검색 맥락 보존 / preserve context" }];
126
+ const columns: DataColumn<Row>[] = [
127
+ { id: "title", header: "Title", cell: row => row.title },
128
+ ];
129
+ export function SearchPreview() {
130
+ const [selectedId, select] = useState<string | null>(null);
131
+ const [width, setWidth] = useState(40);
132
+ const trigger = useRef<HTMLButtonElement | null>(null);
133
+ const selected = rows.find(row => row.id === selectedId);
134
+ return <WorkbenchLayout width={width} onWidthChange={setWidth}
135
+ inspector={selected && <DetailInspector label="Selected change"
136
+ returnFocusRef={trigger} onClose={() => select(null)}>
137
+ <p>{selected.title}</p><code>{selected.id}</code>
138
+ <CopyButton value={selected.id} label="Copy identifier" />
139
+ </DetailInspector>}>
140
+ <DataTable label="Loaded changes" rows={rows} columns={columns}
141
+ rowId={row => row.id} rowLabel={row => row.title} selectedId={selectedId}
142
+ onSelect={(id, button) => { trigger.current = button; select(id); }} />
143
+ </WorkbenchLayout>;
144
+ }
145
+ ```
146
+
147
+ `WorkbenchLayout` accepts children and an optional inspector, with a controlled
148
+ 25–60 percent inspector `width` (default 40) and `onWidthChange`. The native range
149
+ control supports keyboard adjustment; small screens stack the areas. This is not
150
+ a draggable separator or a modal focus trap. `DetailInspector` closes on its
151
+ button or unhandled Escape, calls `onClose`, then focuses `returnFocusRef` without
152
+ scrolling. Keep the selecting row mounted; if it disappears, the consumer must
153
+ choose a meaningful fallback. Preview uses existing row data and makes no request.
154
+
155
+ `PathList` takes `items: {id,label,children?,state?}[]`, optional
156
+ `selectedId/onPathSelect`. Folders are initially collapsed native details; Tab and
157
+ Enter/Space operate summaries, leaves are buttons. `state: loading/error` reports
158
+ lazy-data state; fetching/retry is external. It is a **path list**, not an ARIA
159
+ TreeView: there is no tree arrow-key or typeahead promise. `Breadcrumb` is a nav
160
+ wrapper (default accessible name “경로”); supply links/separators/current-page
161
+ semantics as children. `Skeleton` requires `label` and reports loading status.
162
+ `CopyButton` reports clipboard success/failure and ignores feedback for another
163
+ current value; display selectable text alongside it for unavailable clipboard
164
+ fallback. None of these APIs store navigation or clipboard data persistently.
165
+
166
+ ### Relationship exploration
167
+
168
+ The graph is also available from the optional `@conductor-by-89soone/react/relation`
169
+ entry, exporting `RelationGraph`, `layoutRelationNodes`, and their public types.
170
+ It uses the same implementation and shared Conductor stylesheet, with no extra
171
+ visualization dependency. Use this entry in a client component or dynamic import
172
+ to isolate graph loading; importing Button alone does not load graph code.
173
+
174
+ ```tsx
175
+ import { RelationGraph } from "@conductor-by-89soone/react";
176
+
177
+ export function Relations() {
178
+ return <RelationGraph label="Synthetic change relationships" state="partial"
179
+ stateMessage="Synthetic data; production relationship service is not connected"
180
+ nodes={[
181
+ { id: "sample:change:a", label: "Change A" },
182
+ { id: "sample:change:b", label: "Change B" },
183
+ ]}
184
+ edges={[{ id: "sample:edge:ab", source: "sample:change:a",
185
+ target: "sample:change:b", type: "references", label: "references",
186
+ evidence: "Synthetic example text links A to B", confidence: "inferred",
187
+ ambiguous: true }]} />;
188
+ }
189
+ ```
190
+
191
+ `RelationGraph` receives nodes and edges; node IDs and edge IDs are independent,
192
+ stable, consumer-defined identifiers. An edge is read as **source → label →
193
+ target**. Optional `unresolved/ambiguous/retracted` flags are independent;
194
+ map a consumer's detached state to `retracted` without discarding other flags.
195
+ Missing endpoint metadata is displayed as unavailable information, not fetched.
196
+ `confidence` and `evidence` are text, not an inference engine or HTML renderer.
197
+ Sanitize the *data scope* on the server before passing it: `unavailable` is a
198
+ presentation hint, not authorization, and does not hide a supplied private label.
199
+ Never pass unauthorized nodes, hidden counts, or inaccessible connecting edges.
200
+
201
+ Node search, type filtering, node/edge selection, neighbor emphasis, zoom,
202
+ directional pan buttons, fit view, and selected evidence form one read-only flow.
203
+ The SVG is hidden from assistive technology; semantic node/edge tables expose the
204
+ same selection and information with normal links/buttons. There is no graph
205
+ editing, drag-to-connect, deletion, node movement, or recursive fetch. Nodes use
206
+ deterministic ID ordering; geometric placement is not time, dependency order,
207
+ merge sequence, confidence, or importance. Cycles are allowed.
208
+
209
+ `selection/onSelectionChange` and `viewport/onViewportChange` can be controlled;
210
+ otherwise their state is local to the mounted instance. A consumer can retain
211
+ these values across its own navigation, but the package adds no persistence.
212
+ `state` supports ready/loading/empty/error/partial and `stateMessage` supplies
213
+ context. `onRetry` renders an explicit retry only for error; it does not auto-poll.
214
+ A filter can hide a selected edge without resetting selection; details say so.
215
+ Use an empty state only for confirmed absence, not for unresolved or partial data.
216
+ The generic component has no production graph endpoint; demos are synthetic.
217
+
218
+ ### Processing status
219
+
220
+ ```tsx
221
+ import { ProcessingStatus } from "@conductor-by-89soone/react";
222
+
223
+ export function Processing() {
224
+ return <ProcessingStatus label="Synthetic processing stages" stages={[
225
+ { id: "received", label: "Received", status: "complete", detail: "Receipt confirmed" },
226
+ { id: "stored", label: "Durably stored", status: "complete", detail: "Storage confirmed" },
227
+ { id: "processed", label: "Processed", status: "delayed", detail: "Waiting for the consumer worker" },
228
+ { id: "indexed", label: "Indexed", status: "unknown", detail: "Search freshness not confirmed" },
229
+ ]} />;
230
+ }
231
+ ```
232
+
233
+ `ProcessingStatus` accepts `label` and `stages` with stable `id`, `label`,
234
+ `status: complete/pending/delayed/failed/unknown`, and explanatory `detail`.
235
+ An optional `action` has `label/onAction/disabledReason/busy`; omit it when there
236
+ is no allowed action, show a reason when it is unavailable, and make callback
237
+ outcomes visible. Existing Timeline, Badge, and Button render the pattern.
238
+ Native button keyboard behavior applies. There are no percentages, ETAs, requests,
239
+ automatic retries, or timers. A complete receipt stage says nothing about durable
240
+ storage or index freshness. Preserve successful stages alongside partial failures.
241
+ Consumers retain their already approved polling and authorization policies.
242
+
243
+ ### Migration from consumer-owned compositions
244
+
245
+ 1. Build and pack all three packages, install matching tarballs, import CSS once,
246
+ and verify React peer resolution and Next hydration before application rollout.
247
+ 2. Replace local Tabs with compound `Tabs` parts; explicitly choose manual or
248
+ automatic activation, initial value, controlled selection, and panel lifetime.
249
+ Keep expensive fetching out of mere keyboard focus changes.
250
+ 3. Map local filter values/options into `Combobox`, `MultiSelect`, and `FilterChip`.
251
+ Keep query parsing, stale-request generations, session-bound cursors, and URL
252
+ replace/push semantics in the consumer.
253
+ 4. Replace table/workbench presentation with `DataTable/WorkbenchLayout/DetailInspector`.
254
+ Keep stable identity, current-row reuse, detail routing, and return-focus refs.
255
+ Add compact density explicitly; existing Card styling is unchanged.
256
+ 5. Adapt already authorized relations into `RelationNode/RelationEdge`; retain
257
+ direction, ambiguity, unresolved/detached states, evidence, and confidence.
258
+ Preserve lazy first expansion and cached reopen; do not turn one pending
259
+ relation category into “all relationships absent”. A planned graph API still
260
+ requires a consumer adapter and production integration work.
261
+ 6. Connect menu controls using `AppShellNavTrigger` under the same `AppShell`
262
+ context (use `asChild` with an existing Button). Keep route-to-main focus.
263
+ Remove any old animation-frame fallback only after the consumer reproduces
264
+ Escape/outside-close/navigation focus with the new package; package regression
265
+ tests alone do not prove the original consumer has migrated.
266
+
267
+ This repository change does not migrate or write to a production consumer.
268
+ Published version changes, visual/API snapshots, browser matrices, measured bundle
269
+ costs, and checks that could not run must be reviewed with the implementation log.
package/dist/action.js CHANGED
@@ -1,3 +1,62 @@
1
- export { Button, IconButton } from './chunk-G7TRTZRR.js';
2
- import './chunk-NF3ZHADJ.js';
3
- import './chunk-WEIMKRZV.js';
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { forwardRef } from "react";
4
+ import { cx } from "./cx.js";
5
+ import { Spinner } from "./feedback.js";
6
+ function buttonClasses({ variant = "secondary", tone = "neutral", size = "md", blockedReason, className }) {
7
+ return cx(
8
+ "cdt-btn",
9
+ `cdt-btn--${variant}`,
10
+ `cdt-btn--tone-${tone}`,
11
+ size === "sm" && "cdt-btn--sm",
12
+ blockedReason !== void 0 && "cdt-btn--blocked",
13
+ className
14
+ );
15
+ }
16
+ const Button = forwardRef(function Button2({ blockedReason, className, disabled, iconEnd, iconStart, loading = false, onClick, title, variant, tone, size, children, ...props }, ref) {
17
+ const isBlocked = blockedReason !== void 0;
18
+ const isDisabled = disabled || isBlocked;
19
+ return /* @__PURE__ */ jsxs(
20
+ "button",
21
+ {
22
+ ...props,
23
+ ref,
24
+ className: buttonClasses({ variant, tone, size, blockedReason, className, children }),
25
+ disabled: isDisabled,
26
+ "aria-busy": loading || void 0,
27
+ title: isBlocked ? blockedReason : title,
28
+ onClick: (event) => {
29
+ if (loading) {
30
+ event.preventDefault();
31
+ event.stopPropagation();
32
+ return;
33
+ }
34
+ onClick?.(event);
35
+ },
36
+ children: [
37
+ loading ? /* @__PURE__ */ jsx(Spinner, { size: "sm", label: "", "aria-hidden": "true" }) : iconStart === void 0 ? null : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: iconStart }),
38
+ children,
39
+ iconEnd === void 0 ? null : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: iconEnd })
40
+ ]
41
+ }
42
+ );
43
+ });
44
+ Button.displayName = "Button";
45
+ const IconButton = forwardRef(function IconButton2({ icon, size, ...props }, ref) {
46
+ return /* @__PURE__ */ jsx(
47
+ Button,
48
+ {
49
+ ...props,
50
+ ref,
51
+ size,
52
+ iconStart: icon,
53
+ className: cx("cdt-btn--icon", props.className),
54
+ children: null
55
+ }
56
+ );
57
+ });
58
+ IconButton.displayName = "IconButton";
59
+ export {
60
+ Button,
61
+ IconButton
62
+ };
package/dist/cx.js CHANGED
@@ -1 +1,10 @@
1
- export { cx } from './chunk-WEIMKRZV.js';
1
+ import { scheduleStylesheetWarning } from "./stylesheet-warning.js";
2
+ function cx(...classNames) {
3
+ if (classNames.some((className) => typeof className === "string" && /(?:^|\s)cdt-/.test(className))) {
4
+ scheduleStylesheetWarning();
5
+ }
6
+ return classNames.filter((className) => typeof className === "string" && className !== "").join(" ");
7
+ }
8
+ export {
9
+ cx
10
+ };
package/dist/data.js CHANGED
@@ -1,2 +1,65 @@
1
- export { CodeBlock, Kbd, Table, Timeline } from './chunk-RBD5Z2LR.js';
2
- import './chunk-WEIMKRZV.js';
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef,
5
+ useEffect
6
+ } from "react";
7
+ import { cx } from "./cx.js";
8
+ const TableHead = forwardRef(function TableHead2({ className, ...props }, ref) {
9
+ return /* @__PURE__ */ jsx("thead", { ...props, ref, className: cx("cdt-table__head", className) });
10
+ });
11
+ TableHead.displayName = "Table.Head";
12
+ const TableBody = forwardRef(function TableBody2({ className, ...props }, ref) {
13
+ return /* @__PURE__ */ jsx("tbody", { ...props, ref, className: cx("cdt-table__body", className) });
14
+ });
15
+ TableBody.displayName = "Table.Body";
16
+ const TableRow = forwardRef(function TableRow2({ className, ...props }, ref) {
17
+ return /* @__PURE__ */ jsx("tr", { ...props, ref, className: cx("cdt-table__row", className) });
18
+ });
19
+ TableRow.displayName = "Table.Row";
20
+ const TableHeaderCell = forwardRef(function TableHeaderCell2({ className, scope = "col", ...props }, ref) {
21
+ return /* @__PURE__ */ jsx("th", { ...props, ref, scope, className: cx("cdt-table__header-cell", className) });
22
+ });
23
+ TableHeaderCell.displayName = "Table.HeaderCell";
24
+ const TableCell = forwardRef(function TableCell2({ className, numeric = false, ...props }, ref) {
25
+ return /* @__PURE__ */ jsx("td", { ...props, ref, className: cx("cdt-table__cell", numeric && "cdt-num", className) });
26
+ });
27
+ TableCell.displayName = "Table.Cell";
28
+ const Table = Object.assign(forwardRef(function Table2({ "aria-label": ariaLabel, caption, children, className, scrollContainerProps, ...props }, ref) {
29
+ useEffect(() => {
30
+ if (process.env.NODE_ENV !== "production" && caption == null && ariaLabel === void 0) {
31
+ console.warn("[conductor] Table requires a caption or aria-label.");
32
+ }
33
+ }, [ariaLabel, caption]);
34
+ return /* @__PURE__ */ jsx("div", { ...scrollContainerProps, ...props, ref, tabIndex: props.tabIndex ?? scrollContainerProps?.tabIndex ?? 0, "aria-label": ariaLabel, className: cx("cdt-table__scroll", scrollContainerProps?.className, className), children: /* @__PURE__ */ jsxs("table", { "aria-label": ariaLabel, className: "cdt-table", children: [
35
+ caption == null ? null : /* @__PURE__ */ jsx("caption", { children: caption }),
36
+ children
37
+ ] }) });
38
+ }), { Head: TableHead, Body: TableBody, Row: TableRow, HeaderCell: TableHeaderCell, Cell: TableCell });
39
+ const TimelineStep = forwardRef(function TimelineStep2({ children, className, marker, onSelect, selected = false, ...props }, ref) {
40
+ const stepClassName = cx("cdt-timeline__step", onSelect !== void 0 && "cdt-timeline__step--interactive", className);
41
+ const markerNode = /* @__PURE__ */ jsx("span", { className: "cdt-timeline__marker", "aria-hidden": "true", children: marker });
42
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
43
+ markerNode,
44
+ children
45
+ ] });
46
+ return /* @__PURE__ */ jsx("li", { ref, children: onSelect === void 0 ? /* @__PURE__ */ jsx("div", { ...props, "aria-current": selected ? "step" : void 0, className: stepClassName, children: content }) : /* @__PURE__ */ jsx("button", { ...props, type: "button", "aria-current": selected ? "step" : void 0, onClick: () => onSelect(), className: stepClassName, children: content }) });
47
+ });
48
+ TimelineStep.displayName = "Timeline.Step";
49
+ const Timeline = Object.assign(forwardRef(function Timeline2({ className, ...props }, ref) {
50
+ return /* @__PURE__ */ jsx("ol", { ...props, ref, className: cx("cdt-timeline", className) });
51
+ }), { Step: TimelineStep });
52
+ const CodeBlock = forwardRef(function CodeBlock2({ className, code, language, ...props }, ref) {
53
+ return /* @__PURE__ */ jsx("div", { ...props, ref, role: "region", tabIndex: 0, "data-language": language, className: cx("cdt-code-block", className), children: /* @__PURE__ */ jsx("pre", { className: "cdt-code-block__pre", children: /* @__PURE__ */ jsx("code", { className: "cdt-code-block__code", children: code }) }) });
54
+ });
55
+ CodeBlock.displayName = "CodeBlock";
56
+ const Kbd = forwardRef(function Kbd2({ className, ...props }, ref) {
57
+ return /* @__PURE__ */ jsx("kbd", { ...props, ref, className: cx("cdt-kbd", className) });
58
+ });
59
+ Kbd.displayName = "Kbd";
60
+ export {
61
+ CodeBlock,
62
+ Kbd,
63
+ Table,
64
+ Timeline
65
+ };
package/dist/feedback.js CHANGED
@@ -1,2 +1,65 @@
1
- export { Banner, EmptyState, Meter, ProgressRing, Spinner } from './chunk-NF3ZHADJ.js';
2
- import './chunk-WEIMKRZV.js';
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { forwardRef, useEffect } from "react";
4
+ import { cx } from "./cx.js";
5
+ const Banner = forwardRef(function Banner2({ action, children, className, icon, title, tone = "info", ...props }, ref) {
6
+ useEffect(() => {
7
+ if (process.env.NODE_ENV !== "production" && tone === "danger" && action === void 0) console.warn('[conductor] Banner tone="danger" requires a recovery action.');
8
+ }, [action, tone]);
9
+ return /* @__PURE__ */ jsxs("div", { ...props, ref, role: tone === "danger" ? "alert" : "status", className: cx("cdt-banner", `cdt-banner--${tone}`, className), children: [
10
+ icon === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "cdt-banner__icon", "aria-hidden": "true", children: icon }),
11
+ /* @__PURE__ */ jsxs("div", { className: "cdt-banner__body", children: [
12
+ title === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-banner__title", children: title }),
13
+ children
14
+ ] }),
15
+ action === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-banner__action", children: action })
16
+ ] });
17
+ });
18
+ Banner.displayName = "Banner";
19
+ const EmptyState = forwardRef(function EmptyState2({ action, className, description, icon, title, ...props }, ref) {
20
+ return /* @__PURE__ */ jsxs("div", { ...props, ref, className: cx("cdt-empty-state", className), children: [
21
+ icon === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-empty-state__icon", "aria-hidden": "true", children: icon }),
22
+ /* @__PURE__ */ jsx("div", { className: "cdt-empty-state__title", children: title }),
23
+ description === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-empty-state__description", children: description }),
24
+ action === void 0 ? null : /* @__PURE__ */ jsx("div", { className: "cdt-empty-state__action", children: action })
25
+ ] });
26
+ });
27
+ EmptyState.displayName = "EmptyState";
28
+ const ratio = (value, min, max) => max <= min ? 0 : Math.min(1, Math.max(0, (value - min) / (max - min)));
29
+ const Meter = forwardRef(function Meter2({ "aria-label": ariaLabel, className, exceededAt, max = 100, min = 0, style, value, valueText, warningAt, ...props }, ref) {
30
+ const tone = exceededAt !== void 0 && value >= exceededAt ? "exceeded" : warningAt !== void 0 && value >= warningAt ? "warning" : "normal";
31
+ const meterStyle = { ...style, "--cdt-meter-ratio": String(ratio(value, min, max)) };
32
+ return /* @__PURE__ */ jsxs("div", { ...props, ref, style: meterStyle, role: "meter", "aria-label": ariaLabel, "aria-valuenow": value, "aria-valuemin": min, "aria-valuemax": max, "aria-valuetext": valueText, className: cx("cdt-meter", `cdt-meter--${tone}`, className), children: [
33
+ /* @__PURE__ */ jsx("div", { className: "cdt-meter__track", children: /* @__PURE__ */ jsx("div", { className: "cdt-meter__fill" }) }),
34
+ /* @__PURE__ */ jsx("span", { className: "cdt-meter__value", children: valueText })
35
+ ] });
36
+ });
37
+ Meter.displayName = "Meter";
38
+ const ProgressRing = forwardRef(function ProgressRing2({ "aria-label": ariaLabel, className, max = 100, size = "md", style, value, valueText, ...props }, ref) {
39
+ const ringStyle = { ...style, "--cdt-progress-ring-ratio": String(ratio(value, 0, max)) };
40
+ return /* @__PURE__ */ jsxs("div", { ...props, ref, style: ringStyle, role: "progressbar", "aria-label": ariaLabel, "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuetext": valueText, className: cx("cdt-progress-ring", size === "sm" && "cdt-progress-ring--sm", className), children: [
41
+ /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 32 32", children: [
42
+ /* @__PURE__ */ jsx("circle", { className: "cdt-progress-ring__track", cx: "16", cy: "16", r: "14" }),
43
+ /* @__PURE__ */ jsx("circle", { className: "cdt-progress-ring__indicator", cx: "16", cy: "16", r: "14" })
44
+ ] }),
45
+ /* @__PURE__ */ jsx("span", { className: "cdt-progress-ring__label", children: valueText })
46
+ ] });
47
+ });
48
+ ProgressRing.displayName = "ProgressRing";
49
+ const Spinner = forwardRef(function Spinner2({ className, label, size = "md", ...props }, ref) {
50
+ return /* @__PURE__ */ jsxs("div", { ...props, ref, role: "status", "aria-live": "polite", className: cx("cdt-spinner", size === "sm" && "cdt-spinner--sm", className), children: [
51
+ /* @__PURE__ */ jsxs("svg", { "aria-hidden": "true", viewBox: "0 0 32 32", children: [
52
+ /* @__PURE__ */ jsx("circle", { className: "cdt-spinner__track", cx: "16", cy: "16", r: "14" }),
53
+ /* @__PURE__ */ jsx("circle", { className: "cdt-spinner__indicator", cx: "16", cy: "16", r: "14" })
54
+ ] }),
55
+ /* @__PURE__ */ jsx("span", { className: "cdt-spinner__label", children: label })
56
+ ] });
57
+ });
58
+ Spinner.displayName = "Spinner";
59
+ export {
60
+ Banner,
61
+ EmptyState,
62
+ Meter,
63
+ ProgressRing,
64
+ Spinner
65
+ };
package/dist/form.js CHANGED
@@ -1,2 +1,115 @@
1
- export { Checkbox, Field, Select, Switch, TextArea, TextField } from './chunk-3YDK2JBK.js';
2
- import './chunk-WEIMKRZV.js';
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as RadixCheckbox from "@radix-ui/react-checkbox";
4
+ import * as RadixSelect from "@radix-ui/react-select";
5
+ import * as RadixSwitch from "@radix-ui/react-switch";
6
+ import {
7
+ createContext,
8
+ forwardRef,
9
+ useContext,
10
+ useEffect,
11
+ useId
12
+ } from "react";
13
+ import { cx } from "./cx.js";
14
+ const FieldContext = createContext(null);
15
+ function joinDescribedBy(...ids) {
16
+ const result = ids.filter((id) => id !== void 0).join(" ");
17
+ return result === "" ? void 0 : result;
18
+ }
19
+ function useFieldControl(invalid, describedBy) {
20
+ const field = useContext(FieldContext);
21
+ return {
22
+ id: field?.id ?? "",
23
+ descriptionId: field?.descriptionId,
24
+ errorId: field?.errorId,
25
+ invalid: invalid ?? field?.invalid ?? false,
26
+ required: field?.required ?? false,
27
+ describedBy: joinDescribedBy(describedBy, field?.descriptionId, field?.errorId)
28
+ };
29
+ }
30
+ const Field = forwardRef(function Field2({ children, className, description, error, id: providedId, label, required = false, ...props }, ref) {
31
+ const generatedId = useId();
32
+ const id = providedId ?? generatedId;
33
+ const descriptionId = description === void 0 ? void 0 : `${id}-description`;
34
+ const errorId = error === void 0 ? void 0 : `${id}-error`;
35
+ const invalid = error !== void 0;
36
+ return /* @__PURE__ */ jsx(FieldContext.Provider, { value: { id, descriptionId, errorId, invalid, required }, children: /* @__PURE__ */ jsxs("div", { ...props, ref, className: cx("cdt-field", className), children: [
37
+ /* @__PURE__ */ jsxs("label", { className: "cdt-field__label", htmlFor: id, children: [
38
+ label,
39
+ required ? /* @__PURE__ */ jsx("span", { className: "cdt-field__required", "aria-hidden": "true", children: "*" }) : null
40
+ ] }),
41
+ description === void 0 ? null : /* @__PURE__ */ jsx("div", { id: descriptionId, className: "cdt-field__description", children: description }),
42
+ children,
43
+ error === void 0 ? null : /* @__PURE__ */ jsx("div", { id: errorId, className: "cdt-field__error", children: error })
44
+ ] }) });
45
+ });
46
+ Field.displayName = "Field";
47
+ function useMissingLabelWarning(name, hasField, ariaLabel, ariaLabelledBy) {
48
+ useEffect(() => {
49
+ if (process.env.NODE_ENV !== "production" && !hasField && ariaLabel === void 0 && ariaLabelledBy === void 0) {
50
+ console.warn(`[conductor] ${name} requires Field, aria-label, or aria-labelledby.`);
51
+ }
52
+ }, [ariaLabel, ariaLabelledBy, hasField, name]);
53
+ }
54
+ const TextField = forwardRef(function TextField2({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, iconStart, id: ownId, invalid, required, size = "md", variant = "solid", ...props }, ref) {
55
+ const field = useContext(FieldContext);
56
+ const control = useFieldControl(invalid, ariaDescribedBy);
57
+ useMissingLabelWarning("TextField", field !== null, ariaLabel, ariaLabelledBy);
58
+ const input = /* @__PURE__ */ jsx("input", { ...props, ref, id: ownId ?? (field === null ? void 0 : control.id), "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": control.describedBy, "aria-invalid": ariaInvalid ?? (control.invalid || void 0), required: required ?? control.required, className: cx("cdt-input", variant === "glass" && "cdt-input--glass", size === "sm" && "cdt-input--sm", control.invalid && "cdt-input--invalid", iconStart !== void 0 && "cdt-input--icon-start", className) });
59
+ return iconStart === void 0 ? input : /* @__PURE__ */ jsxs("span", { className: "cdt-input-wrap", children: [
60
+ /* @__PURE__ */ jsx("span", { className: "cdt-input__icon", "aria-hidden": "true", children: iconStart }),
61
+ input
62
+ ] });
63
+ });
64
+ TextField.displayName = "TextField";
65
+ const TextArea = forwardRef(function TextArea2({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, id: ownId, invalid, required, rows = 3, variant = "solid", ...props }, ref) {
66
+ const field = useContext(FieldContext);
67
+ const control = useFieldControl(invalid, ariaDescribedBy);
68
+ useMissingLabelWarning("TextArea", field !== null, ariaLabel, ariaLabelledBy);
69
+ return /* @__PURE__ */ jsx("textarea", { ...props, ref, id: ownId ?? (field === null ? void 0 : control.id), rows, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": control.describedBy, "aria-invalid": ariaInvalid ?? (control.invalid || void 0), required: required ?? control.required, className: cx("cdt-textarea", variant === "glass" && "cdt-textarea--glass", control.invalid && "cdt-textarea--invalid", className) });
70
+ });
71
+ TextArea.displayName = "TextArea";
72
+ const SelectTrigger = forwardRef(function SelectTrigger2({ "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, className, id: ownId, invalid, size = "md", ...props }, ref) {
73
+ const field = useContext(FieldContext);
74
+ const control = useFieldControl(invalid, ariaDescribedBy);
75
+ const requiredAttribute = ariaRequired ?? (control.required ? true : void 0);
76
+ return /* @__PURE__ */ jsx(RadixSelect.Trigger, { ...props, ref, id: ownId ?? (field === null ? void 0 : control.id), "aria-describedby": control.describedBy, "aria-invalid": ariaInvalid ?? (control.invalid || void 0), ...requiredAttribute === void 0 ? {} : { "aria-required": requiredAttribute }, className: cx("cdt-select__trigger", size === "sm" && "cdt-select__trigger--sm", control.invalid && "cdt-select__trigger--invalid", className) });
77
+ });
78
+ SelectTrigger.displayName = "Select.Trigger";
79
+ const SelectContent = forwardRef(function SelectContent2({ children, className, ...props }, ref) {
80
+ return /* @__PURE__ */ jsx(RadixSelect.Portal, { children: /* @__PURE__ */ jsx(RadixSelect.Content, { ...props, ref, className: cx("cdt-select__content", className), children: /* @__PURE__ */ jsx(RadixSelect.Viewport, { children }) }) });
81
+ });
82
+ SelectContent.displayName = "Select.Content";
83
+ const SelectItem = forwardRef(function SelectItem2({ children, className, indicator = "\u2713", ...props }, ref) {
84
+ return /* @__PURE__ */ jsxs(RadixSelect.Item, { ...props, ref, className: cx("cdt-select__item", className), children: [
85
+ /* @__PURE__ */ jsx(RadixSelect.ItemText, { children }),
86
+ /* @__PURE__ */ jsx(RadixSelect.ItemIndicator, { className: "cdt-select__indicator", "aria-hidden": "true", children: indicator })
87
+ ] });
88
+ });
89
+ SelectItem.displayName = "Select.Item";
90
+ function SelectRoot({ required, ...props }) {
91
+ const field = useContext(FieldContext);
92
+ return /* @__PURE__ */ jsx(RadixSelect.Root, { ...props, required: required ?? field?.required });
93
+ }
94
+ SelectRoot.displayName = "Select.Root";
95
+ const Select = { Root: SelectRoot, Trigger: SelectTrigger, Value: RadixSelect.Value, Content: SelectContent, Item: SelectItem, Group: RadixSelect.Group, Label: RadixSelect.Label };
96
+ const Switch = forwardRef(function Switch2({ className, ...props }, ref) {
97
+ const field = useContext(FieldContext);
98
+ const control = useFieldControl(void 0, props["aria-describedby"]);
99
+ return /* @__PURE__ */ jsx(RadixSwitch.Root, { ...props, ref, id: props.id ?? (field === null ? void 0 : control.id), "aria-describedby": control.describedBy, "aria-invalid": control.invalid || void 0, required: props.required ?? control.required, className: cx("cdt-switch", control.invalid && "cdt-switch--invalid", className), children: /* @__PURE__ */ jsx(RadixSwitch.Thumb, { className: "cdt-switch__thumb" }) });
100
+ });
101
+ Switch.displayName = "Switch";
102
+ const Checkbox = forwardRef(function Checkbox2({ className, indicator = "\u2713", ...props }, ref) {
103
+ const field = useContext(FieldContext);
104
+ const control = useFieldControl(void 0, props["aria-describedby"]);
105
+ return /* @__PURE__ */ jsx(RadixCheckbox.Root, { ...props, ref, id: props.id ?? (field === null ? void 0 : control.id), "aria-describedby": control.describedBy, "aria-invalid": control.invalid || void 0, required: props.required ?? control.required, className: cx("cdt-checkbox", control.invalid && "cdt-checkbox--invalid", className), children: /* @__PURE__ */ jsx(RadixCheckbox.Indicator, { className: "cdt-checkbox__indicator", "aria-hidden": "true", children: indicator }) });
106
+ });
107
+ Checkbox.displayName = "Checkbox";
108
+ export {
109
+ Checkbox,
110
+ Field,
111
+ Select,
112
+ Switch,
113
+ TextArea,
114
+ TextField
115
+ };
package/dist/index.d.ts CHANGED
@@ -7,7 +7,10 @@ export { CodeBlock, CodeBlockProps, Kbd, KbdProps, Table, TableCellProps, TableH
7
7
  export { Dialog, DialogContentProps, Drawer, DrawerContentProps, DropdownMenu, DropdownMenuItemProps, Tooltip, TooltipContentProps } from './overlay.js';
8
8
  export { Checkbox, CheckboxProps, Field, FieldProps, Select, SelectItemProps, SelectTriggerProps, Switch, SwitchProps, TextArea, TextAreaProps, TextField, TextFieldProps } from './form.js';
9
9
  export { Banner, BannerProps, EmptyState, EmptyStateProps, Meter, MeterProps, ProgressRing, ProgressRingProps, Spinner, SpinnerProps } from './feedback.js';
10
- export { AppShell, AppShellProps, NavItem, NavLinkRenderProps, NavList, NavListProps, TopBar, TopBarProps } from './shell.js';
10
+ export { AppShell, AppShellNavTrigger, AppShellProps, NavItem, NavLinkRenderProps, NavList, NavListProps, TopBar, TopBarProps } from './shell.js';
11
+ export { Breadcrumb, Combobox, ComboboxProps, CopyButton, CopyButtonProps, DataColumn, DataSort, DataTable, DataTableProps, DetailInspector, DetailInspectorProps, FilterChip, FilterChipProps, FilterToolbar, MultiSelect, MultiSelectProps, PathItem, PathList, PathListProps, ProcessingStage, ProcessingStatus, ProcessingStatusProps, SearchOption, Skeleton, WorkbenchLayout, WorkbenchLayoutProps } from './workbench.js';
12
+ export { Collapsible, Popover, PopoverContentProps, Tabs, TabsContentProps, TabsRootProps } from './interaction.js';
13
+ export { RelationEdge, RelationGraph, RelationGraphProps, RelationNode, RelationSelection, RelationViewport, layoutRelationNodes } from './relation.js';
11
14
  import 'react';
12
15
  import '@radix-ui/react-dialog';
13
16
  import '@radix-ui/react-dropdown-menu';
@@ -15,6 +18,9 @@ import '@radix-ui/react-tooltip';
15
18
  import '@radix-ui/react-checkbox';
16
19
  import '@radix-ui/react-select';
17
20
  import '@radix-ui/react-switch';
21
+ import '@radix-ui/react-tabs';
22
+ import '@radix-ui/react-popover';
23
+ import '@radix-ui/react-collapsible';
18
24
 
19
25
  /** Published name of this package. */
20
26
  declare const PACKAGE_NAME: "@conductor-by-89soone/react";