@you-agent-factory/components 0.0.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.
Files changed (92) hide show
  1. package/README.md +223 -0
  2. package/docs/README.md +83 -0
  3. package/docs/button.md +119 -0
  4. package/docs/charts.md +325 -0
  5. package/docs/data-display-code-panel.md +120 -0
  6. package/docs/feedback-alert-panel.md +117 -0
  7. package/docs/feedback-skeleton.md +54 -0
  8. package/docs/forms-form-field.md +490 -0
  9. package/docs/forms-input-primitives.md +311 -0
  10. package/docs/forms-select-primitives.md +362 -0
  11. package/docs/graphs.md +250 -0
  12. package/docs/layout-display-primitives.md +211 -0
  13. package/docs/overlays.md +414 -0
  14. package/docs/table-data-table.md +331 -0
  15. package/docs/typography-roles.md +182 -0
  16. package/docs/widget-frame-recipes.md +273 -0
  17. package/package.json +161 -0
  18. package/src/category-paths.ts +20 -0
  19. package/src/charts/chart-state-panel.tsx +103 -0
  20. package/src/charts/chart.tsx +287 -0
  21. package/src/charts/index.ts +21 -0
  22. package/src/data-display/code-panel.tsx +67 -0
  23. package/src/data-display/data-table.tsx +180 -0
  24. package/src/data-display/description-list.tsx +24 -0
  25. package/src/data-display/index.ts +34 -0
  26. package/src/data-display/table-layout.ts +12 -0
  27. package/src/data-display/table.tsx +131 -0
  28. package/src/feedback/alert-panel-semantics.ts +75 -0
  29. package/src/feedback/alert-panel.tsx +187 -0
  30. package/src/feedback/index.ts +21 -0
  31. package/src/feedback/skeleton.tsx +16 -0
  32. package/src/forms/index.ts +73 -0
  33. package/src/forms/package-checkbox.tsx +50 -0
  34. package/src/forms/package-enum-select.tsx +185 -0
  35. package/src/forms/package-file-input.tsx +25 -0
  36. package/src/forms/package-form-field.tsx +202 -0
  37. package/src/forms/package-input.tsx +25 -0
  38. package/src/forms/package-native-select.tsx +25 -0
  39. package/src/forms/package-select.tsx +209 -0
  40. package/src/forms/package-textarea.tsx +35 -0
  41. package/src/forms/select-icons.tsx +35 -0
  42. package/src/graphs/graph-edge-path.ts +241 -0
  43. package/src/graphs/graph-edge.tsx +130 -0
  44. package/src/graphs/graph-interactive-example.tsx +187 -0
  45. package/src/graphs/graph-node-button.tsx +51 -0
  46. package/src/graphs/graph-node-handle-badge.tsx +108 -0
  47. package/src/graphs/graph-node-handle.ts +28 -0
  48. package/src/graphs/graph-node-shell.tsx +111 -0
  49. package/src/graphs/graph-node-state-indicator.tsx +47 -0
  50. package/src/graphs/graph-node-state.ts +111 -0
  51. package/src/graphs/graph-viewport-surface.tsx +27 -0
  52. package/src/graphs/index.ts +41 -0
  53. package/src/icons/index.ts +4 -0
  54. package/src/index.ts +209 -0
  55. package/src/layout/action-row.tsx +53 -0
  56. package/src/layout/index.ts +9 -0
  57. package/src/layout/surface-panel.tsx +91 -0
  58. package/src/navigation/index.ts +4 -0
  59. package/src/overlays/collapsible.tsx +22 -0
  60. package/src/overlays/dialog.tsx +148 -0
  61. package/src/overlays/index.ts +31 -0
  62. package/src/overlays/overlay-layout.ts +9 -0
  63. package/src/overlays/popover.tsx +29 -0
  64. package/src/overlays/scroll-area.tsx +78 -0
  65. package/src/primitives/button-link.tsx +22 -0
  66. package/src/primitives/button.tsx +258 -0
  67. package/src/primitives/icon-button-shell.tsx +49 -0
  68. package/src/primitives/index.ts +31 -0
  69. package/src/primitives/package-text.tsx +29 -0
  70. package/src/primitives/typography-roles.ts +12 -0
  71. package/src/primitives/typography.tsx +165 -0
  72. package/src/recipes/index.ts +61 -0
  73. package/src/recipes/widget-frame-content.tsx +114 -0
  74. package/src/recipes/widget-frame-disclosure.tsx +161 -0
  75. package/src/recipes/widget-frame-layout.ts +40 -0
  76. package/src/recipes/widget-frame-skeleton.tsx +16 -0
  77. package/src/recipes/widget-frame-states.tsx +99 -0
  78. package/src/recipes/widget-frame-typography.ts +19 -0
  79. package/src/recipes/widget-frame.tsx +84 -0
  80. package/src/styles/color-palette-presets.css +167 -0
  81. package/src/styles/color-role-tokens.css +163 -0
  82. package/src/styles/layout-role-tokens.css +27 -0
  83. package/src/styles/text-color-role-tokens.css +29 -0
  84. package/src/styles/typography-role-tokens.css +72 -0
  85. package/src/styles/typography-role-utilities.css +73 -0
  86. package/src/styles/typography-roles.css +53 -0
  87. package/src/styles.css +32 -0
  88. package/src/testing/index.ts +13 -0
  89. package/src/testing/render.tsx +16 -0
  90. package/src/tokens/index.ts +4 -0
  91. package/src/utilities/cn.ts +5 -0
  92. package/src/utilities/index.ts +6 -0
@@ -0,0 +1,490 @@
1
+ # Form field structure and messaging
2
+
3
+ Domain-free form-field layout and messaging components for reusable forms. Use
4
+ these components when you need consistent label, description, helper text,
5
+ warning, error, and success presentation with package typography — without
6
+ importing dashboard features, API clients, generated OpenAPI types, or app
7
+ state.
8
+
9
+ Form-field components are **presentation-only**. They render styled labels and
10
+ message regions and forward standard React props. They do not fetch data,
11
+ perform validation decisions, submit forms, or own durable application state.
12
+
13
+ ## Required setup
14
+
15
+ Import package styles once in your host application before rendering form fields:
16
+
17
+ ```css
18
+ @import "@you-agent-factory/components/styles.css";
19
+ ```
20
+
21
+ Form-field messaging depends on package role tokens and Tailwind utility classes
22
+ compiled from those tokens. It does not require dashboard `styles.css`, dashboard
23
+ providers, generated OpenAPI types, React Query, Zustand, or app localization
24
+ context.
25
+
26
+ ## Import paths
27
+
28
+ Prefer the forms category entrypoint for tree-shaking and explicit category
29
+ boundaries:
30
+
31
+ ```ts
32
+ import {
33
+ buildFormFieldAriaDescribedBy,
34
+ FormDescription,
35
+ FormError,
36
+ FormField,
37
+ FormFieldGroup,
38
+ FormFieldGroupLabel,
39
+ FormHelperText,
40
+ FormLabel,
41
+ FormSuccess,
42
+ FormWarning,
43
+ type FormFieldMessageIds,
44
+ } from "@you-agent-factory/components/forms";
45
+ ```
46
+
47
+ The same exports are also available from the package root:
48
+
49
+ ```ts
50
+ import {
51
+ FormField,
52
+ FormLabel,
53
+ buildFormFieldAriaDescribedBy,
54
+ } from "@you-agent-factory/components";
55
+ ```
56
+
57
+ Pair form-field messaging with package input primitives (`PackageInput`,
58
+ `PackageTextarea`, `PackageCheckbox`, `NativeSelect`, `Select`, and related
59
+ controls). See [Form input primitives](./forms-input-primitives.md) and
60
+ [Form select primitives](./forms-select-primitives.md) for control-specific
61
+ behavior.
62
+
63
+ ## Component overview
64
+
65
+ | Component | Role |
66
+ | --- | --- |
67
+ | `FormField` | Vertical spacing wrapper for a single control and its messaging |
68
+ | `FormLabel` | Visible label associated with a control via `htmlFor` |
69
+ | `FormDescription` | Supporting copy below the label (body or supporting typography) |
70
+ | `FormHelperText` | Persistent guidance below the control |
71
+ | `FormWarning` | Cautionary message — no default `role`; pass `status` or `alert` when live-region announcement is desired |
72
+ | `FormError` | Validation error message (defaults to `role="alert"`) |
73
+ | `FormSuccess` | Positive confirmation (defaults to `role="status"`) |
74
+ | `FormFieldGroup` | `fieldset` wrapper for grouped controls |
75
+ | `FormFieldGroupLabel` | `legend` for a grouped field |
76
+ | `buildFormFieldAriaDescribedBy` | Joins host-supplied message element ids into one `aria-describedby` value |
77
+
78
+ ## Host ownership
79
+
80
+ The package owns presentation markup, typography tokens, and message-region
81
+ semantics. Host applications own everything domain-specific:
82
+
83
+ | Concern | Owner |
84
+ | --- | --- |
85
+ | Visible labels, descriptions, and placeholder copy | Host |
86
+ | Helper text, warning text, error text, and success text | Host |
87
+ | `id`, `name`, `required`, `disabled`, and `aria-invalid` on controls | Host |
88
+ | `aria-describedby`, `aria-errormessage`, and `aria-labelledby` wiring | Host |
89
+ | Controlled values, checked state, and change handlers | Host |
90
+ | Validation rules, validation state transitions, and error mapping | Host |
91
+ | Form submission, persistence, and side effects | Host |
92
+ | Required affordances in label copy (for example `*` or “required”) | Host |
93
+ | Data fetching, caching, and durable cross-route state | Host |
94
+
95
+ Pass values, handlers, message copy, and accessibility attributes into
96
+ components as props. Do not expect form-field components to read from your
97
+ routers, stores, or API layers.
98
+
99
+ ## Accessibility relationships
100
+
101
+ Form-field components render message regions with stable `id` values that hosts
102
+ wire to controls. The package does **not** automatically set `aria-describedby`
103
+ or `aria-errormessage` on controls — hosts must pass those attributes to input
104
+ primitives.
105
+
106
+ ### Label and accessible name
107
+
108
+ Associate a visible label with the control:
109
+
110
+ ```tsx
111
+ <FormLabel htmlFor={controlId}>Display name</FormLabel>
112
+ <PackageInput id={controlId} />
113
+ ```
114
+
115
+ Assistive technology reads the accessible name from the label text. For controls
116
+ without a visible label, supply `aria-label` or `aria-labelledby` on the control
117
+ instead.
118
+
119
+ ### Description, helper, warning, and success
120
+
121
+ Include message element ids in `aria-describedby` on the control when those
122
+ messages are present. Use `buildFormFieldAriaDescribedBy` to join ids:
123
+
124
+ ```ts
125
+ const ariaDescribedBy = buildFormFieldAriaDescribedBy({
126
+ descriptionId,
127
+ helperId,
128
+ warningId,
129
+ successId,
130
+ });
131
+ ```
132
+
133
+ Omit ids for messages that are not rendered. When no messages are supplied,
134
+ omit `aria-describedby` entirely.
135
+
136
+ | Message type | Typical `role` | Included in `aria-describedby` |
137
+ | --- | --- | --- |
138
+ | `FormDescription` | (none) | Yes, when rendered |
139
+ | `FormHelperText` | (none) | Yes, when rendered |
140
+ | `FormWarning` | `status` or `alert` (no default — host supplies when needed) | Yes, when rendered |
141
+ | `FormSuccess` | `status` (default) | Yes, when rendered |
142
+ | `FormError` | `alert` (default) | Host choice — see error section |
143
+
144
+ ### Error text and invalid state
145
+
146
+ Expose validation failures without relying on color alone:
147
+
148
+ 1. Set `aria-invalid={true}` on the control when the field fails validation.
149
+ 2. Render error copy in `FormError` with a stable `id`.
150
+ 3. Connect the error to the control with **either**:
151
+ - `aria-errormessage={errorId}` (recommended when error text should not
152
+ duplicate the accessible description), or
153
+ - include `errorId` in `aria-describedby` via `buildFormFieldAriaDescribedBy`.
154
+
155
+ When using `aria-errormessage`, omit `errorId` from
156
+ `buildFormFieldAriaDescribedBy` so error copy is not repeated in the accessible
157
+ description.
158
+
159
+ `FormError` defaults to `role="alert"` so assistive technology announces new
160
+ validation failures.
161
+
162
+ ### Warning text role
163
+
164
+ `FormWarning` does not set a default `role`. Pass `role="status"` for
165
+ persistent cautionary copy that should be discoverable through
166
+ `aria-describedby`, or `role="alert"` when assistive technology should announce
167
+ new warning text immediately. Omit `role` when the warning is purely visual
168
+ supplement to description or helper text.
169
+
170
+ ### Required and disabled state
171
+
172
+ | State | Host responsibility | Package behavior |
173
+ | --- | --- | --- |
174
+ | Required | Set `required` on the native control; add visible required affordance in label copy when your UX requires it | `FormLabel` renders host-supplied label content only |
175
+ | Disabled | Set `disabled` on the native control | Message components remain readable; disabled styling applies on the control |
176
+ | Invalid | Set `aria-invalid={true}` when validation fails | `FormError` uses error-container typography tokens |
177
+
178
+ Required affordances such as asterisks or “(required)” are host-supplied label
179
+ content. Native `required` and `disabled` attributes on controls expose state to
180
+ assistive technology.
181
+
182
+ ### Grouped controls
183
+
184
+ Use `FormFieldGroup` and `FormFieldGroupLabel` for related controls that share
185
+ group context:
186
+
187
+ ```tsx
188
+ <FormFieldGroup aria-describedby={groupDescriptionId}>
189
+ <FormFieldGroupLabel>Notification preferences</FormFieldGroupLabel>
190
+ <FormDescription id={groupDescriptionId}>
191
+ Choose how you want to receive updates.
192
+ </FormDescription>
193
+ <FormField>
194
+ <FormLabel htmlFor={emailId}>Email</FormLabel>
195
+ <PackageCheckbox id={emailId} />
196
+ </FormField>
197
+ <FormField>
198
+ <FormLabel htmlFor={smsId}>Text message</FormLabel>
199
+ <PackageCheckbox id={smsId} />
200
+ </FormField>
201
+ </FormFieldGroup>
202
+ ```
203
+
204
+ `FormFieldGroup` renders a borderless `fieldset`. The group label renders as a
205
+ `legend`. Per-control labels still use `FormLabel` with `htmlFor` pointing at
206
+ each control id. Shared group description ids can be referenced from
207
+ `aria-describedby` on the `fieldset`.
208
+
209
+ ## Minimal examples
210
+
211
+ ### Text field with label and helper text
212
+
213
+ ```tsx
214
+ import { useId } from "react";
215
+ import {
216
+ buildFormFieldAriaDescribedBy,
217
+ FormField,
218
+ FormHelperText,
219
+ FormLabel,
220
+ } from "@you-agent-factory/components/forms";
221
+ import { PackageInput } from "@you-agent-factory/components/forms";
222
+
223
+ export function DisplayNameField() {
224
+ const reactId = useId();
225
+ const controlId = `${reactId}-control`;
226
+ const helperId = `${reactId}-helper`;
227
+
228
+ return (
229
+ <FormField>
230
+ <FormLabel htmlFor={controlId}>Display name</FormLabel>
231
+ <PackageInput
232
+ aria-describedby={buildFormFieldAriaDescribedBy({ helperId })}
233
+ id={controlId}
234
+ />
235
+ <FormHelperText id={helperId}>Use 3 to 40 characters.</FormHelperText>
236
+ </FormField>
237
+ );
238
+ }
239
+ ```
240
+
241
+ ### Required disabled field
242
+
243
+ ```tsx
244
+ import { useId } from "react";
245
+ import {
246
+ FormField,
247
+ FormLabel,
248
+ } from "@you-agent-factory/components/forms";
249
+ import { PackageInput } from "@you-agent-factory/components/forms";
250
+
251
+ export function LockedDisplayNameField() {
252
+ const reactId = useId();
253
+ const controlId = `${reactId}-control`;
254
+
255
+ return (
256
+ <FormField>
257
+ <FormLabel htmlFor={controlId}>
258
+ Display name
259
+ <span className="text-on-error-container"> *</span>
260
+ </FormLabel>
261
+ <PackageInput disabled id={controlId} required value="Example value" />
262
+ </FormField>
263
+ );
264
+ }
265
+ ```
266
+
267
+ ### Error field with `aria-errormessage`
268
+
269
+ ```tsx
270
+ import { useId } from "react";
271
+ import {
272
+ buildFormFieldAriaDescribedBy,
273
+ FormError,
274
+ FormField,
275
+ FormLabel,
276
+ } from "@you-agent-factory/components/forms";
277
+ import { PackageInput } from "@you-agent-factory/components/forms";
278
+
279
+ export function InvalidDisplayNameField({ errorText }: { errorText: string }) {
280
+ const reactId = useId();
281
+ const controlId = `${reactId}-control`;
282
+ const errorId = `${reactId}-error`;
283
+
284
+ return (
285
+ <FormField>
286
+ <FormLabel htmlFor={controlId}>Display name</FormLabel>
287
+ <PackageInput
288
+ aria-describedby={buildFormFieldAriaDescribedBy({})}
289
+ aria-errormessage={errorId}
290
+ aria-invalid
291
+ id={controlId}
292
+ />
293
+ <FormError id={errorId}>{errorText}</FormError>
294
+ </FormField>
295
+ );
296
+ }
297
+ ```
298
+
299
+ ### Warning or success field
300
+
301
+ ```tsx
302
+ import { useId } from "react";
303
+ import {
304
+ buildFormFieldAriaDescribedBy,
305
+ FormField,
306
+ FormLabel,
307
+ FormSuccess,
308
+ FormWarning,
309
+ } from "@you-agent-factory/components/forms";
310
+ import { PackageInput } from "@you-agent-factory/components/forms";
311
+
312
+ export function StatusDisplayNameField({
313
+ successText,
314
+ warningText,
315
+ }: {
316
+ successText?: string;
317
+ warningText?: string;
318
+ }) {
319
+ const reactId = useId();
320
+ const controlId = `${reactId}-control`;
321
+ const warningId = warningText ? `${reactId}-warning` : undefined;
322
+ const successId = successText ? `${reactId}-success` : undefined;
323
+
324
+ return (
325
+ <FormField>
326
+ <FormLabel htmlFor={controlId}>Display name</FormLabel>
327
+ <PackageInput
328
+ aria-describedby={buildFormFieldAriaDescribedBy({
329
+ warningId,
330
+ successId,
331
+ })}
332
+ id={controlId}
333
+ />
334
+ {warningText ? (
335
+ <FormWarning id={warningId} role="status">
336
+ {warningText}
337
+ </FormWarning>
338
+ ) : null}
339
+ {successText ? (
340
+ <FormSuccess id={successId} role="status">
341
+ {successText}
342
+ </FormSuccess>
343
+ ) : null}
344
+ </FormField>
345
+ );
346
+ }
347
+ ```
348
+
349
+ ### Grouped-control field
350
+
351
+ ```tsx
352
+ import { useId } from "react";
353
+ import {
354
+ FormDescription,
355
+ FormField,
356
+ FormFieldGroup,
357
+ FormFieldGroupLabel,
358
+ FormLabel,
359
+ } from "@you-agent-factory/components/forms";
360
+ import { PackageCheckbox } from "@you-agent-factory/components/forms";
361
+
362
+ export function NotificationPreferencesField() {
363
+ const reactId = useId();
364
+ const descriptionId = `${reactId}-description`;
365
+ const emailId = `${reactId}-email`;
366
+ const smsId = `${reactId}-sms`;
367
+
368
+ return (
369
+ <FormFieldGroup aria-describedby={descriptionId}>
370
+ <FormFieldGroupLabel>Notification preferences</FormFieldGroupLabel>
371
+ <FormDescription id={descriptionId}>
372
+ Choose how you want to receive updates.
373
+ </FormDescription>
374
+ <FormField>
375
+ <FormLabel htmlFor={emailId}>Email</FormLabel>
376
+ <PackageCheckbox defaultChecked id={emailId} />
377
+ </FormField>
378
+ <FormField>
379
+ <FormLabel htmlFor={smsId}>Text message</FormLabel>
380
+ <PackageCheckbox id={smsId} />
381
+ </FormField>
382
+ </FormFieldGroup>
383
+ );
384
+ }
385
+ ```
386
+
387
+ ## Host-owned validation updates
388
+
389
+ Hosts update validation state and message props after user interaction. The
390
+ rendered accessible relationships must follow the new state:
391
+
392
+ ```tsx
393
+ import { useId, useState } from "react";
394
+ import {
395
+ buildFormFieldAriaDescribedBy,
396
+ FormError,
397
+ FormField,
398
+ FormLabel,
399
+ } from "@you-agent-factory/components/forms";
400
+ import { PackageInput } from "@you-agent-factory/components/forms";
401
+
402
+ export function ValidatedDisplayNameField() {
403
+ const reactId = useId();
404
+ const controlId = `${reactId}-control`;
405
+ const errorId = `${reactId}-error`;
406
+ const [value, setValue] = useState("");
407
+ const [errorText, setErrorText] = useState<string | undefined>();
408
+
409
+ return (
410
+ <FormField>
411
+ <FormLabel htmlFor={controlId}>Display name</FormLabel>
412
+ <PackageInput
413
+ aria-describedby={buildFormFieldAriaDescribedBy({})}
414
+ aria-errormessage={errorText ? errorId : undefined}
415
+ aria-invalid={errorText ? true : undefined}
416
+ id={controlId}
417
+ onChange={(event) => {
418
+ const nextValue = event.target.value;
419
+ setValue(nextValue);
420
+ setErrorText(
421
+ nextValue.trim().length === 0 ? "Display name is required." : undefined,
422
+ );
423
+ }}
424
+ value={value}
425
+ />
426
+ {errorText ? <FormError id={errorId}>{errorText}</FormError> : null}
427
+ </FormField>
428
+ );
429
+ }
430
+ ```
431
+
432
+ Form-field components render caller-provided message text exactly as supplied.
433
+ They do not generate validation copy or decide when a field is valid.
434
+
435
+ ## Storybook visual reference
436
+
437
+ Package Storybook lives under `Forms/PackageFormField`. Stories use package
438
+ imports and domain-neutral copy only — no dashboard providers.
439
+
440
+ | Story | What it shows |
441
+ | --- | --- |
442
+ | `Default` | Label and control only |
443
+ | `Required` | Required affordance in label copy |
444
+ | `Disabled` | Disabled control with label |
445
+ | `HelperText` | Helper text in accessible description |
446
+ | `Warning` | Warning message styling and description wiring |
447
+ | `ErrorState` | `aria-invalid`, error copy, and `aria-errormessage` |
448
+ | `Success` | Success message styling |
449
+ | `LongMessage` | Long helper text at narrow widths |
450
+ | `GroupedControl` | Fieldset, legend, shared description, per-control labels |
451
+ | `Focus` | Keyboard focus treatment on the control |
452
+ | `MobileWidth` | 320px viewport layout |
453
+
454
+ Run package Storybook locally:
455
+
456
+ ```bash
457
+ cd ui/packages/components
458
+ bun run storybook
459
+ ```
460
+
461
+ Browser verification for form-field responsive and focus stories:
462
+
463
+ ```bash
464
+ cd ui/packages/components
465
+ bun run verify:form-field-storybook-browser
466
+ ```
467
+
468
+ ## Allowed dependencies
469
+
470
+ Form-field source may import:
471
+
472
+ - Package utilities (`cn` from `@you-agent-factory/components/utilities`)
473
+ - Package token CSS via the host `styles.css` import
474
+ - React and `react-dom` peer dependencies
475
+
476
+ Form-field source must **not** import:
477
+
478
+ - Dashboard feature modules, routes, or providers
479
+ - Generated OpenAPI clients or dashboard API adapters
480
+ - React Query, Zustand, Monaco, Sonner, or dashboard i18n/session providers
481
+ - Factory, work, session, or provider domain types
482
+
483
+ `check:package-boundary` enforces these rules in CI.
484
+
485
+ ## Dashboard integration note
486
+
487
+ The dashboard re-exports form-field messaging from `components/ui` so existing
488
+ feature imports keep working while resolving to package implementations. New host
489
+ applications should import directly from `@you-agent-factory/components` or
490
+ `@you-agent-factory/components/forms`.