panelui-web 0.2.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.
@@ -8,7 +8,12 @@
8
8
  * <CommandDock.Panel>
9
9
  * <CommandDock.Input placeholder="What do you want to find?" />
10
10
  * <CommandDock.Results>
11
- * {rows.map((row) => <CommandDock.Row key={row.id}>{row.text}</CommandDock.Row>)}
11
+ * {rows.map((row) => (
12
+ * <CommandDock.Row key={row.id}>
13
+ * <CommandDock.RowTitle>{row.title}</CommandDock.RowTitle>
14
+ * <CommandDock.RowMeta>{row.where}</CommandDock.RowMeta>
15
+ * </CommandDock.Row>
16
+ * ))}
12
17
  * </CommandDock.Results>
13
18
  * </CommandDock.Panel>
14
19
  * </CommandDock>
@@ -99,6 +104,20 @@ declare function CommandDockRow({ className, onSelect, children, ...props }: Com
99
104
  declare namespace CommandDockRow {
100
105
  var displayName: string;
101
106
  }
107
+ export interface CommandDockRowTitleProps extends ComponentProps<'span'> {
108
+ }
109
+ /** What a row is, in a line that takes the row's spare width and truncates. */
110
+ declare function CommandDockRowTitle({ className, ...props }: CommandDockRowTitleProps): import("react").JSX.Element;
111
+ declare namespace CommandDockRowTitle {
112
+ var displayName: string;
113
+ }
114
+ export interface CommandDockRowMetaProps extends ComponentProps<'span'> {
115
+ }
116
+ /** Something short at the end of a row, quieter than its title: a date, a place. */
117
+ declare function CommandDockRowMeta({ className, ...props }: CommandDockRowMetaProps): import("react").JSX.Element;
118
+ declare namespace CommandDockRowMeta {
119
+ var displayName: string;
120
+ }
102
121
  export interface CommandDockStatusProps extends ComponentProps<'div'> {
103
122
  children?: ReactNode;
104
123
  }
@@ -113,6 +132,8 @@ export declare const CommandDock: typeof CommandDockRoot & {
113
132
  Input: typeof CommandDockInput;
114
133
  Results: typeof CommandDockResults;
115
134
  Row: typeof CommandDockRow;
135
+ RowTitle: typeof CommandDockRowTitle;
136
+ RowMeta: typeof CommandDockRowMeta;
116
137
  Status: typeof CommandDockStatus;
117
138
  };
118
- export { CommandDockTrigger, CommandDockPanel, CommandDockInput, CommandDockResults, CommandDockRow, CommandDockStatus, };
139
+ export { CommandDockTrigger, CommandDockPanel, CommandDockInput, CommandDockResults, CommandDockRow, CommandDockRowTitle, CommandDockRowMeta, CommandDockStatus, };
@@ -10,7 +10,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  * <CommandDock.Panel>
11
11
  * <CommandDock.Input placeholder="What do you want to find?" />
12
12
  * <CommandDock.Results>
13
- * {rows.map((row) => <CommandDock.Row key={row.id}>{row.text}</CommandDock.Row>)}
13
+ * {rows.map((row) => (
14
+ * <CommandDock.Row key={row.id}>
15
+ * <CommandDock.RowTitle>{row.title}</CommandDock.RowTitle>
16
+ * <CommandDock.RowMeta>{row.where}</CommandDock.RowMeta>
17
+ * </CommandDock.Row>
18
+ * ))}
14
19
  * </CommandDock.Results>
15
20
  * </CommandDock.Panel>
16
21
  * </CommandDock>
@@ -63,6 +68,9 @@ const commandDockVariants = tv({
63
68
  resultsClip: 'min-h-0 overflow-hidden',
64
69
  resultsInner: 'flex flex-col gap-0.5 border-t p-1.5',
65
70
  row: 'flex items-center gap-3 rounded-xl px-2.5 py-2 text-sm transition-[opacity,translate] duration-300 ease-out starting:translate-y-1.5 starting:opacity-0 motion-reduce:transition-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground',
71
+ // A row's subject takes the room and truncates; its meta keeps its width.
72
+ rowTitle: 'min-w-0 flex-1 truncate font-medium',
73
+ rowMeta: 'shrink-0 text-muted-foreground',
66
74
  status: 'flex items-center gap-2 px-2.5 py-2 text-sm text-muted-foreground',
67
75
  dots: 'flex gap-1',
68
76
  dot: 'size-1 animate-pulse rounded-full bg-muted-foreground',
@@ -220,6 +228,16 @@ function CommandDockRow({ className, onSelect, children, ...props }) {
220
228
  return (_jsx("div", { role: "listitem", ...props, children: onSelect ? (_jsx("button", { type: "button", onClick: onSelect, className: slots.row({ className: ['w-full text-start', className] }), children: children })) : (_jsx("div", { className: slots.row({ className }), children: children })) }));
221
229
  }
222
230
  CommandDockRow.displayName = 'CommandDock.Row';
231
+ /** What a row is, in a line that takes the row's spare width and truncates. */
232
+ function CommandDockRowTitle({ className, ...props }) {
233
+ return (_jsx("span", { "data-slot": "command-dock-row-title", className: commandDockVariants().rowTitle({ className }), ...props }));
234
+ }
235
+ CommandDockRowTitle.displayName = 'CommandDock.RowTitle';
236
+ /** Something short at the end of a row, quieter than its title: a date, a place. */
237
+ function CommandDockRowMeta({ className, ...props }) {
238
+ return (_jsx("span", { "data-slot": "command-dock-row-meta", className: commandDockVariants().rowMeta({ className }), ...props }));
239
+ }
240
+ CommandDockRowMeta.displayName = 'CommandDock.RowMeta';
223
241
  /** Shown under the rows while `busy`: three dots, and words if you give it some. */
224
242
  function CommandDockStatus({ className, children = 'Working', ...props }) {
225
243
  const slots = commandDockVariants();
@@ -232,8 +250,10 @@ export const CommandDock = Object.assign(CommandDockRoot, {
232
250
  Input: CommandDockInput,
233
251
  Results: CommandDockResults,
234
252
  Row: CommandDockRow,
253
+ RowTitle: CommandDockRowTitle,
254
+ RowMeta: CommandDockRowMeta,
235
255
  Status: CommandDockStatus,
236
256
  });
237
257
  /* By name as well, for a Server Component, which cannot dot into a client
238
258
  module. */
239
- export { CommandDockTrigger, CommandDockPanel, CommandDockInput, CommandDockResults, CommandDockRow, CommandDockStatus, };
259
+ export { CommandDockTrigger, CommandDockPanel, CommandDockInput, CommandDockResults, CommandDockRow, CommandDockRowTitle, CommandDockRowMeta, CommandDockStatus, };
@@ -81,7 +81,51 @@ declare function DayGridLegend({ color, less, more, className, ...props }: DayGr
81
81
  declare namespace DayGridLegend {
82
82
  var displayName: string;
83
83
  }
84
+ export interface DayGridWeekdaysProps extends Omit<ComponentProps<'div'>, 'children'> {
85
+ /**
86
+ * The name written beside each row, first row first. An empty string leaves
87
+ * a row unnamed. Default Monday to Sunday with every other name left out.
88
+ */
89
+ labels?: string[];
90
+ }
91
+ /**
92
+ * The days of the week down the side of a grid that runs a week to a column.
93
+ * Put it beside the grid, in a row with it. Hidden from screen readers, which
94
+ * are given each day's own label.
95
+ */
96
+ declare function DayGridWeekdays({ labels, className, style, ...props }: DayGridWeekdaysProps): import("react").JSX.Element;
97
+ declare namespace DayGridWeekdays {
98
+ var displayName: string;
99
+ }
100
+ export interface DayGridCaptionProps extends ComponentProps<'div'> {
101
+ }
102
+ /**
103
+ * What the picked day holds, written out beside the grid: a `DayGrid.Title`
104
+ * over a `DayGrid.Description`. Read out politely each time the pick changes.
105
+ */
106
+ declare function DayGridCaption({ className, ...props }: DayGridCaptionProps): import("react").JSX.Element;
107
+ declare namespace DayGridCaption {
108
+ var displayName: string;
109
+ }
110
+ export interface DayGridTitleProps extends ComponentProps<'p'> {
111
+ }
112
+ /** The picked day's figure: "17 merged". */
113
+ declare function DayGridTitle({ className, ...props }: DayGridTitleProps): import("react").JSX.Element;
114
+ declare namespace DayGridTitle {
115
+ var displayName: string;
116
+ }
117
+ export interface DayGridDescriptionProps extends ComponentProps<'p'> {
118
+ }
119
+ /** The picked day's date, quieter, under its figure. */
120
+ declare function DayGridDescription({ className, ...props }: DayGridDescriptionProps): import("react").JSX.Element;
121
+ declare namespace DayGridDescription {
122
+ var displayName: string;
123
+ }
84
124
  export declare const DayGrid: typeof DayGridRoot & {
85
125
  Legend: typeof DayGridLegend;
126
+ Weekdays: typeof DayGridWeekdays;
127
+ Caption: typeof DayGridCaption;
128
+ Title: typeof DayGridTitle;
129
+ Description: typeof DayGridDescription;
86
130
  };
87
- export { DayGridLegend };
131
+ export { DayGridLegend, DayGridWeekdays, DayGridCaption, DayGridTitle, DayGridDescription };
@@ -46,6 +46,13 @@ const dayGridVariants = tv({
46
46
  dot: 'block size-full rounded-full [scale:calc(var(--pu-size)*(1+var(--pu-lens,0)*0.5)*var(--pu-in,1))] transition-[scale,box-shadow] duration-200 ease-[cubic-bezier(0.34,1.56,0.64,1)] motion-reduce:transition-none',
47
47
  tag: 'pointer-events-none absolute z-20 whitespace-nowrap rounded-lg bg-foreground px-2 py-1 text-xs font-medium text-background shadow-lg',
48
48
  legend: 'flex items-center gap-1.5 text-xs text-muted-foreground',
49
+ // One row per day, matching the grid's own rows, so each name sits level with its days.
50
+ weekdays: 'grid gap-1 text-xs leading-none text-muted-foreground',
51
+ weekday: 'flex items-center',
52
+ // The picked day, written out under the grid.
53
+ caption: 'flex flex-col gap-0.5',
54
+ title: 'text-sm font-medium',
55
+ description: 'text-sm text-muted-foreground',
49
56
  swatch: 'flex size-4 items-center justify-center',
50
57
  },
51
58
  variants: {
@@ -243,9 +250,41 @@ function DayGridLegend({ color = 'primary', less = 'Less', more = 'More', classN
243
250
  return (_jsxs("div", { className: slots.legend({ className }), ...props, children: [_jsx("span", { children: less }), LEVELS.map((level) => (_jsx("span", { "aria-hidden": "true", className: slots.swatch(), children: _jsx("span", { className: dayGridVariants({ level }).dot({ className: 'shadow-none' }) }) }, level))), _jsx("span", { children: more })] }));
244
251
  }
245
252
  DayGridLegend.displayName = 'DayGrid.Legend';
253
+ /**
254
+ * The days of the week down the side of a grid that runs a week to a column.
255
+ * Put it beside the grid, in a row with it. Hidden from screen readers, which
256
+ * are given each day's own label.
257
+ */
258
+ function DayGridWeekdays({ labels = ['Mon', '', 'Wed', '', 'Fri', '', 'Sun'], className, style, ...props }) {
259
+ const slots = dayGridVariants();
260
+ return (_jsx("div", { "aria-hidden": "true", "data-slot": "day-grid-weekdays", className: slots.weekdays({ className }), style: { gridTemplateRows: `repeat(${labels.length}, minmax(0, 1fr))`, ...style }, ...props, children: labels.map((day, index) => (_jsx("span", { className: slots.weekday(), children: day }, index))) }));
261
+ }
262
+ DayGridWeekdays.displayName = 'DayGrid.Weekdays';
263
+ /**
264
+ * What the picked day holds, written out beside the grid: a `DayGrid.Title`
265
+ * over a `DayGrid.Description`. Read out politely each time the pick changes.
266
+ */
267
+ function DayGridCaption({ className, ...props }) {
268
+ return (_jsx("div", { "aria-live": "polite", "data-slot": "day-grid-caption", className: dayGridVariants().caption({ className }), ...props }));
269
+ }
270
+ DayGridCaption.displayName = 'DayGrid.Caption';
271
+ /** The picked day's figure: "17 merged". */
272
+ function DayGridTitle({ className, ...props }) {
273
+ return _jsx("p", { "data-slot": "day-grid-title", className: dayGridVariants().title({ className }), ...props });
274
+ }
275
+ DayGridTitle.displayName = 'DayGrid.Title';
276
+ /** The picked day's date, quieter, under its figure. */
277
+ function DayGridDescription({ className, ...props }) {
278
+ return (_jsx("p", { "data-slot": "day-grid-description", className: dayGridVariants().description({ className }), ...props }));
279
+ }
280
+ DayGridDescription.displayName = 'DayGrid.Description';
246
281
  export const DayGrid = Object.assign(DayGridRoot, {
247
282
  Legend: DayGridLegend,
283
+ Weekdays: DayGridWeekdays,
284
+ Caption: DayGridCaption,
285
+ Title: DayGridTitle,
286
+ Description: DayGridDescription,
248
287
  });
249
288
  /* By name as well, for a Server Component, which cannot dot into a client
250
289
  module. */
251
- export { DayGridLegend };
290
+ export { DayGridLegend, DayGridWeekdays, DayGridCaption, DayGridTitle, DayGridDescription };
@@ -95,6 +95,52 @@ declare function FeedbackField({ value, onValueChange, minHeight, maxHeight, cla
95
95
  declare namespace FeedbackField {
96
96
  var displayName: string;
97
97
  }
98
+ export interface FeedbackDescriptionProps extends ComponentProps<'p'> {
99
+ }
100
+ /**
101
+ * Body text in the panel, in place of the field: a thank-you once the message
102
+ * has gone. In a dialog it is also what the dialog is described as.
103
+ */
104
+ declare function FeedbackDescription({ className, ...props }: FeedbackDescriptionProps): import("react").JSX.Element;
105
+ declare namespace FeedbackDescription {
106
+ var displayName: string;
107
+ }
108
+ export interface FeedbackMessageProps extends ComponentProps<'p'> {
109
+ /**
110
+ * `muted` for a running note, read out politely as it changes: how much is
111
+ * left to write. `destructive` for a failure, announced at once. Default
112
+ * `muted`.
113
+ */
114
+ tone?: 'muted' | 'destructive';
115
+ }
116
+ /** A short line under the field. Figures in it keep their width as they change. */
117
+ declare function FeedbackMessage({ tone, className, ...props }: FeedbackMessageProps): import("react").JSX.Element;
118
+ declare namespace FeedbackMessage {
119
+ var displayName: string;
120
+ }
121
+ export interface FeedbackRatingProps extends Omit<ComponentProps<'div'>, 'children'> {
122
+ /** The highest score. The scores run from 1 to this. Default 5. */
123
+ max?: number;
124
+ /** Called with the score that was pressed. */
125
+ onValueChange?: (value: number) => void;
126
+ /** The word under the lowest score. */
127
+ startLabel?: ReactNode;
128
+ /** The word under the highest score. */
129
+ endLabel?: ReactNode;
130
+ /**
131
+ * The least height it takes, in pixels. Match the field's `minHeight` on the
132
+ * step that follows, so the dialog keeps its size between the two.
133
+ */
134
+ minHeight?: number;
135
+ }
136
+ /**
137
+ * A row of round buttons, one per score, with a word under each end. Use it
138
+ * as the first step of a dialog, and ask for the words once a score is in.
139
+ */
140
+ declare function FeedbackRating({ max, onValueChange, startLabel, endLabel, minHeight, className, style, ...props }: FeedbackRatingProps): import("react").JSX.Element;
141
+ declare namespace FeedbackRating {
142
+ var displayName: string;
143
+ }
98
144
  export interface FeedbackFooterProps extends ComponentProps<'div'> {
99
145
  }
100
146
  /** The action row, in the band under the panel. */
@@ -128,8 +174,11 @@ export declare const Feedback: typeof FeedbackRoot & {
128
174
  Title: typeof FeedbackTitle;
129
175
  Close: typeof FeedbackClose;
130
176
  Field: typeof FeedbackField;
177
+ Description: typeof FeedbackDescription;
178
+ Message: typeof FeedbackMessage;
179
+ Rating: typeof FeedbackRating;
131
180
  Footer: typeof FeedbackFooter;
132
181
  Cancel: typeof FeedbackCancel;
133
182
  Submit: typeof FeedbackSubmit;
134
183
  };
135
- export { FeedbackTrigger, FeedbackContent, FeedbackPanel, FeedbackTitle, FeedbackClose, FeedbackField, FeedbackFooter, FeedbackCancel, FeedbackSubmit, };
184
+ export { FeedbackTrigger, FeedbackContent, FeedbackPanel, FeedbackTitle, FeedbackClose, FeedbackField, FeedbackDescription, FeedbackMessage, FeedbackRating, FeedbackFooter, FeedbackCancel, FeedbackSubmit, };
@@ -84,6 +84,14 @@ const feedbackVariants = tv({
84
84
  // over the ends of its lines.
85
85
  scrollbar: 'my-1 -me-3.5 flex w-1.5 opacity-0 transition-opacity delay-300 data-hovering:opacity-100 data-hovering:delay-0 data-hovering:duration-100 data-scrolling:opacity-100 data-scrolling:delay-0 data-scrolling:duration-100',
86
86
  thumb: 'relative flex-1 rounded-full bg-foreground/20',
87
+ // Body text in the well, set like what is written in the field.
88
+ description: 'text-[17px] leading-6 text-muted-foreground',
89
+ // A line under the field: how much is left to write, or what went wrong.
90
+ message: 'text-sm tabular-nums',
91
+ rating: 'flex flex-col justify-center gap-3',
92
+ ratingScores: 'flex justify-between gap-2',
93
+ ratingScore: 'flex size-12 cursor-pointer items-center justify-center rounded-full bg-foreground/10 text-lg font-semibold transition-colors hover:bg-primary hover:text-primary-foreground',
94
+ ratingEnds: 'flex justify-between text-sm text-muted-foreground',
87
95
  // Held in from the shell's edge further than the panel is. A row the full
88
96
  // width would read as a third edge of the dialog, not as two buttons.
89
97
  footer: 'flex items-center gap-3.5 px-[18px] pb-2.5 pt-4',
@@ -102,6 +110,10 @@ const feedbackVariants = tv({
102
110
  disabled: {
103
111
  true: { action: 'cursor-not-allowed active:scale-100' },
104
112
  },
113
+ messageTone: {
114
+ muted: { message: 'text-muted-foreground' },
115
+ destructive: { message: 'text-destructive' },
116
+ },
105
117
  },
106
118
  compoundVariants: [
107
119
  // Fainter than Cancel's own tint on purpose. Matched, they would read as
@@ -233,6 +245,33 @@ function FeedbackField({ value, onValueChange, minHeight = 200, maxHeight = 360,
233
245
  }, onKeyDown: handleKeyDown, className: slots.field({ className }), style: { minHeight, ...style }, ...props }) }) }), _jsx(ScrollArea.Scrollbar, { orientation: "vertical", className: slots.scrollbar(), children: _jsx(ScrollArea.Thumb, { className: slots.thumb() }) })] }));
234
246
  }
235
247
  FeedbackField.displayName = 'Feedback.Field';
248
+ /**
249
+ * Body text in the panel, in place of the field: a thank-you once the message
250
+ * has gone. In a dialog it is also what the dialog is described as.
251
+ */
252
+ function FeedbackDescription({ className, ...props }) {
253
+ const { inline } = useFeedback('Feedback.Description');
254
+ const slots = feedbackVariants();
255
+ if (inline)
256
+ return _jsx("p", { className: slots.description({ className }), ...props });
257
+ return _jsx(Dialog.Description, { className: slots.description({ className }), ...props });
258
+ }
259
+ FeedbackDescription.displayName = 'Feedback.Description';
260
+ /** A short line under the field. Figures in it keep their width as they change. */
261
+ function FeedbackMessage({ tone = 'muted', className, ...props }) {
262
+ const slots = feedbackVariants({ messageTone: tone });
263
+ return (_jsx("p", { role: tone === 'destructive' ? 'alert' : undefined, "aria-live": tone === 'destructive' ? undefined : 'polite', className: slots.message({ className }), ...props }));
264
+ }
265
+ FeedbackMessage.displayName = 'Feedback.Message';
266
+ /**
267
+ * A row of round buttons, one per score, with a word under each end. Use it
268
+ * as the first step of a dialog, and ask for the words once a score is in.
269
+ */
270
+ function FeedbackRating({ max = 5, onValueChange, startLabel, endLabel, minHeight, className, style, ...props }) {
271
+ const slots = feedbackVariants();
272
+ return (_jsxs("div", { className: slots.rating({ className }), style: { minHeight, ...style }, ...props, children: [_jsx("div", { className: slots.ratingScores(), children: Array.from({ length: max }, (_, index) => index + 1).map((value) => (_jsx("button", { type: "button", "aria-label": `${value} out of ${max}`, onClick: () => onValueChange?.(value), className: slots.ratingScore(), children: value }, value))) }), startLabel !== undefined || endLabel !== undefined ? (_jsxs("div", { className: slots.ratingEnds(), children: [_jsx("span", { children: startLabel }), _jsx("span", { children: endLabel })] })) : null] }));
273
+ }
274
+ FeedbackRating.displayName = 'Feedback.Rating';
236
275
  /** The action row, in the band under the panel. */
237
276
  function FeedbackFooter({ className, ...props }) {
238
277
  const slots = feedbackVariants();
@@ -274,10 +313,13 @@ export const Feedback = Object.assign(FeedbackRoot, {
274
313
  Title: FeedbackTitle,
275
314
  Close: FeedbackClose,
276
315
  Field: FeedbackField,
316
+ Description: FeedbackDescription,
317
+ Message: FeedbackMessage,
318
+ Rating: FeedbackRating,
277
319
  Footer: FeedbackFooter,
278
320
  Cancel: FeedbackCancel,
279
321
  Submit: FeedbackSubmit,
280
322
  });
281
323
  /* The parts by name, for a Server Component, which cannot dot into a client
282
324
  module. `<FeedbackContent>` there is `<Feedback.Content>` everywhere else. */
283
- export { FeedbackTrigger, FeedbackContent, FeedbackPanel, FeedbackTitle, FeedbackClose, FeedbackField, FeedbackFooter, FeedbackCancel, FeedbackSubmit, };
325
+ export { FeedbackTrigger, FeedbackContent, FeedbackPanel, FeedbackTitle, FeedbackClose, FeedbackField, FeedbackDescription, FeedbackMessage, FeedbackRating, FeedbackFooter, FeedbackCancel, FeedbackSubmit, };
@@ -4,7 +4,12 @@
4
4
  * ```tsx
5
5
  * <Flow>
6
6
  * <Flow.Background variant="dots" />
7
- * <Flow.Node id="source" position={{ x: 24, y: 40 }}>…</Flow.Node>
7
+ * <Flow.Node id="source" position={{ x: 24, y: 40 }}>
8
+ * <Flow.NodeContent>
9
+ * <Flow.NodeTitle>Source</Flow.NodeTitle>
10
+ * <Flow.NodeDescription>main</Flow.NodeDescription>
11
+ * </Flow.NodeContent>
12
+ * </Flow.Node>
8
13
  * <Flow.Node id="build" position={{ x: 24, y: 200 }}>…</Flow.Node>
9
14
  * <Flow.Edge from="source" to="build" dashed animated arrow />
10
15
  * <Flow.Controls />
@@ -128,7 +133,7 @@ export interface FlowNodeProps extends Omit<ComponentProps<'div'>, 'id'> {
128
133
  /** How far one arrow key press moves it, in pixels. Shift moves four times as far. */
129
134
  moveStep?: number;
130
135
  }
131
- /** A box on the graph. Whatever is inside it is yours. */
136
+ /** A box on the graph. Fill it with the node parts below, or with anything of your own. */
132
137
  declare function FlowNode({ id, position, draggable, selected, onPress, onDelete, moveStep, className, style, children, ...props }: FlowNodeProps): import("react").JSX.Element;
133
138
  declare namespace FlowNode {
134
139
  var displayName: string;
@@ -145,6 +150,53 @@ declare function FlowGroup({ id, position, draggable, selected: _selected, onPre
145
150
  declare namespace FlowGroup {
146
151
  var displayName: string;
147
152
  }
153
+ export interface FlowNodeHeaderProps extends ComponentProps<'div'> {
154
+ }
155
+ /** A strip across the top of a node, ruled off from the rest: a step number and a state. */
156
+ declare function FlowNodeHeader({ className, ...props }: FlowNodeHeaderProps): import("react").JSX.Element;
157
+ declare namespace FlowNodeHeader {
158
+ var displayName: string;
159
+ }
160
+ export interface FlowNodeContentProps extends ComponentProps<'div'> {
161
+ }
162
+ /** The body of a node, with the node's padding: a title over a description. */
163
+ declare function FlowNodeContent({ className, ...props }: FlowNodeContentProps): import("react").JSX.Element;
164
+ declare namespace FlowNodeContent {
165
+ var displayName: string;
166
+ }
167
+ export interface FlowNodeTitleProps extends ComponentProps<'p'> {
168
+ }
169
+ /** What the node is. */
170
+ declare function FlowNodeTitle({ className, ...props }: FlowNodeTitleProps): import("react").JSX.Element;
171
+ declare namespace FlowNodeTitle {
172
+ var displayName: string;
173
+ }
174
+ export interface FlowNodeDescriptionProps extends ComponentProps<'p'> {
175
+ }
176
+ /** A quieter line about the node. Smaller inside a `Flow.GroupItem`. */
177
+ declare function FlowNodeDescription({ className, ...props }: FlowNodeDescriptionProps): import("react").JSX.Element;
178
+ declare namespace FlowNodeDescription {
179
+ var displayName: string;
180
+ }
181
+ export interface FlowNodeBadgeProps extends Omit<ComponentProps<'span'>, 'color'> {
182
+ /** Which of the theme's roles it is tinted with. `muted` is grey. Default `muted`. */
183
+ color?: FlowColor | 'muted';
184
+ }
185
+ /** A word in a pill, for a node's state: done, now, waiting. */
186
+ declare function FlowNodeBadge({ color, className, ...props }: FlowNodeBadgeProps): import("react").JSX.Element;
187
+ declare namespace FlowNodeBadge {
188
+ var displayName: string;
189
+ }
190
+ export interface FlowGroupItemProps extends ComponentProps<'div'> {
191
+ }
192
+ /**
193
+ * One thing inside a `Flow.Group` that is not a node of its own: an icon
194
+ * beside a `Flow.NodeContent`. It moves with the group and edges cannot join it.
195
+ */
196
+ declare function FlowGroupItem({ className, ...props }: FlowGroupItemProps): import("react").JSX.Element;
197
+ declare namespace FlowGroupItem {
198
+ var displayName: string;
199
+ }
148
200
  export interface FlowHandleProps extends Omit<ComponentProps<'span'>, 'id'> {
149
201
  /** A name for this handle, so an edge can be joined to it and not only to the node. */
150
202
  id?: string;
@@ -228,8 +280,14 @@ export declare const Flow: typeof FlowRoot & {
228
280
  Handle: typeof FlowHandle;
229
281
  Edge: typeof FlowEdge;
230
282
  Group: typeof FlowGroup;
283
+ GroupItem: typeof FlowGroupItem;
284
+ NodeHeader: typeof FlowNodeHeader;
285
+ NodeContent: typeof FlowNodeContent;
286
+ NodeTitle: typeof FlowNodeTitle;
287
+ NodeDescription: typeof FlowNodeDescription;
288
+ NodeBadge: typeof FlowNodeBadge;
231
289
  Controls: typeof FlowControls;
232
290
  MiniMap: typeof FlowMiniMap;
233
291
  };
234
- export { FlowBackground, FlowNode, FlowHandle, FlowEdge, FlowGroup, FlowControls, FlowMiniMap };
292
+ export { FlowBackground, FlowNode, FlowHandle, FlowEdge, FlowGroup, FlowGroupItem, FlowNodeHeader, FlowNodeContent, FlowNodeTitle, FlowNodeDescription, FlowNodeBadge, FlowControls, FlowMiniMap, };
235
293
  export type { FlowSide, FlowRect, FlowPoint };
@@ -6,7 +6,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
6
6
  * ```tsx
7
7
  * <Flow>
8
8
  * <Flow.Background variant="dots" />
9
- * <Flow.Node id="source" position={{ x: 24, y: 40 }}>…</Flow.Node>
9
+ * <Flow.Node id="source" position={{ x: 24, y: 40 }}>
10
+ * <Flow.NodeContent>
11
+ * <Flow.NodeTitle>Source</Flow.NodeTitle>
12
+ * <Flow.NodeDescription>main</Flow.NodeDescription>
13
+ * </Flow.NodeContent>
14
+ * </Flow.Node>
10
15
  * <Flow.Node id="build" position={{ x: 24, y: 200 }}>…</Flow.Node>
11
16
  * <Flow.Edge from="source" to="build" dashed animated arrow />
12
17
  * <Flow.Controls />
@@ -50,6 +55,15 @@ const flowVariants = tv({
50
55
  node: 'absolute left-0 top-0 z-10 cursor-grab rounded-2xl border bg-card text-card-foreground shadow-sm outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-ring active:cursor-grabbing',
51
56
  group: 'absolute left-0 top-0 cursor-grab rounded-3xl border border-dashed bg-muted/40 p-3 pt-8 outline-none focus-visible:ring-2 focus-visible:ring-ring active:cursor-grabbing',
52
57
  groupLabel: 'absolute start-4 top-2.5 text-xs font-medium uppercase tracking-wider text-muted-foreground',
58
+ // What goes inside a node. The padding is on the parts, so a header can
59
+ // draw its rule from edge to edge.
60
+ nodeHeader: 'flex items-center justify-between gap-3 border-b px-4 py-2.5',
61
+ // Inside a group item the item already has its padding.
62
+ nodeContent: 'px-4 py-3 in-data-[slot=flow-group-item]:p-0',
63
+ nodeTitle: 'text-sm font-medium',
64
+ nodeDescription: 'text-sm text-muted-foreground in-data-[slot=flow-group-item]:text-xs',
65
+ nodeBadge: 'rounded-full bg-[color-mix(in_oklab,var(--pu-color)_14%,transparent)] px-2 py-0.5 text-xs font-medium text-[var(--pu-color)]',
66
+ groupItem: 'flex items-center gap-3 rounded-xl border bg-card px-3 py-2.5 [&_svg]:size-4 [&_svg]:text-muted-foreground',
53
67
  handle: 'absolute z-20 size-3 -translate-x-1/2 -translate-y-1/2 cursor-crosshair rounded-full border-2 border-background bg-muted-foreground outline-none transition-[scale,background-color] hover:scale-150 hover:bg-foreground focus-visible:ring-2 focus-visible:ring-ring',
54
68
  edge: 'pointer-events-none absolute left-0 top-0 size-px overflow-visible text-[var(--pu-color)]',
55
69
  controls: 'absolute flex flex-col divide-y divide-border overflow-hidden rounded-xl border bg-card text-card-foreground shadow-sm',
@@ -58,13 +72,16 @@ const flowVariants = tv({
58
72
  },
59
73
  variants: {
60
74
  color: {
61
- primary: { edge: '[--pu-color:var(--primary)]' },
62
- info: { edge: '[--pu-color:var(--info)]' },
63
- success: { edge: '[--pu-color:var(--success)]' },
64
- warning: { edge: '[--pu-color:var(--warning)]' },
65
- destructive: { edge: '[--pu-color:var(--destructive)]' },
75
+ primary: { edge: '[--pu-color:var(--primary)]', nodeBadge: '[--pu-color:var(--primary)]' },
76
+ info: { edge: '[--pu-color:var(--info)]', nodeBadge: '[--pu-color:var(--info)]' },
77
+ success: { edge: '[--pu-color:var(--success)]', nodeBadge: '[--pu-color:var(--success)]' },
78
+ warning: { edge: '[--pu-color:var(--warning)]', nodeBadge: '[--pu-color:var(--warning)]' },
79
+ destructive: { edge: '[--pu-color:var(--destructive)]', nodeBadge: '[--pu-color:var(--destructive)]' },
66
80
  // The colour of a hairline that is meant to be seen but not looked at.
67
- muted: { edge: '[--pu-color:var(--muted-foreground)]' },
81
+ muted: {
82
+ edge: '[--pu-color:var(--muted-foreground)]',
83
+ nodeBadge: 'bg-muted text-muted-foreground',
84
+ },
68
85
  },
69
86
  selected: { true: { node: 'z-20 ring-2 ring-ring' } },
70
87
  position: {
@@ -474,7 +491,7 @@ function useMovable({ id, position, draggable, onPress, onDelete, moveStep, }) {
474
491
  },
475
492
  };
476
493
  }
477
- /** A box on the graph. Whatever is inside it is yours. */
494
+ /** A box on the graph. Fill it with the node parts below, or with anything of your own. */
478
495
  function FlowNode({ id, position, draggable = true, selected = false, onPress, onDelete, moveStep, className, style, children, ...props }) {
479
496
  const movable = useMovable({ id, position, draggable, onPress, onDelete, moveStep });
480
497
  return (_jsx(NodeContext.Provider, { value: id, children: _jsx("div", { ref: movable.element, "data-flow-node": id, tabIndex: 0, "aria-roledescription": "Node", "aria-selected": selected || undefined, className: flowVariants({ selected }).node({ className }), style: { ...movable.style, ...style }, ...movable.handlers, ...props, children: children }) }));
@@ -489,6 +506,39 @@ function FlowGroup({ id, position, draggable = true, selected: _selected, onPres
489
506
  return (_jsx(NodeContext.Provider, { value: id, children: _jsxs("div", { ref: movable.element, "data-flow-node": id, tabIndex: 0, "aria-roledescription": "Group", className: flowVariants().group({ className }), style: { ...movable.style, ...style }, ...movable.handlers, ...props, children: [label !== undefined ? _jsx("span", { className: flowVariants().groupLabel(), children: label }) : null, children] }) }));
490
507
  }
491
508
  FlowGroup.displayName = 'Flow.Group';
509
+ /** A strip across the top of a node, ruled off from the rest: a step number and a state. */
510
+ function FlowNodeHeader({ className, ...props }) {
511
+ return _jsx("div", { "data-slot": "flow-node-header", className: flowVariants().nodeHeader({ className }), ...props });
512
+ }
513
+ FlowNodeHeader.displayName = 'Flow.NodeHeader';
514
+ /** The body of a node, with the node's padding: a title over a description. */
515
+ function FlowNodeContent({ className, ...props }) {
516
+ return _jsx("div", { "data-slot": "flow-node-content", className: flowVariants().nodeContent({ className }), ...props });
517
+ }
518
+ FlowNodeContent.displayName = 'Flow.NodeContent';
519
+ /** What the node is. */
520
+ function FlowNodeTitle({ className, ...props }) {
521
+ return _jsx("p", { "data-slot": "flow-node-title", className: flowVariants().nodeTitle({ className }), ...props });
522
+ }
523
+ FlowNodeTitle.displayName = 'Flow.NodeTitle';
524
+ /** A quieter line about the node. Smaller inside a `Flow.GroupItem`. */
525
+ function FlowNodeDescription({ className, ...props }) {
526
+ return (_jsx("p", { "data-slot": "flow-node-description", className: flowVariants().nodeDescription({ className }), ...props }));
527
+ }
528
+ FlowNodeDescription.displayName = 'Flow.NodeDescription';
529
+ /** A word in a pill, for a node's state: done, now, waiting. */
530
+ function FlowNodeBadge({ color = 'muted', className, ...props }) {
531
+ return (_jsx("span", { "data-slot": "flow-node-badge", className: flowVariants({ color }).nodeBadge({ className }), ...props }));
532
+ }
533
+ FlowNodeBadge.displayName = 'Flow.NodeBadge';
534
+ /**
535
+ * One thing inside a `Flow.Group` that is not a node of its own: an icon
536
+ * beside a `Flow.NodeContent`. It moves with the group and edges cannot join it.
537
+ */
538
+ function FlowGroupItem({ className, ...props }) {
539
+ return _jsx("div", { "data-slot": "flow-group-item", className: flowVariants().groupItem({ className }), ...props });
540
+ }
541
+ FlowGroupItem.displayName = 'Flow.GroupItem';
492
542
  const SIDE_STYLE = {
493
543
  top: (offset) => ({ left: `${offset * 100}%`, top: '0%' }),
494
544
  bottom: (offset) => ({ left: `${offset * 100}%`, top: '100%' }),
@@ -589,9 +639,15 @@ export const Flow = Object.assign(FlowRoot, {
589
639
  Handle: FlowHandle,
590
640
  Edge: FlowEdge,
591
641
  Group: FlowGroup,
642
+ GroupItem: FlowGroupItem,
643
+ NodeHeader: FlowNodeHeader,
644
+ NodeContent: FlowNodeContent,
645
+ NodeTitle: FlowNodeTitle,
646
+ NodeDescription: FlowNodeDescription,
647
+ NodeBadge: FlowNodeBadge,
592
648
  Controls: FlowControls,
593
649
  MiniMap: FlowMiniMap,
594
650
  });
595
651
  /* By name as well, for a Server Component, which cannot dot into a client
596
652
  module. */
597
- export { FlowBackground, FlowNode, FlowHandle, FlowEdge, FlowGroup, FlowControls, FlowMiniMap };
653
+ export { FlowBackground, FlowNode, FlowHandle, FlowEdge, FlowGroup, FlowGroupItem, FlowNodeHeader, FlowNodeContent, FlowNodeTitle, FlowNodeDescription, FlowNodeBadge, FlowControls, FlowMiniMap, };
@@ -44,8 +44,9 @@
44
44
  * ## Rows divide themselves
45
45
  *
46
46
  * The panel draws the hairline between its children, so no row has to know
47
- * whether it is first. A row with `href` is a link and one with `onClick` is a
48
- * button, each with the hover and focus a pressable row should have, and
47
+ * whether it is first. Under `band` it draws none: a figure and its chart are
48
+ * one thing, not two rows. A row with `href` is a link and one with `onClick`
49
+ * is a button, each with the hover and focus a pressable row should have, and
49
50
  * `chevron` marks one that leads somewhere.
50
51
  *
51
52
  * ## It renders on the server
@@ -58,7 +59,8 @@ export type FrameVariant = 'default' | 'band' | 'inset' | 'plain';
58
59
  export interface FrameProps extends ComponentProps<'div'> {
59
60
  /**
60
61
  * `default` sets the panel in a tray. `band` holds a bordered card in a
61
- * thin muted band, for a widget showing one figure or chart. `inset` floats
62
+ * thin muted band, for a widget showing one figure or chart, and its panel
63
+ * draws no rule between its children. `inset` floats
62
64
  * the panel in a recessed band and gives `Frame.Footer` somewhere to sit.
63
65
  * `plain` drops the shell, for a frame inside a container that already draws
64
66
  * its own edge.
@@ -104,7 +106,7 @@ declare namespace FrameAction {
104
106
  }
105
107
  export interface FramePanelProps extends ComponentProps<'div'> {
106
108
  }
107
- /** The card the rows sit on. It draws the hairline between its children. */
109
+ /** The card the rows sit on. It draws the hairline between its children, except under `band`. */
108
110
  declare function FramePanel({ className, ...props }: FramePanelProps): import("react").JSX.Element;
109
111
  declare namespace FramePanel {
110
112
  var displayName: string;
@@ -9,8 +9,11 @@ const frameVariants = tv({
9
9
  slots: {
10
10
  root: 'group/frame flex flex-col text-foreground',
11
11
  header: [
12
- 'grid grid-cols-[minmax(0,1fr)] items-center gap-x-3 gap-y-0.5 px-4 pt-2.5 pb-3',
12
+ 'grid grid-cols-[minmax(0,1fr)] items-center gap-x-3 px-4 pt-2.5 pb-3',
13
13
  'has-data-[slot=frame-action]:grid-cols-[minmax(0,1fr)_auto]',
14
+ // The action spans two rows, so with no description the second is empty
15
+ // and a gap under the title would only add height.
16
+ 'has-data-[slot=frame-description]:gap-y-0.5',
14
17
  'group-data-[variant=inset]/frame:px-3 group-data-[variant=inset]/frame:pt-1 group-data-[variant=inset]/frame:pb-2.5',
15
18
  'group-data-[variant=plain]/frame:px-1',
16
19
  'group-data-[variant=band]/frame:px-3.5 group-data-[variant=band]/frame:py-2.5',
@@ -32,6 +35,8 @@ const frameVariants = tv({
32
35
  // The panel's shape follows the root's variant, read from its data attribute.
33
36
  panel: [
34
37
  'flex flex-col divide-y overflow-hidden bg-card text-card-foreground',
38
+ // A band holds one figure, not a list of rows, so nothing in it is ruled off.
39
+ 'group-data-[variant=band]/frame:divide-y-0',
35
40
  'group-data-[variant=default]/frame:rounded-t-2xl group-data-[variant=default]/frame:border-t',
36
41
  'group-data-[variant=plain]/frame:rounded-3xl group-data-[variant=plain]/frame:border group-data-[variant=plain]/frame:shadow-xs',
37
42
  'group-data-[variant=inset]/frame:rounded-[30px] group-data-[variant=inset]/frame:bg-popover group-data-[variant=inset]/frame:text-popover-foreground group-data-[variant=inset]/frame:shadow-xs',
@@ -109,7 +114,7 @@ function FrameAction({ className, ...props }) {
109
114
  return _jsx("div", { "data-slot": "frame-action", className: frameVariants().action({ className }), ...props });
110
115
  }
111
116
  FrameAction.displayName = 'Frame.Action';
112
- /** The card the rows sit on. It draws the hairline between its children. */
117
+ /** The card the rows sit on. It draws the hairline between its children, except under `band`. */
113
118
  function FramePanel({ className, ...props }) {
114
119
  return _jsx("div", { "data-slot": "frame-panel", className: frameVariants().panel({ className }), ...props });
115
120
  }
@@ -187,9 +187,11 @@ declare namespace KpiSeparator {
187
187
  export interface KpiGroupProps extends ComponentProps<'div'> {
188
188
  /** Side by side with upright rules, or stacked with level ones. */
189
189
  orientation?: 'horizontal' | 'vertical';
190
+ /** Draws the group as a card of its own, for a group not already inside one. */
191
+ surface?: boolean;
190
192
  }
191
193
  /** Several metrics as one panel, with a rule between each and the next. */
192
- declare function KpiGroup({ orientation, className, ...props }: KpiGroupProps): import("react").JSX.Element;
194
+ declare function KpiGroup({ orientation, surface, className, ...props }: KpiGroupProps): import("react").JSX.Element;
193
195
  declare namespace KpiGroup {
194
196
  var displayName: string;
195
197
  }