@sincpro/web-ui 1.2.0 → 1.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.
Files changed (130) hide show
  1. package/dist/components/activity/Activity.js +1 -1
  2. package/dist/components/activity/Messages.d.ts +4 -2
  3. package/dist/components/activity/Messages.js +4 -4
  4. package/dist/components/activity/Timeline.js +1 -1
  5. package/dist/components/assist/AssistMark.js +1 -1
  6. package/dist/components/assist/AssistSuggestion.js +1 -1
  7. package/dist/components/assist/ChangeReview.js +2 -2
  8. package/dist/components/calendar/RecordCalendar.js +4 -1
  9. package/dist/components/form/Sheet.js +5 -2
  10. package/dist/components/lines/LineItems.d.ts +1 -1
  11. package/dist/components/lines/LineItems.js +2 -2
  12. package/dist/components/listing/ListingBar.js +1 -1
  13. package/dist/components/listing/RecordCard.d.ts +4 -0
  14. package/dist/components/listing/RecordCard.js +4 -1
  15. package/dist/components/listing/Row.d.ts +7 -0
  16. package/dist/components/listing/Row.js +7 -1
  17. package/dist/components/listing/ViewSwitcher.d.ts +7 -2
  18. package/dist/components/listing/index.d.ts +1 -1
  19. package/dist/components/listing/set.d.ts +4 -1
  20. package/dist/components/record/Breadcrumb.js +5 -1
  21. package/dist/components/record/DirtyActions.d.ts +20 -6
  22. package/dist/components/record/DirtyActions.js +55 -4
  23. package/dist/components/record/RecordHeader.d.ts +1 -1
  24. package/dist/components/record/RecordHeader.js +32 -13
  25. package/dist/components/record/RecordSkeleton.js +1 -1
  26. package/dist/components/search/CriteriaBuilder.d.ts +1 -1
  27. package/dist/components/search/CriteriaBuilder.js +3 -3
  28. package/dist/components/search/FilterBuilder.d.ts +1 -18
  29. package/dist/components/search/FilterBuilder.js +4 -4
  30. package/dist/components/search/JsonEditor.js +5 -1
  31. package/dist/components/search/SearchBar.d.ts +1 -2
  32. package/dist/components/search/SearchBar.js +12 -3
  33. package/dist/components/search/criteria.d.ts +17 -1
  34. package/dist/components/search/index.d.ts +2 -1
  35. package/dist/components/search/index.js +1 -0
  36. package/dist/components/shell/Panes.d.ts +18 -0
  37. package/dist/components/shell/Panes.js +36 -0
  38. package/dist/components/shell/index.d.ts +1 -0
  39. package/dist/components/shell/index.js +2 -0
  40. package/dist/foundations/calendar.d.ts +3 -1
  41. package/dist/foundations/catalog.d.ts +27 -0
  42. package/dist/foundations/catalog.js +48 -0
  43. package/dist/foundations/copy.d.ts +40 -62
  44. package/dist/foundations/copy.es.js +10 -0
  45. package/dist/foundations/copy.js +25 -93
  46. package/dist/{primitives/field/context.d.ts → foundations/form.d.ts} +9 -0
  47. package/dist/{primitives/field/mode.js → foundations/form.js} +11 -0
  48. package/dist/foundations/grouping.d.ts +9 -3
  49. package/dist/foundations/index.d.ts +2 -0
  50. package/dist/foundations/index.js +5 -0
  51. package/dist/foundations/sticky.d.ts +4 -0
  52. package/dist/foundations/sticky.js +1 -0
  53. package/dist/primitives/button/Button.d.ts +12 -2
  54. package/dist/primitives/button/Button.js +12 -2
  55. package/dist/primitives/cell/CellStack.js +1 -1
  56. package/dist/primitives/choice/ComboBox.d.ts +1 -1
  57. package/dist/primitives/choice/ComboBox.js +4 -4
  58. package/dist/primitives/choice/RadioGroup.d.ts +1 -1
  59. package/dist/primitives/choice/RadioGroup.js +3 -3
  60. package/dist/primitives/choice/SearchField.js +1 -1
  61. package/dist/primitives/choice/SegmentedControl.d.ts +1 -1
  62. package/dist/primitives/choice/SegmentedControl.js +2 -2
  63. package/dist/primitives/choice/Slider.d.ts +1 -1
  64. package/dist/primitives/choice/Slider.js +2 -2
  65. package/dist/primitives/choice/Switch.d.ts +1 -1
  66. package/dist/primitives/choice/Switch.js +2 -2
  67. package/dist/primitives/date/Calendar.js +1 -1
  68. package/dist/primitives/date/DatePicker.d.ts +1 -1
  69. package/dist/primitives/date/DatePicker.js +2 -2
  70. package/dist/primitives/date/DateTime.d.ts +1 -1
  71. package/dist/primitives/date/DateTime.js +2 -2
  72. package/dist/primitives/field/Checks.d.ts +1 -1
  73. package/dist/primitives/field/Checks.js +4 -4
  74. package/dist/primitives/field/DropZone.d.ts +1 -1
  75. package/dist/primitives/field/DropZone.js +2 -2
  76. package/dist/primitives/field/Field.d.ts +10 -5
  77. package/dist/primitives/field/Field.js +10 -6
  78. package/dist/primitives/field/Suggest.d.ts +1 -1
  79. package/dist/primitives/field/Suggest.js +2 -2
  80. package/dist/primitives/field/index.d.ts +1 -3
  81. package/dist/primitives/field/index.js +4 -4
  82. package/dist/primitives/notice/Notice.js +3 -1
  83. package/dist/primitives/picker/Picker.d.ts +1 -1
  84. package/dist/primitives/picker/Picker.js +4 -4
  85. package/dist/primitives/{tag → picker}/TagsField.d.ts +2 -2
  86. package/dist/primitives/{tag → picker}/TagsField.js +5 -5
  87. package/dist/primitives/picker/index.d.ts +2 -0
  88. package/dist/primitives/picker/index.js +5 -1
  89. package/dist/primitives/table/CellCopy.js +1 -1
  90. package/dist/primitives/table/CellEditor.js +2 -4
  91. package/dist/primitives/table/DataTable.d.ts +11 -1
  92. package/dist/primitives/table/DataTable.js +111 -38
  93. package/dist/primitives/table/EditableTable.js +6 -2
  94. package/dist/primitives/table/PivotTable.js +0 -0
  95. package/dist/primitives/table/ReportTable.js +4 -3
  96. package/dist/primitives/table/SheetTable.js +8 -14
  97. package/dist/primitives/table/Table.js +2 -2
  98. package/dist/primitives/tabs/Tabs.d.ts +22 -4
  99. package/dist/primitives/tabs/Tabs.js +77 -23
  100. package/dist/primitives/tabs/index.d.ts +1 -1
  101. package/dist/primitives/tabs/index.js +1 -1
  102. package/dist/primitives/tag/Tags.js +4 -1
  103. package/dist/primitives/tag/index.d.ts +0 -2
  104. package/dist/primitives/tag/index.js +0 -3
  105. package/dist/primitives/value/Avatar.js +8 -1
  106. package/dist/primitives/value/{Fecha.d.ts → DateValue.d.ts} +5 -2
  107. package/dist/primitives/value/{Fecha.js → DateValue.js} +9 -8
  108. package/dist/primitives/value/Relation.d.ts +1 -1
  109. package/dist/primitives/value/Relation.js +2 -2
  110. package/dist/primitives/value/Value.js +4 -1
  111. package/dist/primitives/value/index.d.ts +1 -1
  112. package/dist/primitives/value/index.js +1 -1
  113. package/dist/tokens.css +42 -0
  114. package/dist/views/list/List.d.ts +22 -2
  115. package/dist/views/list/List.js +28 -6
  116. package/dist/views/list/ListView.d.ts +1 -1
  117. package/dist/views/list/ListView.js +47 -16
  118. package/dist/views/list/index.d.ts +2 -2
  119. package/dist/views/list/presentations.js +3 -1
  120. package/dist/views/list/types.d.ts +57 -1
  121. package/dist/views/list/useNarrowList.d.ts +2 -2
  122. package/dist/views/list/useNarrowList.js +5 -5
  123. package/dist/views/record/Record.js +46 -42
  124. package/package.json +1 -1
  125. package/dist/primitives/field/context.js +0 -13
  126. package/dist/primitives/field/mode.d.ts +0 -9
  127. /package/dist/primitives/field/{copy.d.ts → clipboard.d.ts} +0 -0
  128. /package/dist/primitives/field/{copy.js → clipboard.js} +0 -0
  129. /package/dist/primitives/{tag/choices.d.ts → picker/tagChoices.d.ts} +0 -0
  130. /package/dist/primitives/{tag/choices.js → picker/tagChoices.js} +0 -0
@@ -1,6 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, useContext } from "react";
2
+ import { createCopy, fillCopy } from "./catalog.js";
4
3
  export const ENGLISH_COPY = {
5
4
  locale: "en-US",
6
5
  present: {
@@ -49,8 +48,11 @@ export const ENGLISH_COPY = {
49
48
  status: "Status",
50
49
  faces: "Record faces",
51
50
  unsaved: "unsaved",
51
+ unsavedOne: "1 change unsaved",
52
+ unsavedMany: "{count} changes unsaved",
52
53
  save: "Save",
53
54
  saving: "Saving…",
55
+ saved: "Saved",
54
56
  discard: "Discard",
55
57
  failed: "failed",
56
58
  notices: "Notices about this record",
@@ -83,6 +85,7 @@ export const ENGLISH_COPY = {
83
85
  help: "Help",
84
86
  unset: "not set",
85
87
  none: "None",
88
+ changed: "changed",
86
89
  },
87
90
  choice: {
88
91
  noResults: "No results",
@@ -90,6 +93,7 @@ export const ENGLISH_COPY = {
90
93
  },
91
94
  search: {
92
95
  tools: "Filter, group and favorites",
96
+ suggestions: "Suggestions",
93
97
  toolsSetOne: "{label}, {count} applied",
94
98
  toolsSetMany: "{label}, {count} applied",
95
99
  editChip: "Click to edit",
@@ -146,6 +150,7 @@ export const ENGLISH_COPY = {
146
150
  min: "min {value}",
147
151
  max: "max {value}",
148
152
  distinct: "{value} distinct",
153
+ pileMore: "Show {count} more",
149
154
  },
150
155
  menu: {
151
156
  empty: "Nothing matches.",
@@ -157,6 +162,7 @@ export const ENGLISH_COPY = {
157
162
  count: "{count} records",
158
163
  peek: "Quick look",
159
164
  open: "Open",
165
+ refreshing: "Updating the list…",
160
166
  },
161
167
  activity: {
162
168
  bySystem: "the system",
@@ -242,6 +248,7 @@ export const ENGLISH_COPY = {
242
248
  weekOfYear: "Week {n}, {year}",
243
249
  quarter: "Q{n}",
244
250
  quarterOfYear: "Q{n} {year}",
251
+ today: "Today",
245
252
  },
246
253
  grouping: {
247
254
  label: "Group by",
@@ -337,6 +344,8 @@ export const ENGLISH_COPY = {
337
344
  pilesNote: "Over what they folded — the totals above, and «count» — not over the rows: that is the filter above.",
338
345
  pilePageNote: "Only the first level: once a page is asked for, the deeper levels answer only for the piles that survived.",
339
346
  limitPiles: "Limit how many piles come back",
347
+ ruleField: "Field",
348
+ ruleOperator: "Comparison",
340
349
  noValue: "no value",
341
350
  from: "from…",
342
351
  to: "to…",
@@ -502,97 +511,20 @@ export const ENGLISH_COPY = {
502
511
  empty: "empty",
503
512
  },
504
513
  };
505
- let activeCopy = ENGLISH_COPY;
506
- /** The merged catalog the formatters read when they are not a component. */
507
- export function currentCopy() {
508
- return activeCopy;
509
- }
514
+ /* The mechanism is `catalog.tsx`, the same one an application uses for its own words. */
515
+ const ui = createCopy(ENGLISH_COPY);
516
+ /** The installed words: for formatters and sentences that are not a component. */
517
+ export const currentCopy = ui.current;
510
518
  /** BCP-47 the formatters read when they are not a component. */
511
519
  export function currentCopyLocale() {
512
- return activeCopy.locale;
513
- }
514
- function rememberCopy(copy) {
515
- activeCopy = copy;
516
- }
517
- /**
518
- * La copia del árbol, cuando alguien la puso.
519
- *
520
- * Arranca en `null` y NO en el inglés de fábrica: un contexto se queda con el
521
- * valor que tenía cuando se creó, así que con el inglés adentro un componente
522
- * sin provider ignoraba `installCopy` y seguía diciendo «grouped by» aunque la
523
- * aplicación entera estuviera en castellano. Dos verdades sobre la misma cosa.
524
- *
525
- * Con `null`, sin provider se lee la instalada, que es la única.
526
- */
527
- const CopyContext = createContext(null);
528
- export function mergeCopy(over) {
529
- if (over === undefined)
530
- return ENGLISH_COPY;
531
- return {
532
- locale: over.locale ?? ENGLISH_COPY.locale,
533
- present: { ...ENGLISH_COPY.present, ...over.present },
534
- frame: { ...ENGLISH_COPY.frame, ...over.frame },
535
- selection: { ...ENGLISH_COPY.selection, ...over.selection },
536
- pager: { ...ENGLISH_COPY.pager, ...over.pager },
537
- record: { ...ENGLISH_COPY.record, ...over.record },
538
- shell: { ...ENGLISH_COPY.shell, ...over.shell },
539
- field: { ...ENGLISH_COPY.field, ...over.field },
540
- choice: { ...ENGLISH_COPY.choice, ...over.choice },
541
- search: { ...ENGLISH_COPY.search, ...over.search },
542
- failed: { ...ENGLISH_COPY.failed, ...over.failed },
543
- table: { ...ENGLISH_COPY.table, ...over.table },
544
- menu: { ...ENGLISH_COPY.menu, ...over.menu },
545
- list: { ...ENGLISH_COPY.list, ...over.list },
546
- grouping: { ...ENGLISH_COPY.grouping, ...over.grouping },
547
- dates: { ...ENGLISH_COPY.dates, ...over.dates },
548
- calendar: { ...ENGLISH_COPY.calendar, ...over.calendar },
549
- ops: { ...ENGLISH_COPY.ops, ...over.ops },
550
- sorting: { ...ENGLISH_COPY.sorting, ...over.sorting },
551
- copyable: { ...ENGLISH_COPY.copyable, ...over.copyable },
552
- activity: { ...ENGLISH_COPY.activity, ...over.activity },
553
- editable: { ...ENGLISH_COPY.editable, ...over.editable },
554
- picker: { ...ENGLISH_COPY.picker, ...over.picker },
555
- cell: { ...ENGLISH_COPY.cell, ...over.cell },
556
- time: { ...ENGLISH_COPY.time, ...over.time },
557
- dialog: { ...ENGLISH_COPY.dialog, ...over.dialog },
558
- report: { ...ENGLISH_COPY.report, ...over.report },
559
- builder: { ...ENGLISH_COPY.builder, ...over.builder },
560
- pivot: { ...ENGLISH_COPY.pivot, ...over.pivot },
561
- person: { ...ENGLISH_COPY.person, ...over.person },
562
- lines: { ...ENGLISH_COPY.lines, ...over.lines },
563
- lineItems: { ...ENGLISH_COPY.lineItems, ...over.lineItems },
564
- priority: { ...ENGLISH_COPY.priority, ...over.priority },
565
- board: { ...ENGLISH_COPY.board, ...over.board },
566
- kanbanState: { ...ENGLISH_COPY.kanbanState, ...over.kanbanState },
567
- timeline: { ...ENGLISH_COPY.timeline, ...over.timeline },
568
- activityState: { ...ENGLISH_COPY.activityState, ...over.activityState },
569
- relation: { ...ENGLISH_COPY.relation, ...over.relation },
570
- relationCard: { ...ENGLISH_COPY.relationCard, ...over.relationCard },
571
- totals: { ...ENGLISH_COPY.totals, ...over.totals },
572
- inlineEdit: { ...ENGLISH_COPY.inlineEdit, ...over.inlineEdit },
573
- theme: { ...ENGLISH_COPY.theme, ...over.theme },
574
- tree: { ...ENGLISH_COPY.tree, ...over.tree },
575
- assist: { ...ENGLISH_COPY.assist, ...over.assist },
576
- };
577
- }
578
- /** For tests and non-React callers that need a locale before a provider mounts. */
579
- export function installCopy(over) {
580
- const merged = mergeCopy(over);
581
- rememberCopy(merged);
582
- return merged;
583
- }
584
- export function UiCopyProvider({ copy, children, }) {
585
- const merged = mergeCopy(copy);
586
- rememberCopy(merged);
587
- return _jsx(CopyContext.Provider, { value: merged, children: children });
588
- }
589
- export function useUiCopy() {
590
- return useContext(CopyContext) ?? currentCopy();
591
- }
592
- /** `{name}` → value. A missing key stays visible in the template. */
593
- export function fillCopy(template, params) {
594
- return template.replace(/\{(\w+)\}/g, (whole, name) => {
595
- const value = params[name];
596
- return value === undefined ? whole : String(value);
597
- });
520
+ return ui.current().locale;
598
521
  }
522
+ /** English with `over` on top, without installing it. */
523
+ export const mergeCopy = ui.merge;
524
+ /** Sets the session's words: once, before the first draw — and the one global there is. */
525
+ export const installCopy = ui.install;
526
+ /** Changes the words for its branch, on top of what encloses it. It never writes the
527
+ * global: a corner in another language does not change the rest of the screen. */
528
+ export const UiCopyProvider = ui.Provider;
529
+ export const useUiCopy = ui.useCopy;
530
+ export { fillCopy };
@@ -1,3 +1,4 @@
1
+ import { type ReactNode } from "react";
1
2
  /**
2
3
  * What a `Field` hands to the control inside it: the id its label points at, the ids that
3
4
  * describe it (hint, error), and whether it is in error.
@@ -28,3 +29,11 @@ export declare function useFieldIds(own: {
28
29
  describedBy: string | undefined;
29
30
  invalid: boolean;
30
31
  };
32
+ export type FormMode = "edit" | "read";
33
+ export declare function FormModeProvider({ value, children, }: {
34
+ value: FormMode;
35
+ children: ReactNode;
36
+ }): import("react").JSX.Element;
37
+ /** The mode of this field. The prop beats the context — a computed field stays
38
+ * in read mode inside a form that is being edited. */
39
+ export declare function useFormMode(explicit?: FormMode): FormMode;
@@ -1,6 +1,17 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useContext } from "react";
4
+ export const FieldIdsContext = createContext(null);
5
+ /** The ids of the enclosing `Field`, resolved against the control's own props. */
6
+ export function useFieldIds(own) {
7
+ const field = useContext(FieldIdsContext);
8
+ return {
9
+ id: own.id ?? field?.id,
10
+ labelledBy: field?.labelId,
11
+ describedBy: own.describedBy ?? field?.describedBy,
12
+ invalid: own.invalid ?? field?.invalid ?? false,
13
+ };
14
+ }
4
15
  const Ctx = createContext("edit");
5
16
  export function FormModeProvider({ value, children, }) {
6
17
  return _jsx(Ctx.Provider, { value: value, children: children });
@@ -1,3 +1,4 @@
1
+ import { type GroupGrain } from "./calendar.js";
1
2
  /**
2
3
  * LA AGRUPACIÓN, en N niveles.
3
4
  *
@@ -43,9 +44,7 @@
43
44
  * identifica y se lee igual— y por eso el rótulo sólo se pone aparte donde hace
44
45
  * falta: en las fechas, que las nombra `system/calendar.ts`.
45
46
  */
46
- /** El grano de un nivel de fecha. Sin grano, un campo de fecha parte por
47
- * instante y cada fila termina en su propio montón, que es no agrupar. */
48
- export type GroupGrain = "day" | "week" | "month" | "quarter" | "year";
47
+ export type { GroupGrain } from "./calendar.js";
49
48
  /**
50
49
  * De GRUESO a fino. Es el orden en que se anidan los granos de un mismo campo, y
51
50
  * no es una preferencia: agrupar por día y después por mes daría montones de un
@@ -196,6 +195,13 @@ export type Grouping<T> = {
196
195
  /** Qué montones están plegados, por `pathKey(pile.path)`. */
197
196
  collapsed?: readonly string[];
198
197
  onToggle?: (key: string) => void;
198
+ /**
199
+ * Un montón contado afuera trae su PRIMERA página y su conteo entero: esto pide las
200
+ * siguientes. Con esto, cada presentación ofrece «Ver N más» debajo del montón que
201
+ * tiene menos filas de las que cuenta; quien guarda los datos agrega las que lleguen a
202
+ * `pile.items`.
203
+ */
204
+ onMore?: (pile: Pile<T>) => void;
199
205
  };
200
206
  /**
201
207
  * El árbol que hay que dibujar, o `null` si no se agrupa.
@@ -1,5 +1,7 @@
1
+ export { type CopyCatalog, type CopyOverride, createCopy, mergeCopyOver, } from "./catalog.js";
1
2
  export { currentCopy, currentCopyLocale, ENGLISH_COPY, fillCopy, installCopy, mergeCopy, type UiCopy, type UiCopyOverride, UiCopyProvider, useUiCopy, } from "./copy.js";
2
3
  export { byDensity, type Density, DensityProvider, useDensity, } from "./density.js";
4
+ export { type FieldIds, type FormMode, FormModeProvider, useFieldIds, useFormMode, } from "./form.js";
3
5
  export { bySurface, SurfaceProvider, useSurface } from "./surface.js";
4
6
  export { applyTheme, isTheme, type ResolvedTheme, resolveTheme, type Theme, THEME_ATTRIBUTE, ThemeProvider, type ThemeProviderProps, THEMES, useTheme, } from "./theme.js";
5
7
  export type { OpenTarget, Size, Surface, Tone } from "./types.js";
@@ -1,6 +1,11 @@
1
1
  /* What is shared by more than one family and belongs to none of them. */
2
+ /* THE MECHANISM of a copy catalog — web-ui's words are one; an application's own are another. */
3
+ export { createCopy, mergeCopyOver, } from "./catalog.js";
2
4
  export { currentCopy, currentCopyLocale, ENGLISH_COPY, fillCopy, installCopy, mergeCopy, UiCopyProvider, useUiCopy, } from "./copy.js";
3
5
  export { byDensity, DensityProvider, useDensity, } from "./density.js";
6
+ /* EL CONTRATO DE UN CONTROL — los ids que le da su `Field` y si se mira o se escribe. Lo
7
+ hablan los controles del paquete y los de la aplicación por igual. */
8
+ export { FormModeProvider, useFieldIds, useFormMode, } from "./form.js";
4
9
  /* EL TEMA — claro, oscuro o el del sistema operativo. Las paletas viven en `tokens.css`; esto
5
10
  sólo escribe `data-theme`. */
6
11
  export { bySurface, SurfaceProvider, useSurface } from "./surface.js";
@@ -35,6 +35,10 @@ export declare function useScrollBox<T extends HTMLElement>(options: {
35
35
  ref: RefObject<T | null>;
36
36
  className: string;
37
37
  style: CSSProperties;
38
+ /** `0` when the box scrolls: someone without a pointer has to be able to reach it to
39
+ * scroll it (WCAG 2.1.1) — a twelve-column report is unreadable past its third column
40
+ * otherwise. `undefined` when it grows with its content. */
41
+ tabIndex: 0 | undefined;
38
42
  };
39
43
  /**
40
44
  * Mide el alto de un elemento y devuelve el estilo que empuja la pila para sus
@@ -94,6 +94,7 @@ export function useScrollBox(options) {
94
94
  return {
95
95
  ref,
96
96
  className: suelta ? "overflow-clip" : "overflow-auto",
97
+ tabIndex: suelta ? undefined : 0,
97
98
  style: suelta
98
99
  ? {}
99
100
  : {
@@ -1,5 +1,5 @@
1
1
  import type { Surface } from "../../foundations/types.js";
2
- import type { LucideIcon } from "lucide-react";
2
+ import { type LucideIcon } from "lucide-react";
3
3
  import type { ComponentPropsWithRef, ReactNode } from "react";
4
4
  export type ButtonRole = "primary" | "secondary" | "ghost" | "danger";
5
5
  export type ButtonSize = "sm" | "md" | "lg";
@@ -53,8 +53,18 @@ export type ButtonProps = NativeProps<"button", "type" | "disabled" | "title" |
53
53
  expanded?: boolean;
54
54
  /** What it opens, written to `aria-haspopup`. `menu` when absent. */
55
55
  haspopup?: ButtonPopup;
56
+ /**
57
+ * What it started is running — saving, publishing, registering a payment.
58
+ *
59
+ * Context: the buttons said it by turning `disabled`, and a disabled button drops the focus
60
+ * to the page: whoever pressed Save with the keyboard was left nowhere, and a press while
61
+ * it ran was silently lost or — worse — sent twice. Busy, the button KEEPS the focus and
62
+ * its width (the label stays), says so (`aria-busy`, a turning mark where its icon was) and
63
+ * ignores presses until it is done.
64
+ */
65
+ busy?: boolean;
56
66
  };
57
- export declare function Button({ children, role, size, surface: surfaceProp, icon: Icon, disabled, title, type, expanded, haspopup, ref, ...native }: ButtonProps): import("react").JSX.Element;
67
+ export declare function Button({ children, role, size, surface: surfaceProp, icon: Icon, disabled, title, type, expanded, haspopup, busy, ref, onClick, ...native }: ButtonProps): import("react").JSX.Element;
58
68
  export type ButtonLinkProps = NativeProps<"a", "href" | "download" | "title"> & {
59
69
  children: ReactNode;
60
70
  href: string;
@@ -60,6 +60,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
60
60
  * ==================================================================== */
61
61
  import { useSurface } from "../../foundations/surface.js";
62
62
  import { Tooltip, TooltipTrigger, useInTooltipTrigger, } from "../overlay/Tooltip.js";
63
+ import { LoaderCircle } from "lucide-react";
63
64
  /** The height comes from `--size-control-*`; the padding and the type follow.
64
65
  *
65
66
  * sm a row, the header of a block
@@ -146,10 +147,19 @@ function popupAttrs(expanded, haspopup) {
146
147
  ...(expanded === undefined ? {} : { "aria-expanded": expanded }),
147
148
  };
148
149
  }
149
- export function Button({ children, role = "secondary", size = "md", surface: surfaceProp, icon: Icon, disabled = false, title, type = "button", expanded, haspopup, ref, ...native }) {
150
+ export function Button({ children, role = "secondary", size = "md", surface: surfaceProp, icon: Icon, disabled = false, title, type = "button", expanded, haspopup, busy = false, ref, onClick, ...native }) {
150
151
  const surface = useSurface(surfaceProp);
151
152
  const ic = ICON[size];
152
- return (_jsxs("button", { ...native, ref: ref, type: type, title: title, disabled: disabled, ...popupAttrs(expanded, haspopup), "data-emphasis": role, className: buttonClasses(role, size, surface), children: [Icon === undefined ? null : (_jsx(Icon, { className: `${ic.size} shrink-0`, strokeWidth: ic.stroke, "aria-hidden": true })), children] }));
153
+ const Mark = busy ? LoaderCircle : Icon;
154
+ return (_jsxs("button", { ...native, ref: ref, type: type, title: title, disabled: disabled, ...(busy ? { "aria-busy": true, "aria-disabled": true } : {}), onClick: (event) => {
155
+ if (busy) {
156
+ event.preventDefault();
157
+ return;
158
+ }
159
+ onClick?.(event);
160
+ }, ...popupAttrs(expanded, haspopup), "data-emphasis": role, className: busy
161
+ ? `${buttonClasses(role, size, surface)} cursor-progress`
162
+ : buttonClasses(role, size, surface), children: [Mark === undefined ? null : (_jsx(Mark, { className: `${ic.size} shrink-0 ${busy ? "motion-safe:animate-spin" : ""}`, strokeWidth: ic.stroke, "aria-hidden": true })), children] }));
153
163
  }
154
164
  /**
155
165
  * The same button, but it is a real link.
@@ -32,5 +32,5 @@ export function Thumb({ src, alt, icon: Icon, size = "md", }) {
32
32
  export function CellStack({ primary, secondary, media, avatar, wrap = false, }) {
33
33
  return (_jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [avatar !== undefined ? (
34
34
  /* The name is written beside it: the photo's `alt` would say it twice. */
35
- _jsx("span", { "aria-hidden": true, className: "flex shrink-0", children: _jsx(Avatar, { name: avatar.name, size: "lg", ...(avatar.src === undefined ? {} : { src: avatar.src }) }) })) : media === undefined ? null : (_jsx(Thumb, { size: "md", ...(media.src === undefined ? {} : { src: media.src }), ...(media.alt === undefined ? {} : { alt: media.alt }), ...(media.icon === undefined ? {} : { icon: media.icon }) })), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate text-meta font-medium text-ink", ...(typeof primary === "string" ? { title: primary } : {}), children: primary }), secondary === undefined ? null : (_jsx("span", { className: `text-micro text-muted ${wrap ? "line-clamp-2" : "truncate"}`, ...(typeof secondary === "string" ? { title: secondary } : {}), children: secondary }))] })] }));
35
+ _jsx("span", { "aria-hidden": true, className: "flex shrink-0", children: _jsx(Avatar, { name: avatar.name, size: "lg", ...(avatar.src === undefined ? {} : { src: avatar.src }) }) })) : media === undefined ? null : (_jsx(Thumb, { size: "md", ...(media.src === undefined ? {} : { src: media.src }), ...(media.alt === undefined ? {} : { alt: media.alt }), ...(media.icon === undefined ? {} : { icon: media.icon }) })), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "line-clamp-1 text-meta font-medium break-all text-ink", ...(typeof primary === "string" ? { title: primary } : {}), children: primary }), secondary === undefined ? null : (_jsx("span", { className: `text-micro text-muted ${wrap ? "line-clamp-2" : "line-clamp-1 break-all"}`, ...(typeof secondary === "string" ? { title: secondary } : {}), children: secondary }))] })] }));
36
36
  }
@@ -1,5 +1,5 @@
1
+ import { type FormMode } from "../../foundations/form.js";
1
2
  import { type ChoiceOption } from "./options.js";
2
- import { type FormMode } from "../field/mode.js";
3
3
  export type ComboBoxProps = {
4
4
  /** The chosen option's `value`, or `null` when nothing is chosen. */
5
5
  value: string | null;
@@ -1,14 +1,14 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { AriaLocale } from "../../foundations/aria.js";
4
+ import { useFieldIds } from "../../foundations/form.js";
5
+ import { useFormMode } from "../../foundations/form.js";
4
6
  import { labelOf } from "./options.js";
5
7
  import { FieldButton, FieldGroup, listOptionStyles, } from "../field/aria.js";
6
- import { useFieldIds } from "../field/context.js";
7
8
  import { ReadValue } from "../field/Field.js";
8
- import { useFormMode } from "../field/mode.js";
9
9
  import { Popover } from "../overlay/Popover.js";
10
10
  import { Check, ChevronDown } from "lucide-react";
11
- import { ComboBox as AriaComboBox, Input, ListBox, ListBoxItem, Text, useFilter, } from "react-aria-components";
11
+ import { ComboBox as AriaComboBox, Input, ListBox, ListBoxItem, useFilter, } from "react-aria-components";
12
12
  /**
13
13
  * One of a closed list, found by typing: a country, a currency, a tax regime, a unit.
14
14
  *
@@ -52,5 +52,5 @@ function EditableComboBox({ value, onChange, options, placeholder, disabled = fa
52
52
  /* `defaultItems` and not `items`: with `items` React Aria hands the filtering to the
53
53
  consumer. These are the same options every render — only the filtering is React
54
54
  Aria's. */
55
- defaultItems: options, value: value, onChange: (next) => onChange?.(next === null ? null : String(next)), defaultFilter: contains, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: [_jsxs(FieldGroup, { children: [_jsx(Input, { placeholder: placeholder, className: "h-full min-w-0 flex-1 bg-transparent px-2.5 text-meta text-ink outline-0 placeholder:text-muted disabled:cursor-not-allowed" }), _jsx(FieldButton, { className: "mr-1", children: _jsx(ChevronDown, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }), _jsx(Popover, { placement: "bottom start", className: "w-(--trigger-width)", children: _jsx(ListBox, { items: options, className: "max-h-[inherit] overflow-auto p-1 outline-0", children: (item) => (_jsx(ListBoxItem, { id: item.value, textValue: item.label, isDisabled: item.disabled, className: option, children: ({ isSelected }) => (_jsxs(_Fragment, { children: [_jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx(Text, { slot: "label", className: `truncate ${isSelected ? "font-medium" : ""}`, children: item.label }), item.description === undefined ? null : (_jsx(Text, { slot: "description", className: "truncate text-micro text-muted", children: item.description }))] }), _jsx("span", { className: "flex size-(--size-icon-md) shrink-0 items-center justify-center", children: isSelected ? (_jsx(Check, { "aria-hidden": true, className: "size-(--size-icon-md) text-primary forced-colors:text-[HighlightText]", strokeWidth: 2 })) : null })] })) })) }) })] }));
55
+ defaultItems: options, value: value, onChange: (next) => onChange?.(next === null ? null : String(next)), defaultFilter: contains, isInvalid: invalid, isDisabled: disabled, className: "w-full", children: [_jsxs(FieldGroup, { children: [_jsx(Input, { placeholder: placeholder, className: "h-full min-w-0 flex-1 bg-transparent px-2.5 text-meta text-ink outline-0 placeholder:text-muted disabled:cursor-not-allowed" }), _jsx(FieldButton, { className: "mr-1", children: _jsx(ChevronDown, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }), _jsx(Popover, { placement: "bottom start", className: "w-(--trigger-width)", children: _jsx(ListBox, { items: options, className: "max-h-[inherit] overflow-auto p-1 outline-0", children: (item) => (_jsx(ListBoxItem, { id: item.value, textValue: item.label, isDisabled: item.disabled, className: option, children: ({ isSelected }) => (_jsxs(_Fragment, { children: [_jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: `truncate ${isSelected ? "font-medium" : ""}`, children: item.label }), item.description === undefined ? null : (_jsx("span", { className: "truncate text-micro text-muted", children: item.description }))] }), _jsx("span", { className: "flex size-(--size-icon-md) shrink-0 items-center justify-center", children: isSelected ? (_jsx(Check, { "aria-hidden": true, className: "size-(--size-icon-md) text-primary forced-colors:text-[HighlightText]", strokeWidth: 2 })) : null })] })) })) }) })] }));
56
56
  }
@@ -1,5 +1,5 @@
1
+ import { type FormMode } from "../../foundations/form.js";
1
2
  import { type ChoiceOption } from "./options.js";
2
- import { type FormMode } from "../field/mode.js";
3
3
  export type RadioGroupProps = {
4
4
  /** The chosen option's `value`, or `null` while nothing is chosen. */
5
5
  value: string | null;
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useFieldIds } from "../../foundations/form.js";
4
+ import { useFormMode } from "../../foundations/form.js";
3
5
  import { tv } from "../../foundations/variants.js";
4
6
  import { labelOf } from "./options.js";
5
- import { useFieldIds } from "../field/context.js";
6
7
  import { ReadValue } from "../field/Field.js";
7
- import { useFormMode } from "../field/mode.js";
8
8
  import { RadioButton, RadioField, RadioGroup as AriaRadioGroup, Text, } from "react-aria-components";
9
9
  /**
10
10
  * One of few, all in sight: the payment method of a sale, the kind of a contact.
@@ -46,5 +46,5 @@ export function RadioGroup({ value, onChange, options, orientation = "vertical",
46
46
  field does not look shorter than its neighbours in the same row. */
47
47
  className: orientation === "horizontal"
48
48
  ? "flex min-w-0 flex-wrap items-start gap-x-5 gap-y-2 py-2"
49
- : "flex min-w-0 flex-col gap-2.5 py-2", children: options.map((o) => (_jsxs(RadioField, { value: o.value, isDisabled: o.disabled, className: "flex min-w-0 flex-col data-disabled:opacity-45", children: [_jsx(RadioButton, { className: "group flex min-w-0 cursor-pointer items-start gap-2.5 text-meta text-ink disabled:cursor-not-allowed", children: (state) => (_jsxs(_Fragment, { children: [_jsx("span", { className: mark(state), children: _jsx("span", { className: `size-1.5 rounded-pill bg-surface transition-transform duration-(--duration-instant) ${state.isSelected ? "scale-100" : "scale-0"}` }) }), _jsx("span", { className: "min-w-0", children: o.label })] })) }), o.description === undefined ? null : (_jsx(Text, { slot: "description", className: "ml-6.5 text-micro text-muted", children: o.description }))] }, o.value))) }));
49
+ : "flex min-w-0 flex-col gap-2.5 py-2", children: options.map((o) => (_jsxs(RadioField, { value: o.value, isDisabled: o.disabled, className: "flex min-w-0 flex-col", children: [_jsx(RadioButton, { className: "group flex min-w-0 cursor-pointer items-start gap-2.5 text-meta text-ink disabled:cursor-not-allowed data-disabled:opacity-45", children: (state) => (_jsxs(_Fragment, { children: [_jsx("span", { className: mark(state), children: _jsx("span", { className: `size-1.5 rounded-pill bg-surface transition-transform duration-(--duration-instant) ${state.isSelected ? "scale-100" : "scale-0"}` }) }), _jsx("span", { className: "min-w-0", children: o.label })] })) }), o.description === undefined ? null : (_jsx(Text, { slot: "description", className: "ml-6.5 text-micro text-muted", children: o.description }))] }, o.value))) }));
50
50
  }
@@ -2,8 +2,8 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { AriaLocale } from "../../foundations/aria.js";
4
4
  import { useUiCopy } from "../../foundations/copy.js";
5
+ import { useFieldIds } from "../../foundations/form.js";
5
6
  import { FieldButton, FieldGroup } from "../field/aria.js";
6
- import { useFieldIds } from "../field/context.js";
7
7
  import { Search, X } from "lucide-react";
8
8
  import { Input, SearchField as AriaSearchField } from "react-aria-components";
9
9
  /** The trailing «…» of a placeholder is a visual cue that typing is expected, not part of
@@ -1,5 +1,5 @@
1
+ import { type FormMode } from "../../foundations/form.js";
1
2
  import { type SegmentOption } from "./options.js";
2
- import { type FormMode } from "../field/mode.js";
3
3
  export type SegmentedControlProps = {
4
4
  value: string;
5
5
  onChange?: (next: string) => void;
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useFieldIds } from "../../foundations/form.js";
4
+ import { useFormMode } from "../../foundations/form.js";
3
5
  import { tv } from "../../foundations/variants.js";
4
6
  import { labelOf } from "./options.js";
5
- import { useFieldIds } from "../field/context.js";
6
7
  import { ReadValue } from "../field/Field.js";
7
- import { useFormMode } from "../field/mode.js";
8
8
  import { ToggleButton, ToggleButtonGroup } from "react-aria-components";
9
9
  /**
10
10
  * Switching what is looked at: the period of a report ("Día · Semana · Mes"), the grouping of
@@ -1,4 +1,4 @@
1
- import { type FormMode } from "../field/mode.js";
1
+ import { type FormMode } from "../../foundations/form.js";
2
2
  export type SliderProps = {
3
3
  value: number;
4
4
  onChange?: (next: number) => void;
@@ -2,10 +2,10 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { AriaLocale } from "../../foundations/aria.js";
4
4
  import { useUiCopy } from "../../foundations/copy.js";
5
+ import { useFieldIds } from "../../foundations/form.js";
6
+ import { useFormMode } from "../../foundations/form.js";
5
7
  import { tv } from "../../foundations/variants.js";
6
- import { useFieldIds } from "../field/context.js";
7
8
  import { ReadValue } from "../field/Field.js";
8
- import { useFormMode } from "../field/mode.js";
9
9
  import { Slider as AriaSlider, SliderFill, SliderOutput, SliderThumb, SliderTrack, } from "react-aria-components";
10
10
  /**
11
11
  * A number in a range, where the position says as much as the figure: a discount, a
@@ -1,4 +1,4 @@
1
- import { type FormMode } from "../field/mode.js";
1
+ import { type FormMode } from "../../foundations/form.js";
2
2
  import { type ReactNode } from "react";
3
3
  export type SwitchProps = {
4
4
  checked: boolean;
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useFieldIds } from "../../foundations/form.js";
4
+ import { useFormMode } from "../../foundations/form.js";
3
5
  import { tv } from "../../foundations/variants.js";
4
- import { useFieldIds } from "../field/context.js";
5
- import { useFormMode } from "../field/mode.js";
6
6
  import { Check, Minus } from "lucide-react";
7
7
  import { useId } from "react";
8
8
  import { Switch as AriaSwitch } from "react-aria-components";
@@ -44,7 +44,7 @@ const YEARS_LISTED = 100;
44
44
  function Header({ jump }) {
45
45
  const { direction } = useLocale();
46
46
  const [Prev, Next] = direction === "rtl" ? [ChevronRight, ChevronLeft] : [ChevronLeft, ChevronRight];
47
- return (_jsxs("header", { className: "mb-2 flex items-center gap-1", children: [_jsx(AriaButton, { slot: "previous", className: navButton, children: _jsx(Prev, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) }), jump ? (_jsxs("div", { className: "flex flex-1 items-center justify-center", children: [_jsx(CalendarMonthPicker, { format: "long", children: (aria) => _jsx(Jump, { ...aria }) }), _jsx(CalendarYearPicker, { visibleYears: YEARS_LISTED, children: (aria) => _jsx(Jump, { ...aria }) })] })) : (_jsx(CalendarHeading, { className: "flex-1 text-center text-body font-semibold text-ink first-letter:uppercase" })), _jsx(AriaButton, { slot: "next", className: navButton, children: _jsx(Next, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }));
47
+ return (_jsxs("div", { className: "mb-2 flex items-center gap-1", children: [_jsx(AriaButton, { slot: "previous", className: navButton, children: _jsx(Prev, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) }), jump ? (_jsxs("div", { className: "flex flex-1 items-center justify-center", children: [_jsx(CalendarMonthPicker, { format: "long", children: (aria) => _jsx(Jump, { ...aria }) }), _jsx(CalendarYearPicker, { visibleYears: YEARS_LISTED, children: (aria) => _jsx(Jump, { ...aria }) })] })) : (_jsx(CalendarHeading, { className: "flex-1 text-center text-body font-semibold text-ink first-letter:uppercase" })), _jsx(AriaButton, { slot: "next", className: navButton, children: _jsx(Next, { "aria-hidden": true, className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }));
48
48
  }
49
49
  function WeekDays() {
50
50
  return (_jsx(CalendarGridHeader, { children: (day) => (_jsx(CalendarHeaderCell, { className: "pb-1 text-micro font-medium text-muted", children: day })) }));
@@ -1,4 +1,4 @@
1
- import { type FormMode } from "../field/mode.js";
1
+ import { type FormMode } from "../../foundations/form.js";
2
2
  /** The typed segments inside the field box — shared with `TimeField` and `DateTimePicker`
3
3
  * (internal: not in the family barrel). */
4
4
  export declare function Segments({ slot }: {
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { DateFormatter, parseDate } from "@internationalized/date";
4
4
  import { AriaLocale } from "../../foundations/aria.js";
5
5
  import { useUiCopy } from "../../foundations/copy.js";
6
+ import { useFieldIds } from "../../foundations/form.js";
7
+ import { useFormMode } from "../../foundations/form.js";
6
8
  import { tv } from "../../foundations/variants.js";
7
9
  import { Calendar, RangeCalendar } from "./Calendar.js";
8
10
  import { FieldButton, FieldGroup } from "../field/aria.js";
9
- import { useFieldIds } from "../field/context.js";
10
11
  import { ReadValue } from "../field/Field.js";
11
- import { useFormMode } from "../field/mode.js";
12
12
  import { Popover } from "../overlay/Popover.js";
13
13
  import { CalendarDays } from "lucide-react";
14
14
  import { DateField as AriaDateField, DateInput, DatePicker as AriaDatePicker, DateRangePicker as AriaDateRangePicker, DateSegment, Dialog, } from "react-aria-components";
@@ -1,4 +1,4 @@
1
- import { type FormMode } from "../field/mode.js";
1
+ import { type FormMode } from "../../foundations/form.js";
2
2
  type HourCycle = 12 | 24;
3
3
  type Shared = {
4
4
  id?: string;
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { DateFormatter, parseDateTime, parseTime, toCalendarDate, } from "@internationalized/date";
4
4
  import { AriaLocale } from "../../foundations/aria.js";
5
5
  import { useUiCopy } from "../../foundations/copy.js";
6
+ import { useFieldIds } from "../../foundations/form.js";
7
+ import { useFormMode } from "../../foundations/form.js";
6
8
  import { Calendar } from "./Calendar.js";
7
9
  import { Segments } from "./DatePicker.js";
8
10
  import { FieldButton, FieldGroup } from "../field/aria.js";
9
- import { useFieldIds } from "../field/context.js";
10
11
  import { ReadValue } from "../field/Field.js";
11
- import { useFormMode } from "../field/mode.js";
12
12
  import { Popover } from "../overlay/Popover.js";
13
13
  import { CalendarDays } from "lucide-react";
14
14
  import { DatePicker as AriaDatePicker, Dialog, TimeField as AriaTimeField, } from "react-aria-components";
@@ -1,4 +1,4 @@
1
- import { type FormMode } from "./mode.js";
1
+ import { type FormMode } from "../../foundations/form.js";
2
2
  export interface CheckItem {
3
3
  value: string;
4
4
  label: string;
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useUiCopy } from "../../foundations/copy.js";
4
- import { useFieldIds } from "./context.js";
5
- import { useFormMode } from "./mode.js";
4
+ import { useFieldIds } from "../../foundations/form.js";
5
+ import { useFormMode } from "../../foundations/form.js";
6
6
  import { Check, Minus } from "lucide-react";
7
7
  import { useId } from "react";
8
- import { Checkbox as AriaCheckbox, ListBox, ListBoxItem, Text, } from "react-aria-components";
8
+ import { Checkbox as AriaCheckbox, ListBox, ListBoxItem, } from "react-aria-components";
9
9
  function Mark({ on, mixed, focus = false, invalid = false, }) {
10
10
  return (_jsx("span", { "aria-hidden": true, className: `inline-flex size-4 shrink-0 items-center justify-center rounded-mark border transition-colors duration-(--duration-instant) forced-color-adjust-none ${on
11
11
  ? "border-primary bg-primary text-surface forced-colors:border-[Highlight] forced-colors:bg-[Highlight] forced-colors:text-[HighlightText]"
@@ -68,5 +68,5 @@ export function CheckList({ label, items, value, onChange, mono = false, emptyLa
68
68
  }
69
69
  return (_jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [_jsx("p", { id: labelId, className: "text-micro text-muted", children: label }), _jsx(ListBox, { "aria-labelledby": labelId, "aria-describedby": describedBy, items: items, selectionMode: "multiple", selectedKeys: value, onSelectionChange: (keys) => onChange?.(valuesOf(keys, items)), disabledKeys: items.filter((item) => item.disabled).map((item) => item.value), shouldFocusWrap: true, className: "flex flex-col overflow-hidden rounded-box border border-line bg-surface outline-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary", children: (item) => (_jsx(ListBoxItem, { id: item.value, textValue: item.label, className: ({ isSelected, isFocusVisible, isDisabled }) => `flex min-w-0 cursor-pointer items-center gap-2.5 border-b border-line px-3 py-2 text-left outline-0 last:border-b-0 ${isFocusVisible
70
70
  ? "outline-2 -outline-offset-2 outline-primary forced-colors:outline-[Highlight]"
71
- : ""} ${isDisabled ? "cursor-not-allowed opacity-45" : ""} ${isSelected ? "bg-primary/8" : "hover:bg-ink/4"}`, children: ({ isSelected }) => (_jsxs(_Fragment, { children: [_jsx(Mark, { on: isSelected, mixed: false }), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx(Text, { slot: "label", className: `truncate text-meta text-ink ${mono ? "font-mono" : ""}`, children: item.label }), item.detail === undefined ? null : (_jsx(Text, { slot: "description", className: "truncate text-micro text-muted", children: item.detail }))] })] })) })) })] }));
71
+ : ""} ${isDisabled ? "cursor-not-allowed opacity-45" : ""} ${isSelected ? "bg-primary/8" : "hover:bg-ink/4"}`, children: ({ isSelected }) => (_jsxs(_Fragment, { children: [_jsx(Mark, { on: isSelected, mixed: false }), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: `truncate text-meta text-ink ${mono ? "font-mono" : ""}`, children: item.label }), item.detail === undefined ? null : (_jsx("span", { className: "truncate text-micro text-muted", children: item.detail }))] })] })) })) })] }));
72
72
  }
@@ -1,4 +1,4 @@
1
- import { type FormMode } from "./mode.js";
1
+ import { type FormMode } from "../../foundations/form.js";
2
2
  /**
3
3
  * Drop a file, or pick one. Progress is a real bar; failure is a sentence.
4
4
  *
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useUiCopy } from "../../foundations/copy.js";
4
+ import { useFieldIds } from "../../foundations/form.js";
5
+ import { useFormMode } from "../../foundations/form.js";
4
6
  import { cn } from "../../foundations/variants.js";
5
7
  import { Button, buttonClasses } from "../button/Button.js";
6
- import { useFieldIds } from "./context.js";
7
8
  import { ReadValue } from "./Field.js";
8
- import { useFormMode } from "./mode.js";
9
9
  import { Failed } from "../notice/Notice.js";
10
10
  import { ProgressBar } from "../value/ProgressBar.js";
11
11
  import { Button as AriaButton, DropZone as AriaDropZone, FileTrigger, } from "react-aria-components";